Files
Trashbox/www/cgi-bin/template.html

546 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
<title>Trashbox - личная помоечка</title>
<link rel="shortcut icon" href="/pic.svg" type="image/svg">
<link rel="stylesheet" href="/cgi-bin/style.cgi">
<meta property="og:image" content="https://home.mashup.su/og.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="315" />
<meta property="og:locale" content="ru_RU" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Файлопомойка от RomkaZVO" />
<meta property="og:description" content="Моя личная помоечка" />
<meta property="og:url" content="https://home.mashup.su/" />
<meta property="og:site_name" content="Помойка от RomkaZVO" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Файлопомойка от RomkaZVO" />
<meta name="twitter:description" content="Моя личная помоечка" />
<meta name="twitter:image" content="https://home.mashup.su/og.png" />
<meta name="description" content="Моя личная помоечка" />
<meta name="keywords" content="RomkaZVO, mashup, mashup su, файлы, обмен, скачать, загрузить" />
<script>
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
(function() {
const savedTheme = getCookie('theme');
if (savedTheme === 'dark') {
document.documentElement.classList.add('dark-theme');
}
})();
</script>
<style>
.preview-btn,
.copy-btn,
.download-btn {
background: transparent !important;
color: var(--text-secondary) !important;
border: 1px solid var(--border-color) !important;
border-radius: 6px !important;
width: 32px !important;
height: 32px !important;
cursor: pointer !important;
font-size: 0.85rem !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.2s ease !important;
opacity: 0.7 !important;
flex-shrink: 0 !important;
}
.preview-btn:hover,
.copy-btn:hover,
.download-btn:hover {
background: var(--accent-blue) !important;
color: white !important;
border-color: var(--accent-blue) !important;
opacity: 1 !important;
transform: scale(1.05) !important;
}
#copyNotification {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
background: var(--accent-green);
color: white;
padding: 12px 24px;
border-radius: 8px;
font-weight: 600;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
z-index: 3000;
transition: opacity 0.3s;
opacity: 1;
}
@media (max-width: 480px) {
.preview-btn,
.copy-btn,
.download-btn {
width: 28px !important;
height: 28px !important;
font-size: 0.75rem !important;
}
}
</style>
</head>
<body>
<button class="theme-toggle" onclick="toggleTheme()">🌙</button>
<div class="container">
<div class="header">
<div style="display:flex;align-items:center;justify-content:center;gap:0.0002rem;flex-wrap:wrap;">
<img src="/pic.svg" alt="Trashbox" style="max-width:120px;height:auto;border-radius:12px;flex-shrink:0;">
<div>
<h1 style="margin:0;">Trashbox</h1>
<div style="opacity:0.9;font-size:1.1rem;margin-top:0.2rem;">
Склад всякой всячины
</div>
</div>
</div>
</div>
<div style="text-align:center;padding:0.5rem 1rem;font-size:1.1rem;color:var(--text-secondary);border-bottom:1px solid var(--border-color);background:var(--bg-primary);">
<span style="opacity:0.7;">Личный уголок для обмена файлами. Заливаю сюда то, чем хочу поделиться, без всяких лимитов и сторонних сервисов.</span>
</div>
<div class="content">
<!--BREADCRUMB-->
<!--CONTENT-->
</div>
</div>
<footer class="footer">
<div class="footer-content">
<span>Навайбкодил с любовью ❤️</span>
<a href="https://romkazvo.ru" target="_blank" class="footer-link">RomkaZVO</a><br>
Работает на BusyBox httpd и Nginx для SSL<br>
Исходники <a href="https://git.mashup.su/RomkaZVO/Trashbox" target="_blank" class="footer-link">здесь</a><br>
</div>
<div id="status" style="margin-top: 1rem; font-size: 0.8rem; opacity: 0.7; line-height: 1.4;">
Загрузка статистики...
</div>
<script>
(function() {
const statusEl = document.getElementById('status');
if (!statusEl) return;
setTimeout(function() {
fetch('/cgi-bin/status.cgi')
.then(response => response.text())
.then(data => {
statusEl.innerHTML = data.replace(/\n/g, '<br>');
})
.catch(function() {
statusEl.innerHTML = 'Ошибка загрузки статуса';
});
}, 300);
})();
</script>
</footer>
<button class="scroll-top" onclick="scrollToTop()"></button>
<div id="previewModal" class="preview-modal">
<div class="preview-content" id="previewContent">
<div class="preview-header">
<h3 class="preview-title" id="previewTitle">Предпросмотр файла</h3>
<button class="close-preview" onclick="closePreview()">×</button>
</div>
<iframe id="previewFrame" class="preview-iframe" sandbox="allow-scripts allow-same-origin"></iframe>
</div>
</div>
<script>
// ============================================
// СОХРАНЯЕМ ТОКЕН В localStorage
// ============================================
(function() {
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
if (token) {
localStorage.setItem('trashbox_token', token);
if (window.history && window.history.replaceState) {
const newUrl = window.location.pathname + window.location.search.replace(/[&?]token=[^&]*/, '').replace(/^&/, '?').replace(/\?$/, '');
window.history.replaceState({}, document.title, newUrl);
}
}
})();
// ============================================
// ДОБАВЛЯЕМ ТОКЕН ВО ВСЕ ССЫЛКИ
// ============================================
document.addEventListener('DOMContentLoaded', function() {
const savedToken = localStorage.getItem('trashbox_token');
if (savedToken) {
document.querySelectorAll('a[href*="/cgi-bin/index.cgi"]').forEach(function(link) {
if (!link.href.includes('token=')) {
link.href += (link.href.includes('?') ? '&' : '?') + 'token=' + savedToken;
}
});
}
});
// ============================================
// ГЛОБАЛЬНЫЕ ФУНКЦИИ
// ============================================
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// ============================================
// ПОКАЗ ФОРМЫ ПАРОЛЯ (с проверкой токена)
// ============================================
window.showPasswordForm = function(encodedPath, folderName) {
const savedToken = localStorage.getItem('trashbox_token');
if (savedToken) {
window.location.href = '/cgi-bin/index.cgi?path=' + encodedPath + '&token=' + savedToken;
return;
}
const modal = document.getElementById('previewModal');
const content = document.getElementById('previewContent');
const title = document.getElementById('previewTitle');
const frame = document.getElementById('previewFrame');
if (!modal || !content || !title || !frame) {
console.error('Modal elements not found!');
return;
}
frame.style.display = 'none';
title.textContent = '🔒 Введите пароль для папки: ' + folderName;
const oldContainer = document.getElementById('passwordFormContainer');
if (oldContainer) oldContainer.remove();
const container = document.createElement('div');
container.id = 'passwordFormContainer';
container.style.cssText = 'display:flex;justify-content:center;align-items:center;padding:2rem;flex-grow:1;min-height:300px;';
container.innerHTML = `
<div style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;padding:2rem;max-width:400px;width:100%;text-align:center;box-shadow:0 4px 20px rgba(0,0,0,0.1);">
<div style="font-size:3rem;margin-bottom:1rem;">🔒</div>
<h2 style="font-size:1.3rem;margin-bottom:0.5rem;color:var(--text-primary);">Защищенная папка</h2>
<p style="color:var(--text-secondary);margin-bottom:1.5rem;font-size:0.95rem;">
Введите пароль для доступа к <strong style="color:var(--text-primary);word-break:break-all;">${folderName}</strong>
</p>
<form method="POST" action="/cgi-bin/index.cgi" onsubmit="return validatePasswordForm(this)">
<input type="hidden" name="path" value="${encodedPath}">
<input type="password" name="password" placeholder="Введите пароль" required style="width:100%;padding:0.75rem 1rem;border:2px solid var(--border-color);border-radius:8px;font-size:1rem;background:var(--bg-primary);color:var(--text-primary);transition:border-color 0.2s ease;box-sizing:border-box;">
<button type="submit" style="width:100%;padding:0.75rem;background:var(--accent-blue);color:white;border:none;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;transition:background 0.2s ease;margin-top:0.75rem;">Войти</button>
</form>
<p class="error-msg" style="color:#dc3545;font-size:0.9rem;min-height:1.5rem;margin:0.5rem 0;"></p>
<button onclick="closePreview()" style="background:transparent;border:none;color:var(--text-secondary);cursor:pointer;font-size:0.9rem;margin-top:0.5rem;">✖ Закрыть</button>
</div>
`;
content.appendChild(container);
content.style.width = '500px';
content.style.height = 'auto';
content.style.maxWidth = '90vw';
content.style.maxHeight = 'auto';
content.style.margin = 'auto';
content.style.position = 'absolute';
content.style.left = '50%';
content.style.top = '50%';
content.style.transform = 'translate(-50%, -50%)';
modal.style.display = 'block';
document.body.style.overflow = 'hidden';
setTimeout(() => {
const input = container.querySelector('input[type="password"]');
if (input) input.focus();
}, 100);
};
window.validatePasswordForm = function(form) {
const password = form.querySelector('input[type="password"]').value;
const errorMsg = form.parentElement.querySelector('.error-msg');
if (!password || password.length < 1) {
if (errorMsg) errorMsg.textContent = '❌ Пожалуйста, введите пароль';
return false;
}
return true;
};
window.downloadFile = function(path) {
let cleanPath = path;
cleanPath = cleanPath.replace(/[?&]token=[^&]*/g, '');
cleanPath = cleanPath.replace(/[?&]$/, '');
let fileName = cleanPath.split('/').pop().split('?')[0];
fileName = decodeURIComponent(fileName);
const link = document.createElement('a');
link.href = cleanPath;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
window.copyLink = function(path) {
let cleanPath = path;
cleanPath = cleanPath.replace(/[?&]token=[^&]*/g, '');
cleanPath = cleanPath.replace(/[?&]$/, '');
if (!cleanPath || cleanPath === '') cleanPath = '/';
const url = window.location.origin + cleanPath;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function() {
showNotification('✅ Ссылка скопирована!');
}).catch(function() {
fallbackCopy(url);
});
} else {
fallbackCopy(url);
}
};
function fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '-9999px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showNotification('✅ Ссылка скопирована!');
} catch (e) {
alert('Не удалось скопировать ссылку');
}
document.body.removeChild(textarea);
}
function showNotification(msg) {
const old = document.getElementById('copyNotification');
if (old) old.remove();
const div = document.createElement('div');
div.id = 'copyNotification';
div.textContent = msg;
document.body.appendChild(div);
setTimeout(() => {
div.style.opacity = '0';
setTimeout(() => div.remove(), 400);
}, 2500);
}
window.scrollToTop = function() {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
window.addEventListener('scroll', function() {
const scrollBtn = document.querySelector('.scroll-top');
if (window.scrollY > 300) {
scrollBtn.classList.add('visible');
} else {
scrollBtn.classList.remove('visible');
}
});
window.toggleTheme = function() {
const isDark = document.body.classList.contains('dark-theme');
const newTheme = isDark ? 'light' : 'dark';
document.body.classList.toggle('dark-theme');
document.documentElement.classList.toggle('dark-theme');
const button = document.querySelector('.theme-toggle');
button.textContent = isDark ? '🌙' : '☀️';
const date = new Date();
date.setFullYear(date.getFullYear() + 1);
document.cookie = 'theme=' + newTheme + '; expires=' + date.toUTCString() + '; path=/';
};
window.openPreview = function(fileUrl) {
const modal = document.getElementById('previewModal');
const frame = document.getElementById('previewFrame');
const content = document.getElementById('previewContent');
const title = document.getElementById('previewTitle');
const oldContainer = document.getElementById('passwordFormContainer');
if (oldContainer) oldContainer.remove();
frame.style.display = 'block';
const fileName = fileUrl.split('/').pop();
const decodedFileName = decodeURIComponent(fileName);
title.textContent = decodedFileName;
const fileExt = fileName.split('.').pop().toLowerCase();
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'ico'];
const textTypes = ['txt', 'md', 'html', 'htm', 'css', 'js', 'json', 'xml', 'csv'];
const audioTypes = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];
const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'avi', 'mkv'];
const isPDF = fileExt === 'pdf';
frame.src = '';
let imgContainer = document.getElementById('previewImageContainer');
if (!imgContainer) {
imgContainer = document.createElement('div');
imgContainer.id = 'previewImageContainer';
imgContainer.className = 'preview-image-container';
content.appendChild(imgContainer);
}
imgContainer.style.display = 'none';
imgContainer.innerHTML = '';
if (imageTypes.includes(fileExt)) {
frame.style.display = 'none';
imgContainer.style.display = 'flex';
const img = document.createElement('img');
img.src = fileUrl;
img.alt = fileName;
img.className = 'preview-image';
img.onload = function() { adjustModalForImage(this); };
img.onerror = function() {
console.error('Failed to load image:', fileUrl);
frame.style.display = 'block';
imgContainer.style.display = 'none';
frame.src = fileUrl;
};
imgContainer.appendChild(img);
content.style.width = '90vw';
content.style.height = '90vh';
content.style.maxWidth = 'none';
content.style.maxHeight = 'none';
content.style.margin = '2% auto';
content.style.position = 'relative';
content.style.left = 'auto';
content.style.top = 'auto';
content.style.transform = 'none';
} else {
frame.style.display = 'block';
imgContainer.style.display = 'none';
if (textTypes.includes(fileExt)) {
content.style.width = '80vw';
content.style.height = '70vh';
content.style.maxWidth = '800px';
content.style.maxHeight = '600px';
} else if (audioTypes.includes(fileExt)) {
content.style.width = '500px';
content.style.height = '300px';
content.style.maxWidth = '90vw';
content.style.maxHeight = '400px';
} else if (videoTypes.includes(fileExt)) {
content.style.width = '90vw';
content.style.height = '80vh';
content.style.maxWidth = '1200px';
content.style.maxHeight = '800px';
} else if (isPDF) {
content.style.width = '90vw';
content.style.height = '90vh';
content.style.maxWidth = '1200px';
content.style.maxHeight = '900px';
frame.classList.add('pdf');
} else {
content.style.width = '80vw';
content.style.height = '80vh';
content.style.maxWidth = '1000px';
content.style.maxHeight = '800px';
}
content.style.margin = '2% auto';
content.style.position = 'relative';
content.style.left = 'auto';
content.style.top = 'auto';
content.style.transform = 'none';
frame.src = fileUrl;
}
modal.style.display = 'block';
document.body.style.overflow = 'hidden';
};
window.adjustModalForImage = function(img) {
const content = document.getElementById('previewContent');
const maxWidth = window.innerWidth * 0.95;
const maxHeight = window.innerHeight * 0.95;
const imgWidth = img.naturalWidth;
const imgHeight = img.naturalHeight;
let scale = 1;
if (imgWidth > maxWidth) scale = Math.min(scale, maxWidth / imgWidth);
if (imgHeight > maxHeight) scale = Math.min(scale, maxHeight / imgHeight);
const displayWidth = imgWidth * scale;
const displayHeight = imgHeight * scale;
content.style.width = (displayWidth + 20) + 'px';
content.style.height = (displayHeight + 80) + 'px';
content.style.margin = 'auto';
content.style.position = 'absolute';
content.style.left = '50%';
content.style.top = '50%';
content.style.transform = 'translate(-50%, -50%)';
};
window.closePreview = function() {
const modal = document.getElementById('previewModal');
const frame = document.getElementById('previewFrame');
const content = document.getElementById('previewContent');
modal.style.display = 'none';
frame.src = '';
frame.classList.remove('pdf');
frame.style.display = 'block';
const oldContainer = document.getElementById('passwordFormContainer');
if (oldContainer) oldContainer.remove();
content.style.width = '';
content.style.height = '';
content.style.maxWidth = '';
content.style.maxHeight = '';
content.style.margin = '';
content.style.position = '';
content.style.left = '';
content.style.top = '';
content.style.transform = '';
const imgContainer = document.getElementById('previewImageContainer');
if (imgContainer) {
imgContainer.style.display = 'none';
imgContainer.innerHTML = '';
}
const title = document.getElementById('previewTitle');
title.textContent = 'Предпросмотр файла';
document.body.style.overflow = 'auto';
};
document.getElementById('previewModal').addEventListener('click', function(e) {
if (e.target === this) closePreview();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closePreview();
});
document.addEventListener('DOMContentLoaded', function() {
const savedTheme = getCookie('theme');
const button = document.querySelector('.theme-toggle');
if (savedTheme === 'dark') {
document.body.classList.add('dark-theme');
button.textContent = '☀️';
} else {
document.body.classList.remove('dark-theme');
button.textContent = '🌙';
}
document.body.classList.add('loaded');
});
document.addEventListener('touchstart', function() {}, { passive: true });
</script>
</body>
</html>