From caffb757c8847cad662d39cd1f9da9adfe6b61dd Mon Sep 17 00:00:00 2001 From: RomkaZVO Date: Thu, 20 Nov 2025 16:05:18 +0800 Subject: [PATCH] first upload --- README.md | 76 +++++++ busybox_nginx_reverse.txt | 50 +++++ index.c | 436 ++++++++++++++++++++++++++++++++++++++ status.c | 56 +++++ style.c | 37 ++++ style.css | 371 ++++++++++++++++++++++++++++++++ template.html | 133 ++++++++++++ 7 files changed, 1159 insertions(+) create mode 100644 README.md create mode 100755 busybox_nginx_reverse.txt create mode 100755 index.c create mode 100755 status.c create mode 100755 style.c create mode 100755 style.css create mode 100755 template.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..00568e6 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# 🗂️ Trashbox - Минималистичный файловый сервер + +### Установка зависимостей +``` bash +sudo apt update +sudo apt install nginx busybox gcc +``` +### Настройка структуры +```bash +mkdir -p /home/romkazvo/www/cgi-bin +cd /home/romkazvo/www/cgi-bin +``` +### Компиляция CGI приложений +```bash +gcc -o index.cgi index.c +gcc -o style.cgi style.c +gcc -o status.cgi status.c +chmod +x *.cgi +``` +### Конфигурация Nginx +Создайте файл конфигурации `/etc/nginx/sites-available/trashbox`: + +server { + listen 443 ssl http2; + server_name home.mashup.su www.home.mashup.su; + + ssl_certificate /etc/letsencrypt/live/www.home.mashup.su/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.home.mashup.su/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + + location = /gachi { + proxy_pass http://127.0.0.1:8001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600; + proxy_send_timeout 3600; + add_header X-Stream-Name "Gachi Stream" always; + add_header Access-Control-Allow-Origin "*" always; + add_header Access-Control-Allow-Methods "GET, OPTIONS, HEAD" always; + add_header Access-Control-Allow-Headers "Range, Accept-Encoding" always; + } + + location /cgi-bin/ { + proxy_pass http://127.0.0.1:8050; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + proxy_pass http://127.0.0.1:8050; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + listen 80; + server_name home.mashup.su www.home.mashup.su; + return 301 https://$server_name$request_uri; +} + + +### Запуск BusyBox +```bash +busybox httpd -p 127.0.0.1:8050 -h /home/romkazvo/www +``` diff --git a/busybox_nginx_reverse.txt b/busybox_nginx_reverse.txt new file mode 100755 index 0000000..54d2e5f --- /dev/null +++ b/busybox_nginx_reverse.txt @@ -0,0 +1,50 @@ +server { + listen 443 ssl http2; + server_name home.mashup.su www.home.mashup.su; + + ssl_certificate /etc/letsencrypt/live/www.home.mashup.su/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.home.mashup.su/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + + # Прокси для MPD потока /gachi - ИСПРАВЛЕННЫЙ ВАРИАНТ + location = /gachi { + proxy_pass http://127.0.0.1:8001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600; + proxy_send_timeout 3600; + add_header X-Stream-Name "Gachi Stream" always; + add_header Access-Control-Allow-Origin "*" always; + add_header Access-Control-Allow-Methods "GET, OPTIONS, HEAD" always; + add_header Access-Control-Allow-Headers "Range, Accept-Encoding" always; + } + + location /cgi-bin/ { + proxy_pass http://127.0.0.1:8050; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + proxy_pass http://127.0.0.1:8050; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + listen 80; + server_name home.mashup.su www.home.mashup.su; + return 301 https://$server_name$request_uri; +} diff --git a/index.c b/index.c new file mode 100755 index 0000000..7ae5bdc --- /dev/null +++ b/index.c @@ -0,0 +1,436 @@ +// /home/romkazvo/www/cgi-bin/index.c +// Основной файловый сервер +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ENTRIES 1000 +#define TEMPLATE_PATH "/home/romkazvo/www/cgi-bin/template.html" +#define OUTPUT_BUFFER_SIZE 65536 + +typedef struct { + char name[256]; + int is_dir; + long size; + char icon[8]; + char encoded_path[1024]; + char file_url[1024]; +} entry_t; + +typedef struct { + char *data; + size_t size; + size_t capacity; +} buffer_t; + +void buffer_init(buffer_t *buf) { + buf->capacity = OUTPUT_BUFFER_SIZE; + buf->data = malloc(buf->capacity); + buf->size = 0; +} + +void buffer_append(buffer_t *buf, const char *str) { + size_t len = strlen(str); + if (buf->size + len >= buf->capacity) { + buf->capacity *= 2; + buf->data = realloc(buf->data, buf->capacity); + } + memcpy(buf->data + buf->size, str, len); + buf->size += len; +} + +void buffer_append_size(buffer_t *buf, const char *str, size_t len) { + if (buf->size + len >= buf->capacity) { + buf->capacity *= 2; + buf->data = realloc(buf->data, buf->capacity); + } + memcpy(buf->data + buf->size, str, len); + buf->size += len; +} + +void buffer_free(buffer_t *buf) { + free(buf->data); +} + +const char* get_file_icon(const char* filename) { + const char *ext = strrchr(filename, '.'); + if (!ext) return "📄"; + + ext++; + + if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0 || + strcasecmp(ext, "png") == 0 || strcasecmp(ext, "gif") == 0 || + strcasecmp(ext, "webp") == 0 || strcasecmp(ext, "bmp") == 0) + return "🖼️"; + + if (strcasecmp(ext, "mp3") == 0 || strcasecmp(ext, "wav") == 0 || + strcasecmp(ext, "flac") == 0 || strcasecmp(ext, "ogg") == 0) + return "🎵"; + + if (strcasecmp(ext, "mp4") == 0 || strcasecmp(ext, "avi") == 0 || + strcasecmp(ext, "mkv") == 0 || strcasecmp(ext, "mov") == 0) + return "🎬"; + + if (strcasecmp(ext, "zip") == 0 || strcasecmp(ext, "rar") == 0 || + strcasecmp(ext, "7z") == 0 || strcasecmp(ext, "tar") == 0 || + strcasecmp(ext, "gz") == 0) + return "📦"; + + if (strcasecmp(ext, "pdf") == 0) return "📕"; + if (strcasecmp(ext, "doc") == 0 || strcasecmp(ext, "docx") == 0) return "📘"; + if (strcasecmp(ext, "xls") == 0 || strcasecmp(ext, "xlsx") == 0) return "📗"; + if (strcasecmp(ext, "txt") == 0) return "📝"; + + return "📄"; +} + +int compare_entries(const void *a, const void *b) { + const entry_t *entryA = (const entry_t *)a; + const entry_t *entryB = (const entry_t *)b; + + if (entryA->is_dir && !entryB->is_dir) return -1; + if (!entryA->is_dir && entryB->is_dir) return 1; + + return strcasecmp(entryA->name, entryB->name); +} + +void format_size(long size, char* buffer) { + if (size < 1024) { + snprintf(buffer, 32, "%ld B", size); + } else if (size < 1024 * 1024) { + snprintf(buffer, 32, "%.1f KB", size / 1024.0); + } else if (size < 1024 * 1024 * 1024) { + snprintf(buffer, 32, "%.1f MB", size / (1024.0 * 1024.0)); + } else { + snprintf(buffer, 32, "%.1f GB", size / (1024.0 * 1024.0 * 1024.0)); + } +} + +void url_encode(const char *src, char *dst, size_t dst_size) { + static const char *hex = "0123456789ABCDEF"; + char *p = dst; + size_t i = 0; + + while (*src && i < dst_size - 3) { + unsigned char c = (unsigned char)*src; + + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + strchr("-_.~", c)) { + *p++ = c; + i++; + } + else if (c == ' ') { + *p++ = '%'; + *p++ = '2'; + *p++ = '0'; + i += 3; + } + else { + *p++ = '%'; + *p++ = hex[(c >> 4) & 0xF]; + *p++ = hex[c & 0xF]; + i += 3; + } + src++; + } + *p = '\0'; +} + +void url_decode_enhanced(const char *src, char *dst, size_t dst_size) { + char *p = dst; + size_t decoded_len = 0; + + while (*src && decoded_len < dst_size - 1) { + if (*src == '%') { + if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) { + char hex[3] = {src[1], src[2], '\0'}; + unsigned char c = (unsigned char)strtol(hex, NULL, 16); + + if (c >= 0x80) { + *p++ = c; + decoded_len++; + } else if (c >= 0x20 || c == 0x0A || c == 0x0D) { + *p++ = c; + decoded_len++; + } else { + *p++ = '_'; + decoded_len++; + } + src += 3; + } else { + *p++ = *src++; + decoded_len++; + } + } else if (*src == '+') { + *p++ = ' '; + decoded_len++; + src++; + } else { + *p++ = *src++; + decoded_len++; + } + } + *p = '\0'; +} + +void safe_path_join(char *result, size_t result_size, const char *base, const char *path) { + snprintf(result, result_size, "%s/%s", base, path); +} + +void print_breadcrumb(buffer_t *buf, const char *display_path) { + buffer_append(buf, "
\n"); + buffer_append(buf, " 🏠 Главная"); + + if (display_path && display_path[0]) { + char temp_path[1024] = ""; + char encoded[2048]; + char *path_copy = strdup(display_path); + char *token = strtok(path_copy, "/"); + + while (token) { + buffer_append(buf, " / "); + + if (temp_path[0]) strcat(temp_path, "/"); + strcat(temp_path, token); + + url_encode(temp_path, encoded, sizeof(encoded)); + char link[4096]; + snprintf(link, sizeof(link), "%s", encoded, token); + buffer_append(buf, link); + + token = strtok(NULL, "/"); + } + free(path_copy); + } + buffer_append(buf, "\n
\n"); +} + +void print_content(buffer_t *buf, const char *base_path, const char *display_path) { + DIR *dir; + struct dirent *entry; + struct stat file_stat; + char full_path[1024]; + + entry_t entries[MAX_ENTRIES]; + int entry_count = 0; + int dir_count = 0, file_count = 0; + long long total_size = 0; + + dir = opendir(base_path); + if (!dir) { + char alt_path[1024]; + snprintf(alt_path, sizeof(alt_path), "/home/romkazvo/www"); + if (display_path[0]) { + char *encoded = strdup(display_path); + url_decode_enhanced(encoded, alt_path + strlen(alt_path), + sizeof(alt_path) - strlen(alt_path)); + free(encoded); + } + + dir = opendir(alt_path); + if (!dir) { + buffer_append(buf, "
\n"); + buffer_append(buf, "
📁
\n"); + buffer_append(buf, "

