5 Commits

Author SHA1 Message Date
a56a2bc4fb мерж 1.2.0 2025-08-10 18:06:39 +00:00
0984e169eb ebat obnova 2025-08-10 20:45:45 +03:00
055a3167e8 fixed gzaza 2025-08-09 20:32:27 +03:00
41d1984d5b мерж 1.1.3 2025-08-09 17:11:56 +00:00
4ed6833a9e da emae 2025-08-09 20:00:09 +03:00
4 changed files with 98 additions and 33 deletions

View File

@@ -1,48 +1,81 @@
## debweb
# debweb
**debweb** - простой webserver для дебилов (for me) на асинхронных сокетах
## установка и настройка
# установка и настройка
debweb использует всего одну стороннюю библиотеку - aiofiles. ее можно установить с помощью
```bash
pip install aiofiles
```
> [!IMPORTANT]
конфигурация сия шедевра происходит в файле `config.py`
### основное
## основное
- `name` - название сервера, отображается в http заголовках
- `proxied` - указывает, находится ли сервер за прокси
### сеть
## сеть
- `addr` - адрес сервера
- `port` - порт сервера
### файлы и директории
## файлы и директории
- `log_file` - файл логов (по умолчанию вывод в консоль)
- `preset_file` - файл пресета. обычный html документ. но в нем нужно указать одиночный тег `<FILES>` для отображения файлов в директории
- `directory` - рабочая директория **обязательно с / на конце!!!!**
- `preset_file` - файл пресета
- `directory` - рабочая директория
### буферы
## буферы
- `read_buffer` - буфер для запроса
- `read_buffer` - размер буфера для запросов
- `write_buffer` - размер буфера при отправке файлов
### логи
## логи
- `start_msg` - лог при старте сервера
- `conn_msg` - лог при подключении
- `get_msg` - лог при GET запросе
`<ADDR>` будет заменен на адрес клиента
## теги
`<FILE>` будет заменен на файл / директорию, к которой запрашивается доступ
- `<ADDR>` - адрес клиента
- `<FILE>` - файл / директория, к которой запрашивается доступ
### ошибки
## шаблоны
- `e404_file` - html файл, который будет отправлен при ошибке 404
- `e404_msg` - лог при ошибке 404
- `file_entry` - шаблон для генерации строк файлов в листинге директории
- `dir_entry` - шаблон для генерации строк каталогов в листинге директории
- `time_format` - формат времени для всего документа
### теги шаблонов
- `<NAME>` - название элемента
- `<REL_PATH>` - относительный путь элемента
- `<CDATE>` - дата создания элемент
- `<MDATE>` - дата модификации элемента
- `<SIZE_B>` - размер файла в байтах
- `<SIZE_KB>` - размер файла в килобайтах
- `<SIZE_MB>` - размер файла в мегабайтах
- все остальные html теги
## preset.html
обычный html документ, являющийся шаблоном для листинга каталога
### теги пресета
- `<FILES>` - отображает все элементы директории
- `<FILE_COUNT>` - количество файлов
- `<DIR_COUNT>` - количество подкаталогов
- `<TOTAL_COUNT>` - общее количество элементов
- `<SERVER>` - название сервера
- `<LOAD_TIME>` - время обработки страницы
- `<SERVER_TIME>` - время на сервере
## ошибки
- `err_Files` - словарь с кодами ошибок и файлами, которые отправляются при этих ошибках
- `err_msgs` - словарь с кодами ошибок и логами, которые отправляются при этих ошибках

View File

