|
|
<!doctype html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="utf-8" /> |
|
|
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" /> |
|
|
<title>DOTO — Temp Upload</title> |
|
|
|
|
|
<link rel="stylesheet" href="/static/css/styles.css" /> |
|
|
|
|
|
<style> |
|
|
@font-face { |
|
|
font-family: "DOTO"; |
|
|
src: local("DOTO"), local("Doto"), local("Doto-Bold"); |
|
|
font-weight: 700; |
|
|
font-style: normal; |
|
|
} |
|
|
|
|
|
|
|
|
.modal-overlay { |
|
|
position: fixed; |
|
|
inset: 0; |
|
|
background: rgba(0,0,0,0.55); |
|
|
display: none; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
z-index: 2000; |
|
|
} |
|
|
|
|
|
.modal { |
|
|
background: #fff; |
|
|
border-radius: 14px; |
|
|
width: 90%; |
|
|
max-width: 480px; |
|
|
padding: 0; |
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.25); |
|
|
transform: scale(0.8); |
|
|
opacity: 0; |
|
|
transition: all 0.3s ease-in-out; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.modal.show { |
|
|
transform: scale(1); |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.modal header { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
padding: 12px 16px; |
|
|
background: #f5f5f5; |
|
|
font-weight: bold; |
|
|
border-bottom: 1px solid #ddd; |
|
|
} |
|
|
|
|
|
.modal header strong { |
|
|
font-size: 1rem; |
|
|
} |
|
|
|
|
|
#closeApi { |
|
|
background: none; |
|
|
border: none; |
|
|
font-size: 1.2rem; |
|
|
cursor: pointer; |
|
|
color: #333; |
|
|
} |
|
|
|
|
|
.modal-body { |
|
|
padding: 16px; |
|
|
font-size: 0.95rem; |
|
|
} |
|
|
|
|
|
.modal-body p { |
|
|
margin: 6px 0; |
|
|
} |
|
|
|
|
|
.curl { |
|
|
margin: 12px 0; |
|
|
background: #f7f7f7; |
|
|
border-radius: 6px; |
|
|
padding: 10px; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.curl pre { |
|
|
white-space: pre-wrap; |
|
|
word-wrap: break-word; |
|
|
font-size: 0.85rem; |
|
|
margin: 0 0 8px 0; |
|
|
} |
|
|
|
|
|
.curl button { |
|
|
position: absolute; |
|
|
top: 8px; |
|
|
right: 8px; |
|
|
font-size: 0.8rem; |
|
|
} |
|
|
|
|
|
.dev { |
|
|
margin-top: 12px; |
|
|
text-align: center; |
|
|
} |
|
|
|
|
|
.dev strong { |
|
|
color: #222; |
|
|
} |
|
|
|
|
|
.dev .muted { |
|
|
color: #666; |
|
|
font-size: 0.8rem; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="theme-light"> |
|
|
<div class="app"> |
|
|
<header class="topbar"> |
|
|
<div class="brand">DOTO</div> |
|
|
<div class="meta"> |
|
|
<button id="apiBtn" class="icon-btn" title="API & Developer">/></button> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
<main class="main"> |
|
|
<section class="upload-card" id="uploadCard"> |
|
|
<div class="placeholder" id="placeholder"> |
|
|
<img src="/static/images/placeholder.gif" alt="placeholder" class="placeholder-gif"/> |
|
|
<div class="placeholder-text"> |
|
|
<strong>Drop a file or tap to upload</strong> |
|
|
<small>Files expire ~3 hours • max 2GB</small> |
|
|
</div> |
|
|
<input id="fileInput" type="file" class="file-input" /> |
|
|
</div> |
|
|
|
|
|
<div class="preview" id="preview" hidden> |
|
|
<div class="preview-left"> |
|
|
<div id="previewThumb" class="preview-thumb"></div> |
|
|
</div> |
|
|
<div class="preview-right"> |
|
|
<div id="previewName" class="filename"></div> |
|
|
<div id="previewSize" class="filesize"></div> |
|
|
<div class="progress-wrap"> |
|
|
<progress id="uploadProgress" value="0" max="100"></progress> |
|
|
<div id="progressText" class="progress-text"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<section class="history" id="historySection"> |
|
|
<div class="history-header"> |
|
|
<strong>Recent uploads</strong> |
|
|
<button id="clearHistory" class="link-btn">Clear</button> |
|
|
</div> |
|
|
<div id="historyList" class="history-list"></div> |
|
|
</section> |
|
|
</main> |
|
|
|
|
|
<footer class="sticky-footer"> |
|
|
<div class="left"> |
|
|
<button id="uploadBtn" class="big-btn">Upload</button> |
|
|
</div> |
|
|
<div class="right actions"> |
|
|
<button id="copyBtn" class="small-btn" title="Copy link">Copy</button> |
|
|
<button id="viewBtn" class="small-btn" title="View">View</button> |
|
|
<button id="dlBtn" class="small-btn" title="Download">Download</button> |
|
|
</div> |
|
|
</footer> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="apiModal" class="modal-overlay"> |
|
|
<div class="modal"> |
|
|
<header> |
|
|
<strong>API & Developer</strong> |
|
|
<button id="closeApi" class="icon-btn">✕</button> |
|
|
</header> |
|
|
<div class="modal-body"> |
|
|
<p><strong>Version:</strong> <span id="apiVersion">{{ api_version }}</span></p> |
|
|
<p><strong>Upload endpoint:</strong> <code>/api/upload</code></p> |
|
|
<p><strong>Max size:</strong> <code>{{ max_bytes }} bytes</code></p> |
|
|
<p><strong>Expiry:</strong> ~{{ expire_seconds }} seconds</p> |
|
|
<div class="curl"> |
|
|
<label>cURL example</label> |
|
|
<pre id="curlExample">curl -X POST -F "file=@/path/to/file" https://your-space-url/api/upload</pre> |
|
|
<button id="copyCurl" class="link-btn">Copy</button> |
|
|
</div> |
|
|
<hr/> |
|
|
<div class="dev"> |
|
|
<small>Made by <strong>Aditya Devarshi</strong></small><br/> |
|
|
<small class="muted">Files are public to anyone with the link. Avoid sensitive uploads.</small> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
window.APP_CONFIG = { |
|
|
API_VERSION: "{{ api_version }}", |
|
|
MAX_BYTES: {{ max_bytes }}, |
|
|
EXPIRE_SECONDS: {{ expire_seconds }} |
|
|
} |
|
|
|
|
|
const apiBtn = document.getElementById("apiBtn"); |
|
|
const apiModal = document.getElementById("apiModal"); |
|
|
const closeApi = document.getElementById("closeApi"); |
|
|
const copyCurl = document.getElementById("copyCurl"); |
|
|
const curlExample = document.getElementById("curlExample"); |
|
|
|
|
|
|
|
|
apiBtn.addEventListener("click", () => { |
|
|
apiModal.style.display = "flex"; |
|
|
requestAnimationFrame(() => { |
|
|
apiModal.querySelector(".modal").classList.add("show"); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
function closeModal() { |
|
|
const modalCard = apiModal.querySelector(".modal"); |
|
|
modalCard.classList.remove("show"); |
|
|
setTimeout(() => { |
|
|
apiModal.style.display = "none"; |
|
|
}, 300); |
|
|
} |
|
|
|
|
|
closeApi.addEventListener("click", closeModal); |
|
|
apiModal.addEventListener("click", (e) => { |
|
|
if (e.target === apiModal) closeModal(); |
|
|
}); |
|
|
|
|
|
|
|
|
copyCurl.addEventListener("click", () => { |
|
|
navigator.clipboard.writeText(curlExample.innerText).then(() => { |
|
|
copyCurl.innerText = "Copied!"; |
|
|
setTimeout(() => (copyCurl.innerText = "Copy"), 1500); |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
<script src="/static/js/app.js"></script> |
|
|
</body> |
|
|
</html> |
|
|
|