Spaces:
Running
Running
<!-- templates/index.html --> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Image Uploader</title> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<style> | |
.upload-container { | |
background-color: #f8f9fa; | |
padding: 20px; | |
border-radius: 8px; | |
margin-bottom: 30px; | |
} | |
.image-card { | |
margin-bottom: 20px; | |
transition: transform 0.3s; | |
} | |
.image-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
} | |
.copy-btn { | |
cursor: pointer; | |
} | |
.image-preview { | |
max-height: 200px; | |
object-fit: cover; | |
width: 100%; | |
} | |
.code-container { | |
background-color: #f5f5f5; | |
padding: 10px; | |
border-radius: 4px; | |
margin-top: 10px; | |
font-family: monospace; | |
font-size: 0.8rem; | |
overflow-x: auto; | |
white-space: nowrap; | |
} | |
.hidden { | |
display: none; | |
} | |
#uploadProgress { | |
margin-top: 10px; | |
} | |
.success-message { | |
background-color: #d1e7dd; | |
color: #0f5132; | |
padding: 15px; | |
border-radius: 4px; | |
margin-top: 15px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container py-5"> | |
<div class="d-flex justify-content-between align-items-center mb-4"> | |
<h1>🖼️ Image Uploader & Embed Link Generator</h1> | |
<a href="/logout" class="btn btn-outline-danger">Logout</a> | |
</div> | |
<div class="upload-container"> | |
<h2>Upload New Image</h2> | |
<form id="uploadForm" enctype="multipart/form-data"> | |
<div class="mb-3"> | |
<label for="file" class="form-label">Select an image to upload</label> | |
<input class="form-control" type="file" id="file" name="file" accept="image/*"> | |
</div> | |
<button type="submit" class="btn btn-primary">Upload & Generate Links</button> | |
</form> | |
<div id="uploadProgress" class="progress hidden"> | |
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div> | |
</div> | |
<div id="uploadResult" class="hidden success-message"> | |
<h4>✅ Upload Successful!</h4> | |
<div id="previewContainer" class="mt-3"> | |
<img id="imagePreview" class="img-fluid img-thumbnail mb-3" alt="Uploaded image preview"> | |
</div> | |
<h5>Embed Options:</h5> | |
<div class="mb-3"> | |
<h6>1. Direct URL (hosted on this server)</h6> | |
<div class="input-group"> | |
<input type="text" id="directUrl" class="form-control" readonly> | |
<button class="btn btn-outline-secondary copy-btn" data-target="directUrl">Copy</button> | |
</div> | |
</div> | |
<div class="mb-3"> | |
<h6>2. HTML Embed Code</h6> | |
<div class="code-container" id="htmlEmbed"></div> | |
<button class="btn btn-sm btn-outline-secondary mt-2 copy-btn" data-target="htmlEmbed">Copy HTML</button> | |
</div> | |
<div class="mb-3"> | |
<h6>3. Base64 Embed (works anywhere without hosting)</h6> | |
<div class="code-container" id="base64Embed">Base64 code is truncated for display. Use the copy button to get the full code.</div> | |
<button class="btn btn-sm btn-outline-secondary mt-2 copy-btn" data-target="base64Embed">Copy Base64 HTML</button> | |
</div> | |
<p class="text-muted mt-3"> | |
<small>Tip: Base64 encoding embeds the image directly in your HTML, so you don't need to host the image separately.</small> | |
</p> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-12"> | |
<h2 class="mb-4">Your Uploaded Images</h2> | |
{% if not uploaded_images %} | |
<div class="alert alert-info"> | |
No images have been uploaded yet. Upload your first image above! | |
</div> | |
{% else %} | |
<div class="row"> | |
{% for image in uploaded_images %} | |
<div class="col-md-4"> | |
<div class="card image-card"> | |
<img src="{{ image.url }}" class="card-img-top image-preview" alt="{{ image.name }}"> | |
<div class="card-body"> | |
<h5 class="card-title text-truncate">{{ image.name }}</h5> | |
<div class="d-flex justify-content-between mt-3"> | |
<a href="/view/{{ image.name }}" class="btn btn-sm btn-primary">View Details</a> | |
<button class="btn btn-sm btn-danger delete-btn" data-filename="{{ image.name }}">Delete</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endfor %} | |
</div> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
const uploadForm = document.getElementById('uploadForm'); | |
const uploadProgress = document.getElementById('uploadProgress'); | |
const progressBar = uploadProgress.querySelector('.progress-bar'); | |
const uploadResult = document.getElementById('uploadResult'); | |
// Handle form submission | |
uploadForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const fileInput = document.getElementById('file'); | |
if (!fileInput.files.length) { | |
alert('Please select a file to upload.'); | |
return; | |
} | |
const file = fileInput.files[0]; | |
const formData = new FormData(); | |
formData.append('file', file); | |
// Show progress | |
uploadProgress.classList.remove('hidden'); | |
progressBar.style.width = '0%'; | |
const xhr = new XMLHttpRequest(); | |
xhr.upload.addEventListener('progress', function(e) { | |
if (e.lengthComputable) { | |
const percentComplete = (e.loaded / e.total) * 100; | |
progressBar.style.width = percentComplete + '%'; | |
} | |
}); | |
xhr.addEventListener('load', function() { | |
if (xhr.status === 200) { | |
const response = JSON.parse(xhr.responseText); | |
// Update preview | |
document.getElementById('imagePreview').src = response.file_url; | |
document.getElementById('directUrl').value = window.location.origin + response.file_url; | |
document.getElementById('htmlEmbed').textContent = response.embed_html; | |
// Store the full base64 embed, but display truncated version | |
const base64EmbedEl = document.getElementById('base64Embed'); | |
base64EmbedEl.textContent = 'Base64 embed code (truncated for display): ' + | |
response.base64_embed.substring(0, 50) + '...' + | |
response.base64_embed.substring(response.base64_embed.length - 20); | |
base64EmbedEl.dataset.fullCode = response.base64_embed; | |
// Show results | |
uploadResult.classList.remove('hidden'); | |
// Reset form for next upload | |
uploadForm.reset(); | |
// Refresh the page after 2 seconds to show the new image in the gallery | |
setTimeout(() => { | |
window.location.reload(); | |
}, 2000); | |
} else { | |
alert('Upload failed. Please try again.'); | |
} | |
uploadProgress.classList.add('hidden'); | |
}); | |
xhr.addEventListener('error', function() { | |
alert('Upload failed. Please try again.'); | |
uploadProgress.classList.add('hidden'); | |
}); | |
xhr.open('POST', '/upload/'); | |
xhr.send(formData); | |
}); | |
// Handle copy buttons | |
document.querySelectorAll('.copy-btn').forEach(function(btn) { | |
btn.addEventListener('click', function() { | |
const targetId = this.dataset.target; | |
const targetEl = document.getElementById(targetId); | |
let textToCopy; | |
if (targetEl.tagName === 'INPUT') { | |
textToCopy = targetEl.value; | |
} else { | |
// For base64, we have the full code in the data attribute | |
textToCopy = targetEl.dataset.fullCode || targetEl.textContent; | |
} | |
navigator.clipboard.writeText(textToCopy).then(() => { | |
// Change button text temporarily | |
const originalText = this.textContent; | |
this.textContent = 'Copied!'; | |
setTimeout(() => { | |
this.textContent = originalText; | |
}, 1500); | |
}).catch(err => { | |
console.error('Failed to copy text: ', err); | |
}); | |
}); | |
}); | |
// Handle delete buttons | |
document.querySelectorAll('.delete-btn').forEach(function(btn) { | |
btn.addEventListener('click', function() { | |
const filename = this.dataset.filename; | |
if (confirm(`Are you sure you want to delete ${filename}?`)) { | |
fetch(`/delete/${filename}`, { | |
method: 'DELETE' | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.success) { | |
window.location.reload(); | |
} else { | |
alert('Failed to delete the image.'); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
alert('An error occurred while deleting the image.'); | |
}); | |
} | |
}); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |