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()