Skip to content

Commit

Permalink
Implement attachment upload
Browse files Browse the repository at this point in the history
  • Loading branch information
MetroWind committed Sep 25, 2024
1 parent b39032e commit a36ef2d
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 4 deletions.
61 changes: 60 additions & 1 deletion src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
_ASSIGN_OR_RESPOND_ERROR(_CONCAT_NAMES(assign_or_return_tmp, __COUNTER__), \
var, val, res)

namespace fs = std::filesystem;

namespace {
std::unordered_map<std::string, std::string> parseCookies(std::string_view value)
{
Expand Down Expand Up @@ -587,7 +589,6 @@ void App::handleAttachment(const httplib::Request& req, httplib::Response& res)
return;
}

namespace fs = std::filesystem;
auto path = fs::path(config.attachment_dir) / attachment_manager.path(*att);
if(!fs::exists(path))
{
Expand All @@ -602,6 +603,59 @@ void App::handleAttachment(const httplib::Request& req, httplib::Response& res)
res.set_content(data, att->content_type);
}

void App::handleAttachmentUpload(const httplib::Request& req,
httplib::Response& res) const
{
auto session = prepareSession(req, res);
if(!session) return;

if(!req.has_file("file"))
{
res.status = 400;
res.set_content("File expected", "text/plain");
return;
}
const auto& file = req.get_file_value("file");
Attachment att = attachment_manager.attachmentFromBytes(
file.content, file.filename, file.content_type);
fs::path path = fs::path(config.attachment_dir) /
attachment_manager.path(att);
spdlog::debug("Adding attachment to {}...", path.string());
fs::path dir = path.parent_path();
if(!fs::exists(dir))
{
if(!fs::create_directory(dir))
{
res.status = 500;
res.set_content("Failed to create directory for attachment",
"text/plain");
return;
}
}
std::ofstream att_file(path);
try
{
att_file.write(file.content.data(), file.content.size());
}
catch(const std::ios_base::failure& e)
{
res.status = 500;
res.set_content(std::format("Failed to write attachment: {}", e.what()),
"text/plain");
return;
}
att_file.close();
E<void> error_maybe = data->addAttachment(std::move(att));
if(!error_maybe)
{
fs::remove(path);
res.status = 500;
res.set_content(errorMsg(error_maybe.error()), "text/plain");
return;
}
res.set_redirect(urlFor("attachments"));
}

nlohmann::json App::postToJson(const Post& p) const
{
nlohmann::json result;
Expand Down Expand Up @@ -772,6 +826,11 @@ void App::start()
{
handleAttachment(req, res);
});
server.Post(getPath("upload-attachment"),
[&](const httplib::Request& req, httplib::Response& res)
{
handleAttachmentUpload(req, res);
});

spdlog::info("Listening at http://{}:{}/...", config.listen_address,
config.listen_port);
Expand Down
2 changes: 2 additions & 0 deletions src/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class App
httplib::Response& res) const;
void handleAttachments(const httplib::Request& req, httplib::Response& res);
void handleAttachment(const httplib::Request& req, httplib::Response& res);
void handleAttachmentUpload(const httplib::Request& req,
httplib::Response& res) const;

void start();
void stop();
Expand Down
12 changes: 10 additions & 2 deletions src/attachment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,19 @@ std::string probeMimeType(std::string_view bytes)
} // namespace

Attachment AttachmentManager::attachmentFromBytes(
const std::string& bytes, std::string_view filename) const
const std::string& bytes, std::string_view filename,
std::string_view content_type) const
{
Attachment att;
att.hash = hasher.hashToHexStr(bytes);
att.content_type = probeMimeType(bytes);
if(!content_type.empty())
{
att.content_type = content_type;
}
else
{
att.content_type = probeMimeType(bytes);
}
att.original_name = filename;
return att;
}
Expand Down
3 changes: 2 additions & 1 deletion src/attachment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class AttachmentManager

// Get an attachment object out of some bytes.
Attachment attachmentFromBytes(const std::string& bytes,
std::string_view filename) const;
std::string_view filename,
std::string_view content_type="") const;
// Get the path in the local file system of the attachment,
// relative to the attachment dir set in the config.
std::string path(const Attachment& att) const;
Expand Down
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
#include "config.hpp"
#include "data.hpp"
#include "http_client.hpp"
#include "spdlog/common.h"
#include "spdlog/spdlog.h"
#include "utils.hpp"
#include "url.hpp"

int main(int argc, char** argv)
{
spdlog::set_level(spdlog::level::debug);
cxxopts::Options cmd_options("NS Weekly", "Naively simple weekly snippet");
cmd_options.add_options()
("c,config", "Config file",
Expand Down Expand Up @@ -66,6 +68,7 @@ int main(int argc, char** argv)

App app(*conf, *std::move(auth), *std::move(data_source));
app.start();
app.wait();

return 0;
}
9 changes: 9 additions & 0 deletions templates/attachments.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
<body>
<div id="Body">
{% include "nav.html" %}

<form method="post" enctype="multipart/form-data" action="{{ url_for("upload-attachment") }}">
<div>
<label for="file">Upload an attachment</label>
<input type="file" id="FilePicker" name="file" />
<input type="submit" value="Upload" />
</div>
</form>

<table id="AttachmentTable">
<tr>
<th>Hash</th><th>Filename</th><th>Type</th><th>Upload time</th>
Expand Down

0 comments on commit a36ef2d

Please sign in to comment.