diff options
| author | Татьяна Фарбер <alexandr@asustem.ru> | 2026-10-21 17:36:08 +0400 |
|---|---|---|
| committer | Татьяна Фарбер <alexandr@asustem.ru> | 2026-10-21 17:36:08 +0400 |
| commit | a5ee4bf22b93f25b501c4bf684d9482283c13abc (patch) | |
| tree | f9517896cae66709e6180d0f286cf41452769747 | |
| -rw-r--r-- | README.md | 0 | ||||
| -rw-r--r-- | nginx.conf | 15 | ||||
| -rw-r--r-- | server.lua | 90 |
3 files changed, 105 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..3a61a3e --- /dev/null +++ b/nginx.conf @@ -0,0 +1,15 @@ +user root; +worker_processes auto; +worker_rlimit_nofile 16000; + +events { + worker_connections 1024; +} + +stream { + lua_code_cache on; + server { + listen 70; + content_by_lua_file /home/gopher/server.lua; + } +} diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..efdce32 --- /dev/null +++ b/server.lua @@ -0,0 +1,90 @@ +local lfs = require "lfs" +local data_dir = "/home/gemini/data" + +local function handle_gopher() + local sock = ngx.req.socket(true) + if not sock then return end + + sock:settimeout(4000) + local line, err = sock:receive("*l") + if not line then line = "" end + + -- Очищаем запрос от перевода строки + local selector = line:gsub("%s+", "") + local out = {} + + -- ========================================================================= + -- ГЛАВНОЕ МЕНЮ + -- ========================================================================= + if selector == "" or selector == "/" then + table.insert(out, "i[ GOPHER-АРХИВ ]\t\tlocalhost\t70") + table.insert(out, "i=========================================================\t\tlocalhost\t70") + table.insert(out, "iАвтономный листингдокументации.\t\tlocalhost\t70\n") + + -- Сканируем папки проектов в data + for dir in lfs.dir(data_dir) do + if dir ~= "." and dir ~= ".." and dir ~= "README.md" then + local attr = lfs.attributes(data_dir .. "/" .. dir) + if attr and attr.mode == "directory" then + -- Тип 1 означает директорию (папку) в протоколе Gopher + table.insert(out, string.format("1// %s\t/%s\tasustem.ru\t70", dir, dir)) + end + end + end + + table.insert(out, "\n1[ ВЕРНУТЬСЯ В КОРЕНЬ ]\t/\tasustem.ru\t70") + table.insert(out, "i---------------------------------------------------------\t\tlocalhost\t70") + table.insert(out, "iGopher-сервер на базе OpenResty/LuaJIT. Ссылка на капсулу:\t\tlocalhost\t70") + table.insert(out, ".") -- Точка на отдельной строке — маркер конца передачи в Gopher + + sock:send(table.concat(out, "\r\n") .. "\r\n") + return + end + + -- ========================================================================= + -- СЛУЖЕБНЫЙ ЛИСТИНГ КОНКРЕТНОЙ ПАПКИ ИЛИ ОТДАЧА ФАЙЛА + -- ========================================================================= + local local_path = data_dir .. selector + local attr = lfs.attributes(local_path) + + if not attr then + sock:send("Файл или папка не найдены на сервере.\t\tlocalhost\t70\r\n.\r\n") + return + end + + -- Если это папка проекта — выводим список её файлов + if attr.mode == "directory" then + table.insert(out, string.format("i[ СОДЕРЖИМОЕ ПРОЕКТА: %s ]\t\tlocalhost\t70", selector:sub(2))) + table.insert(out, "i=========================================================\t\tlocalhost\t70") + + for file in lfs.dir(local_path) do + if file ~= "." and file ~= ".." then + local f_path = selector .. "/" .. file + local g_type = "0" -- По умолчанию тип 0 (текст/документ) + + if file:match("%.jpg$") or file:match("%.png$") then + g_type = "I" -- Тип I означает графический файл (image) + elseif file:match("%.qet$") or file:match("%.pdf$") then + g_type = "9" -- Тип 9 означает бинарный файл (binary) + end + + table.insert(out, string.format("%s%s\t%s\t\t70", g_type, file, f_path)) + end + end + + table.insert(out, "\n1[ НАЗАД В ГЛАВНОЕ МЕНЮ ]\t/\\t70") + table.insert(out, ".") + sock:send(table.concat(out, "\r\n") .. "\r\n") + + -- Если это файл — стримим его бинарное содержимое в сокет напрямую + elseif attr.mode == "file" then + local f = io.open(local_path, "rb") + if f then + local content = f:read("*all") + f:close() + sock:send(content) -- В Gopher файлы отдаются без заголовков, чистым потоком + end + end +end + +handle_gopher() |
