drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.06k stars 1.06k forks source link

Upload path comment does not stand #1964

Open Mis1eader-dev opened 4 months ago

Mis1eader-dev commented 4 months ago

Describe the bug In config.example.json it is stated:

upload_path: The path to save the uploaded file. "uploads" by default. If the path isn't prefixed with /, ./ or ../, it is relative path of document_root path

However, if I setup a project where I put a relative path to the running context for both document_root and upload_path:

"document_root": "./frontend",
"upload_path": "./confidential/database/uploads"

Drogon creates the uploads folder within the document_root path, ./frontend/confidential/database/uploads. This contradicts with the statement written as a comment in the example.

Expected behavior Pages should be served in ./frontend, and uploads should be uploaded to ./confidential/database/uploads.

Desktop (please complete the following information):

Additional context Currently it's possible to achieve the desired result with either /home/user/project/confidential/database/uploads or ../confidential/database/uploads.

Mis1eader-dev commented 4 months ago

To add to this, it seems we can't even alias the uploads folder with "locations" configuration.

{
    "app": {
        "document_root": "./frontend",
        "upload_path": "../confidential/database/uploads",
        "locations": [
            {
                "uri_prefix": "/uploads",
                "is_case_sensitive": false,
                "is_recursive": true,
                "alias": "../confidential/database/uploads",
                "filters": [ "UploadsFilter" ]
            }
        ]
    }
}

We have to create an HTTP controller and do a file response with the uploads path.

Mis1eader-dev commented 4 months ago

This is the way I had to do it:

#include "drogon/HttpAppFramework.h"
#include "drogon/HttpResponse.h"
#include "drogon/utils/FunctionTraits.h"
#include <functional>
#include <iostream>
#include <string>
#include <string_view>

static constexpr std::string_view
    uploadsPrefix = "/uploads/",
    uploadsPathRegex = "^/uploads/.+";

static_assert(uploadsPathRegex.substr(1).starts_with(uploadsPrefix), "Uploads path and regex must match");

static const std::string uploadsRegex = std::string(uploadsPathRegex);

int main()
{
    drogon::app()
        .registerHandlerViaRegex(::uploadsRegex,
        [](
            const drogon::HttpRequestPtr &req,
            std::function<void(const drogon::HttpResponsePtr &)> &&callback)
        {
            std::string_view uploads = drogon::app().getUploadPath();

            std::string_view path = req->path();
            path.remove_prefix(::uploadsPrefix.size());

            std::string file;
            file.reserve(uploads.size() + 1 + path.size());

            file = uploads;
            file += '/';
            file += path;

            callback(drogon::HttpResponse::newFileResponse(file));
        })
        .loadConfigFile("./config.json")
        .addListener("127.0.0.1", 8080)
        .run();
}

config.json:

{
    "app": {
        "document_root": "./frontend",
        "upload_path": "../confidential/database/uploads"
    }
}