feat: autoindex
This commit is contained in:
parent
b27b3fe2d2
commit
0534d07948
@ -1,7 +1,8 @@
|
||||
#include <webserv/handler/FileHandler.hpp>
|
||||
#include "webserv/utils/AutoIndex.hpp"
|
||||
|
||||
#include <webserv/config/AConfig.hpp> // for AConfig
|
||||
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||
#include <webserv/handler/FileHandler.hpp>
|
||||
#include <webserv/handler/MIMETypes.hpp> // for MIMETypes
|
||||
#include <webserv/handler/URI.hpp> // for URI
|
||||
#include <webserv/http/HttpConstants.hpp> // for NOT_FOUND, FORBIDDEN, OK
|
||||
@ -53,7 +54,6 @@ void FileHandler::handleDirectory(const std::string &dirpath, ResourceType type)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
Log::debug("Requested path is a directory: " + dirpath);
|
||||
|
||||
if (type == DIRECTORY_INDEX)
|
||||
{
|
||||
auto possible_indexes = config_->get<std::vector<std::string>>("index").value();
|
||||
@ -71,7 +71,10 @@ void FileHandler::handleDirectory(const std::string &dirpath, ResourceType type)
|
||||
if (type == DIRECTORY_AUTOINDEX)
|
||||
{
|
||||
Log::debug("Requested path is a directory: " + dirpath);
|
||||
ErrorHandler::createErrorResponse(Http::StatusCode::FORBIDDEN, response_, config_);
|
||||
// ErrorHandler::createErrorResponse(Http::StatusCode::FORBIDDEN, response_, config_);
|
||||
// TODO: This doesn't trigger for some reason :p
|
||||
response_.setBody(AutoIndex::generate(dirpath));
|
||||
response_.setStatus(Http::StatusCode::OK);
|
||||
return;
|
||||
}
|
||||
ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_);
|
||||
|
||||
83
webserv/utils/AutoIndex.cpp
Normal file
83
webserv/utils/AutoIndex.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include <webserv/utils/AutoIndex.hpp>
|
||||
#include <webserv/utils/FileUtils.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::string AutoIndex::generate(const std::string &dir)
|
||||
{
|
||||
std::ostringstream html;
|
||||
|
||||
html << "<!DOCTYPE html>\n"
|
||||
<< "<html><head>\n"
|
||||
<< "<title>Index of " << dir << "</title>\n"
|
||||
<< "<style>\n"
|
||||
<< "body { font-family: Arial, sans-serif; margin: 40px; }\n"
|
||||
<< "h1 { border-bottom: 1px solid #ccc; }\n"
|
||||
<< "table { border-collapse: collapse; width: 100%; }\n"
|
||||
<< "th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid #ddd; }\n"
|
||||
<< "th { background-color: #f5f5f5; }\n"
|
||||
<< "a { text-decoration: none; color: #0066cc; }\n"
|
||||
<< "a:hover { text-decoration: underline; }\n"
|
||||
<< ".dir { font-weight: bold; }\n"
|
||||
<< "</style>\n"
|
||||
<< "</head><body>\n"
|
||||
<< "<h1>Index of " << dir << "</h1>\n"
|
||||
<< "<table>\n"
|
||||
<< "<tr><th>Name</th><th>Last Modified</th><th>Size</th></tr>\n";
|
||||
|
||||
// Add parent directory link if not root
|
||||
if (dir != "/")
|
||||
{
|
||||
html << "<tr><td><a href=\"../\" class=\"dir\">[Parent Directory]</a></td><td>-</td><td>-</td></tr>\n";
|
||||
}
|
||||
|
||||
auto entries = FileUtils::listDirectory(dir);
|
||||
|
||||
for (const auto &entry : entries)
|
||||
{
|
||||
std::string href = entry.path().filename().string();
|
||||
if (entry.is_directory())
|
||||
{
|
||||
href += "/";
|
||||
}
|
||||
|
||||
html << "<tr><td><a href=\"" << href << "\"";
|
||||
if (entry.is_directory())
|
||||
{
|
||||
html << " class=\"dir\"";
|
||||
}
|
||||
html << ">" << entry.path().filename().string();
|
||||
if (entry.is_directory())
|
||||
{
|
||||
html << "/";
|
||||
}
|
||||
html << "</a></td>" << "<td></td>"; //<< std::format("{:%Y-%m-%d %H:%M:%S}", entry.last_write_time()) << "</td>"
|
||||
html << "<td>" << (entry.is_directory() ? "-" : formatFileSize(entry.file_size())) << "</td></tr>\n";
|
||||
}
|
||||
|
||||
html << "</table>\n"
|
||||
<< "<hr><p><em>webserv</em></p>\n"
|
||||
<< "</body></html>\n";
|
||||
|
||||
return html.str();
|
||||
}
|
||||
|
||||
std::string AutoIndex::formatFileSize(uintmax_t size)
|
||||
{
|
||||
static const std::vector<std::string> units = {"B", "KB", "MB", "GB", "TB"};
|
||||
int unitIndex = 0;
|
||||
auto displaySize = static_cast<double>(size);
|
||||
|
||||
while (displaySize >= 1024 && unitIndex < 4)
|
||||
{
|
||||
displaySize /= 1024;
|
||||
++unitIndex;
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss.precision(2);
|
||||
oss << std::fixed << displaySize << " " << units.at(unitIndex);
|
||||
return oss.str();
|
||||
}
|
||||
19
webserv/utils/AutoIndex.hpp
Normal file
19
webserv/utils/AutoIndex.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class AutoIndex
|
||||
{
|
||||
public:
|
||||
AutoIndex() = delete;
|
||||
AutoIndex(const AutoIndex &) = delete;
|
||||
AutoIndex &operator=(const AutoIndex &) = delete;
|
||||
AutoIndex(AutoIndex &&) = delete;
|
||||
AutoIndex &operator=(AutoIndex &&) = delete;
|
||||
~AutoIndex() = delete;
|
||||
static std::string generate(const std::string &dir);
|
||||
|
||||
private:
|
||||
static std::string formatFileSize(uintmax_t size);
|
||||
};
|
||||
@ -1,11 +1,12 @@
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/utils/FileUtils.hpp>
|
||||
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
|
||||
#include <cstring> // for size_t
|
||||
#include <filesystem>
|
||||
#include <fstream> // for basic_ifstream, basic_ios, basic_istream, ios, ifstream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, streamsize
|
||||
#include <iterator> // for istreambuf_iterator, operator==
|
||||
#include <string> // for basic_string, string, char_traits, operator+
|
||||
#include <vector>
|
||||
|
||||
#include <sys/stat.h> // for stat, S_ISDIR, S_ISREG
|
||||
|
||||
@ -13,7 +14,10 @@ namespace FileUtils
|
||||
{
|
||||
bool isDirectory(const std::string &path)
|
||||
{
|
||||
struct stat pathStat{};
|
||||
struct stat pathStat
|
||||
{
|
||||
};
|
||||
|
||||
if (stat(path.c_str(), &pathStat) != 0)
|
||||
{
|
||||
return false; // Could not access path
|
||||
@ -23,7 +27,10 @@ bool isDirectory(const std::string &path)
|
||||
|
||||
bool isFile(const std::string &path)
|
||||
{
|
||||
struct stat pathStat{};
|
||||
struct stat pathStat
|
||||
{
|
||||
};
|
||||
|
||||
if (stat(path.c_str(), &pathStat) != 0)
|
||||
{
|
||||
return false; // Could not access path
|
||||
@ -33,7 +40,10 @@ bool isFile(const std::string &path)
|
||||
|
||||
bool isValidPath(const std::string &path)
|
||||
{
|
||||
struct stat pathStat{};
|
||||
struct stat pathStat
|
||||
{
|
||||
};
|
||||
|
||||
return stat(path.c_str(), &pathStat) == 0;
|
||||
}
|
||||
|
||||
@ -115,4 +125,15 @@ std::string readFileAsString(const std::string &filepath)
|
||||
return {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::directory_entry> listDirectory(const std::string &dirpath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> entries = {};
|
||||
|
||||
for (const auto &entry : std::filesystem::directory_iterator(dirpath))
|
||||
{
|
||||
entries.emplace_back(entry);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
} // namespace FileUtils
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -14,4 +15,6 @@ std::string joinPath(const std::string &base, const std::string &addition);
|
||||
|
||||
std::vector<char> readBinaryFile(const std::string &filepath);
|
||||
std::string readFileAsString(const std::string &filepath);
|
||||
|
||||
std::vector<std::filesystem::directory_entry> listDirectory(const std::string &dirpath);
|
||||
} // namespace FileUtils
|
||||
Loading…
Reference in New Issue
Block a user