@@ -1,4 +1,4 @@
name="debweb 1.1.3"
name="debweb 1.2.0"
proxied=False
addr="localhost"
@@ -15,6 +15,10 @@ start_msg="started at <ADDR>"
conn_msg="conn from <ADDR>"
get_msg="<ADDR> got <FILE>"
file_entry = "<a href='/<REL_PATH>'><NAME></a> <SIZE_KB> <CDATE><br>\n"
dir_entry = "<a href='/<REL_PATH>'><NAME></a> <CDATE><br>\n"
time_format = "%a %b %e %H:%M:%S %Z %Y"
err_files = {
404: "html/404.html",
403: "html/403.html",

47
main.py
View File

@@ -5,6 +5,8 @@ import datetime
import aiofiles
import asyncio
import config
import utils
import time
import os
STATUS = {
@@ -15,8 +17,6 @@ STATUS = {
418: "418 I'm a teapot"
}
# file_size = os.path.getsize(file_path)
class WebServer:
async def log(self, text: str, addr: tuple=None, file: str=None) -> None:
text = text.replace("<ADDR>", f"{addr[0]}:{addr[1]}" if addr else "")
@@ -62,9 +62,10 @@ class WebServer:
async def handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
addr = writer.get_extra_info("peername")
await self.log(config.conn_msg, addr)
conn_time = time.time()
rdata = await reader.read(config.read_buffer)
data = rdata.decode()
if not data: return
real_addr = None
@@ -90,7 +91,7 @@ class WebServer:
await self.send_headers(writer, 418, file_size)
await self.send_file(writer, config.err_files[418], file_size)
await writer.close()
writer.close()
await writer.wait_closed()
return
@@ -112,9 +113,10 @@ class WebServer:
files = ""
base_path = os.path.relpath(file_path, config.directory).replace('\\', '/')
if base_path == '.':
base_path = ''
if base_path == '.': base_path = ''
file_count = 0
dir_count = 0
for item in sorted(os.listdir(file_path)):
item_path = os.path.join(file_path, item)
is_dir = os.path.isdir(item_path)
@@ -125,16 +127,29 @@ class WebServer:
if is_dir:
rel_path += "/"
item += "/"
modify_time = os.path.getmtime(item_path)
modify_datetime = datetime.datetime.fromtimestamp(modify_time)
formatted_time = modify_datetime.strftime("%d.%m.%Y %H:%M:%S")
dir_count += 1
else: file_count += 1
rel_path_encoded = rel_path.replace(' ', '%20').replace('#', '%23')
files += f'<a class={"dir" if is_dir else "file"} href="/{rel_path_encoded}">{item}</a> | {formatted_time}<br>\n'
entry = config.dir_entry if is_dir else config.file_entry
entry = entry.replace("<NAME>", item)
entry = entry.replace("<REL_PATH>", rel_path_encoded)
entry = entry.replace("<CDATE>", utils.get_create_time(item_path, config.time_format))
entry = entry.replace("<MDATE>", utils.get_mod_time(item_path, config.time_format))
entry = entry.replace("<SIZE_B>", f"{os.path.getsize(item_path)}B")
entry = entry.replace("<SIZE_KB>", f"{format(os.path.getsize(item_path) / 1024, ".2f")}KB")
entry = entry.replace("<SIZE_MB>", f"{format(os.path.getsize(item_path) / 1024 ** 2, ".2f")}MB")
files += entry
resp = resp.replace("<FILES>", files)
resp = resp.replace("<FILE_COUNT>", str(file_count))
resp = resp.replace("<DIR_COUNT>", str(dir_count))
resp = resp.replace("<TOTAL_COUNT>", str(file_count + dir_count))
resp = resp.replace("<SERVER>", config.name)
resp = resp.replace("<LOAD_TIME>", format(time.time() - conn_time, ".3f"))
resp = resp.replace("<SERVER_TIME>", datetime.datetime.now().strftime(config.time_format))
resp = resp.encode()
await self.send_headers(writer, 200, len(resp))
@@ -144,10 +159,10 @@ class WebServer:
else:
await self.log(self.e404_msg, addr, file_path)
file_size = os.path.getsize(file_path)
await self.log(config.err_msgs[404], addr, config.err_files[404])
file_size = os.path.getsize(config.err_files[404])
await self.send_headers(writer, 404, file_size)
await self.send_file(writer, self._e404_file, file_size)
await self.send_file(writer, config.err_files[404], file_size)
writer.close()
await writer.wait_closed()

13
utils.py Normal file
View File

@@ -0,0 +1,13 @@
import datetime
import os
def get_mod_time(path: str, format: str="%a %b %e %H:%M:%S %Z %Y") -> str:
modify_time = os.path.getmtime(path)
modify_datetime = datetime.datetime.fromtimestamp(modify_time)
return modify_datetime.strftime(format)
def get_create_time(path: str, format: str="%a %b %e %H:%M:%S %Z %Y") -> str:
create_time = os.path.getctime(path)
create_datetime = datetime.datetime.fromtimestamp(create_time)
return create_datetime.strftime(format)