546 lines
24 KiB
HTML
546 lines
24 KiB
HTML
<!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> |