Ошибка открытия директории

\n"); + char error_msg[512]; + snprintf(error_msg, sizeof(error_msg), "

Путь: %s

\n", base_path); + buffer_append(buf, error_msg); + snprintf(error_msg, sizeof(error_msg), "

Ошибка: %s

\n", strerror(errno)); + buffer_append(buf, error_msg); + buffer_append(buf, "

← Вернуться на главную

\n"); + buffer_append(buf, "
\n"); + return; + } + } + + while ((entry = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + if (strcmp(entry->d_name, "cgi-bin") == 0) + continue; + + snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name); + + if (stat(full_path, &file_stat) == 0) { + strncpy(entries[entry_count].name, entry->d_name, sizeof(entries[0].name) - 1); + entries[entry_count].name[sizeof(entries[0].name) - 1] = '\0'; + + if (S_ISDIR(file_stat.st_mode)) { + entries[entry_count].is_dir = 1; + entries[entry_count].size = 0; + strcpy(entries[entry_count].icon, "📁"); + + char new_path[2048]; + snprintf(new_path, sizeof(new_path), "%s%s%s", display_path, + display_path[0] ? "/" : "", entries[entry_count].name); + url_encode(new_path, entries[entry_count].encoded_path, sizeof(entries[0].encoded_path)); + + dir_count++; + } else if (S_ISREG(file_stat.st_mode)) { + entries[entry_count].is_dir = 0; + entries[entry_count].size = file_stat.st_size; + strcpy(entries[entry_count].icon, get_file_icon(entry->d_name)); + + snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), + "%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name); + + file_count++; + total_size += file_stat.st_size; + } else { + continue; + } + entry_count++; + } + } + closedir(dir); + + if (entry_count == 0) { + buffer_append(buf, "
\n"); + buffer_append(buf, "
📄
\n"); + buffer_append(buf, "

Здесь пусто

\n"); + buffer_append(buf, "

В этой директории нет файлов или папок

\n"); + buffer_append(buf, "
\n"); + return; + } + + qsort(entries, entry_count, sizeof(entry_t), compare_entries); + + buffer_append(buf, "\n"); + + char total_size_str[32]; + format_size(total_size, total_size_str); + char stats[256]; + snprintf(stats, sizeof(stats), + "
\n Папки: %d | Файлы: %d | Общий размер: %s\n
\n", + dir_count, file_count, total_size_str); + buffer_append(buf, stats); +} + +void print_template(const char *base_path, const char *display_path) { + FILE *file = fopen(TEMPLATE_PATH, "r"); + if (!file) { + printf("Error: Cannot read template\n"); + return; + } + + buffer_t output; + buffer_init(&output); + + char line[4096]; + while (fgets(line, sizeof(line), file)) { + if (strstr(line, "")) { + print_breadcrumb(&output, display_path); + } else if (strstr(line, "")) { + print_content(&output, base_path, display_path); + } else { + buffer_append(&output, line); + } + } + fclose(file); + + fwrite(output.data, 1, output.size, stdout); + buffer_free(&output); +} + +int main() { + setlocale(LC_ALL, "en_US.UTF-8"); + setlocale(LC_CTYPE, "en_US.UTF-8"); + + printf("Content-type: text/html; charset=utf-8\n\n"); + + char base_path[1024] = "/home/romkazvo/www"; + char display_path[1024] = ""; + char safe_display_path[1024] = ""; + + char *query_string = getenv("QUERY_STRING"); + if (query_string) { + char *path_start = strstr(query_string, "path="); + if (path_start) { + path_start += 5; + char *path_end = strchr(path_start, '&'); + int path_len = path_end ? path_end - path_start : strlen(path_start); + + if (path_len > 0 && path_len < sizeof(display_path) - 1) { + char encoded_path[1024]; + strncpy(encoded_path, path_start, path_len); + encoded_path[path_len] = '\0'; + + url_decode_enhanced(encoded_path, display_path, sizeof(display_path)); + + strncpy(safe_display_path, display_path, sizeof(safe_display_path) - 1); + safe_display_path[sizeof(safe_display_path) - 1] = '\0'; + + safe_path_join(base_path, sizeof(base_path), "/home/romkazvo/www", safe_display_path); + } + } + } + + print_template(base_path, display_path); + return 0; +} \ No newline at end of file diff --git a/status.c b/status.c new file mode 100755 index 0000000..b9bf4f1 --- /dev/null +++ b/status.c @@ -0,0 +1,56 @@ +// /home/romkazvo/www/cgi-bin/status.c +// Отрисовка статистики +#include +#include +#include +#include + +long get_nginx_memory() { + long total_kb = 0; + char command[] = "ps -o rss= -C nginx 2>/dev/null"; + + FILE *ps = popen(command, "r"); + if (!ps) return 0; + + char line[64]; + while (fgets(line, sizeof(line), ps)) { + long kb; + if (sscanf(line, "%ld", &kb) == 1) { + total_kb += kb; + } + } + pclose(ps); + + return total_kb; +} + +int get_process_memory(const char *process_name, long *memory_kb) { + char command[256]; + snprintf(command, sizeof(command), "ps -o rss= -C %s 2>/dev/null | head -1", process_name); + + FILE *ps = popen(command, "r"); + if (!ps) return 0; + + int result = fscanf(ps, "%ld", memory_kb); + pclose(ps); + + return result == 1; +} + +int main() { + printf("Content-type: text/plain; charset=utf-8\n\n"); + + // Получаем память процессов + long busybox_kb = 0, nginx_kb = 0; + + get_process_memory("busybox", &busybox_kb); + nginx_kb = get_nginx_memory(); + + // Выводим в одну строку + printf("BusyBox: %ld KB | Nginx: %ld MB | Всего: %.1f MB", + busybox_kb, + nginx_kb / 1024, + (busybox_kb + nginx_kb) / 1024.0); + + return 0; +} \ No newline at end of file diff --git a/style.c b/style.c new file mode 100755 index 0000000..71db6d7 --- /dev/null +++ b/style.c @@ -0,0 +1,37 @@ +// Подгрузка CSS +#include +#include +#include +#include + +int main() { + char css_path[1024] = "/home/romkazvo/www/cgi-bin/style.css"; + struct stat st; + + if (stat(css_path, &st) != 0) { + printf("Status: 404 Not Found\n"); + printf("Content-type: text/plain\n\n"); + printf("CSS file not found\n"); + return 1; + } + + printf("Content-type: text/css\n"); + printf("Cache-Control: public, max-age=3600\n\n"); + + FILE *css_file = fopen(css_path, "r"); + if (!css_file) { + printf("Status: 500 Internal Server Error\n"); + printf("Content-type: text/plain\n\n"); + printf("Cannot open CSS file\n"); + return 1; + } + + char buffer[4096]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), css_file)) > 0) { + fwrite(buffer, 1, bytes_read, stdout); + } + + fclose(css_file); + return 0; +} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100755 index 0000000..2225857 --- /dev/null +++ b/style.css @@ -0,0 +1,371 @@ +:root { + --bg-primary: #f8f9fa; + --bg-secondary: #ffffff; + --bg-header: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --text-primary: #333333; + --text-secondary: #5f6368; + --text-muted: #6c757d; + --border-color: #e9ecef; + --border-light: #f1f3f4; + --accent-blue: #1a73e8; + --accent-green: #188038; + --hover-bg: #f8f9fa; + --shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.dark-theme { + --bg-primary: #121212; + --bg-secondary: #1e1e1e; + --bg-header: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); + --text-primary: #e0e0e0; + --text-secondary: #a0a0a0; + --text-muted: #888888; + --border-color: #2d3748; + --border-light: #2d3748; + --accent-blue: #63b3ed; + --accent-green: #68d391; + --hover-bg: #2d3748; + --shadow: 0 2px 4px rgba(0,0,0,0.3); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; + min-height: 100vh; + transition: none; +} + +body.loaded * { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; +} + +.container { + max-width: 100%; + margin: 0 auto; + background: var(--bg-secondary); + min-width: 320px; + min-height: 100vh; + box-shadow: var(--shadow); +} + +@media (min-width: 768px) { + .container { + max-width: 1000px; + margin: 10px auto; + min-height: auto; + border-radius: 8px; + overflow: hidden; + } +} + +.theme-toggle { + position: fixed; + top: 10px; + right: 10px; + background: rgba(255,255,255,0.2); + border: none; + border-radius: 50%; + width: 50px; + height: 50px; + font-size: 1.3rem; + cursor: pointer; + z-index: 1000; + backdrop-filter: blur(10px); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.dark-theme .theme-toggle { + background: rgba(0,0,0,0.2); + color: var(--text-primary); +} + +.scroll-top { + position: fixed; + bottom: 20px; + right: 20px; + width: 50px; + height: 50px; + background: var(--accent-blue); + color: white; + border: none; + border-radius: 50%; + font-size: 1.3rem; + cursor: pointer; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 1000; + box-shadow: 0 2px 10px rgba(0,0,0,0.2); + display: flex; + align-items: center; + justify-content: center; +} + +.scroll-top.visible { + opacity: 1; + visibility: visible; +} + +.scroll-top:hover { + background: var(--accent-green); + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(0,0,0,0.3); +} + +.theme-toggle:hover { + transform: scale(1.1); + background: rgba(255,255,255,0.3); +} + +.dark-theme .theme-toggle:hover { + background: rgba(0,0,0,0.3); +} + +.header { + background: var(--bg-header); + color: white; + padding: 1.5rem 1rem; + text-align: center; + position: relative; +} + +h1 { + font-size: 1.8rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.content { + padding: 1rem; + min-width: 0; +} + +.breadcrumb { + background: var(--bg-primary); + padding: 1rem; + margin: 0 0 1rem 0; + border-bottom: 1px solid var(--border-color); + font-size: 0.9rem; + overflow-x: auto; + white-space: nowrap; + -webkit-overflow-scrolling: touch; +} + +.breadcrumb a { + color: var(--accent-blue); + text-decoration: none; + font-weight: 500; + display: inline-block; +} + +.breadcrumb a:hover { + text-decoration: underline; +} + +ul { + list-style: none; + padding: 0; + margin: 0; +} + +li { + padding: 1rem 0.5rem; + border-bottom: 1px solid var(--border-light); + display: flex; + align-items: center; + flex-wrap: wrap; +} + +li:last-child { border-bottom: none; } + +li:hover { + background-color: var(--hover-bg); +} + +a { + text-decoration: none; + flex-grow: 1; + display: flex; + align-items: center; + min-width: 0; + padding-right: 0.5rem; +} + +.file-link { + color: var(--accent-blue); + font-weight: 500; +} + +.dir-link { + color: var(--accent-green); + font-weight: 600; +} + +.size { + color: var(--text-secondary); + font-size: 0.85rem; + margin-left: auto; + padding-left: 0.5rem; + flex-shrink: 0; + white-space: nowrap; +} + +.file-icon { + margin-right: 1rem; + font-size: 1.4rem; + flex-shrink: 0; + width: 24px; + text-align: center; +} + +.file-name { + flex-grow: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.95rem; +} + +.section-title { + color: var(--text-secondary); + font-size: 0.9rem; + font-weight: 600; + margin: 1.5rem 0 0.5rem 0; + padding: 0.75rem 0.5rem; + border-bottom: 2px solid var(--border-light); + text-transform: uppercase; + letter-spacing: 0.5px; + background: var(--bg-primary); +} + +.stats { + text-align: center; + color: var(--text-secondary); + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid var(--border-color); + font-size: 0.9rem; +} + +.empty-state { + text-align: center; + padding: 3rem 1rem; + color: var(--text-secondary); +} + +.empty-state .icon { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.5; +} + +.footer { + background: var(--bg-primary); + border-top: 1px solid var(--border-color); + padding: 1rem; + text-align: center; + color: var(--text-secondary); + font-size: 0.9rem; +} + +.footer-content { + max-width: 1000px; + margin: 0 auto; + text-align: center; +} + +.footer-content span, +.footer-content a { + display: inline; + vertical-align: baseline; +} + +.footer-link { + color: var(--accent-blue); + text-decoration: none; + font-weight: 500; + transition: opacity 0.2s ease; + margin-left: 0.3rem; +} + +.footer-link:hover { + opacity: 0.8; + text-decoration: underline; +} + +@media (max-width: 480px) { + .theme-toggle { + top: 5px; + right: 5px; + width: 45px; + height: 45px; + font-size: 1.2rem; + } + .scroll-top { + bottom: 15px; + right: 15px; + width: 45px; + height: 45px; + font-size: 1.2rem; + } + .header { + padding: 1rem 0.5rem; + } + h1 { + font-size: 1.5rem; + padding: 0 0.5rem; + } + .content { + padding: 0.75rem; + } + .breadcrumb { + padding: 0.75rem; + font-size: 0.85rem; + } + li { + padding: 0.75rem 0.25rem; + } + .size { + font-size: 0.8rem; + padding-left: 0.25rem; + } + .file-icon { + margin-right: 0.75rem; + font-size: 1.2rem; + width: 20px; + } + .file-name { + font-size: 0.9rem; + } + .section-title { + font-size: 0.85rem; + padding: 0.5rem; + margin: 1rem 0 0.25rem 0; + } + .footer { + padding: 0.75rem; + font-size: 0.85rem; + } +} + +@media (max-width: 360px) { + .container { + min-width: 100%; + margin: 0; + } + .content { + padding: 0.5rem; + } + .file-name { + font-size: 0.85rem; + } + .size { + font-size: 0.75rem; + } +} \ No newline at end of file diff --git a/template.html b/template.html new file mode 100755 index 0000000..10c283d --- /dev/null +++ b/template.html @@ -0,0 +1,133 @@ + + + + + + Trashbox + + + + + + + + + + + + + + + + + + + + + + + +
+
+

Trashbox

+
+ Моя личная помоечка +
+
+ +
+ + +
+
+ +
+ + +
+ Загрузка статистики... +
+ + +
+ + + + + + \ No newline at end of file