复现已有算法
This commit is contained in:
+89
@@ -0,0 +1,89 @@
|
||||
set(ctl_sources
|
||||
cmd.cc
|
||||
create.cc
|
||||
create_controller.cc
|
||||
create_filter.cc
|
||||
create_model.cc
|
||||
create_plugin.cc
|
||||
create_project.cc
|
||||
create_view.cc
|
||||
help.cc
|
||||
main.cc
|
||||
press.cc
|
||||
version.cc)
|
||||
add_executable(_drogon_ctl
|
||||
main.cc
|
||||
cmd.cc
|
||||
create.cc
|
||||
create_view.cc)
|
||||
target_link_libraries(_drogon_ctl ${PROJECT_NAME})
|
||||
if (WIN32 AND BUILD_SHARED_LIBS)
|
||||
set(DROGON_FILE $<TARGET_FILE:drogon>)
|
||||
if (USE_SUBMODULE)
|
||||
set(TRANTOR_FILE $<TARGET_FILE:trantor>)
|
||||
else()
|
||||
set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)
|
||||
endif()
|
||||
add_custom_command(TARGET _drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${DROGON_FILE}
|
||||
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
add_custom_command(TARGET _drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${TRANTOR_FILE}
|
||||
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
endif()
|
||||
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp)
|
||||
foreach(cspFile ${SCP_LIST})
|
||||
message(STATUS "cspFile:" ${cspFile})
|
||||
get_filename_component(classname ${cspFile} NAME_WE)
|
||||
message(STATUS "view classname:" ${classname})
|
||||
add_custom_command(OUTPUT ${classname}.h ${classname}.cc
|
||||
COMMAND ${DROGON_CTL}
|
||||
ARGS
|
||||
create
|
||||
view
|
||||
${cspFile}
|
||||
DEPENDS ${cspFile}
|
||||
VERBATIM)
|
||||
set(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc)
|
||||
endforeach()
|
||||
add_executable(drogon_ctl ${ctl_sources} ${TEMPL_SRC})
|
||||
target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})
|
||||
target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_dependencies(drogon_ctl _drogon_ctl)
|
||||
if(WIN32)
|
||||
target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi)
|
||||
endif(WIN32)
|
||||
if(APPLE)
|
||||
target_link_libraries(drogon_ctl PRIVATE resolv)
|
||||
endif()
|
||||
message(STATUS "bin:" ${INSTALL_BIN_DIR})
|
||||
install(TARGETS _drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})
|
||||
install(TARGETS drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})
|
||||
if(WIN32)
|
||||
set(CTL_FILE $<TARGET_FILE:drogon_ctl>)
|
||||
add_custom_command(TARGET drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${CTL_FILE}
|
||||
-DINSTALL_BIN_DIR=${INSTALL_BIN_DIR}
|
||||
-DRENAME_EXE=ON
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
else(WIN32)
|
||||
install(CODE "execute_process( \
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink \
|
||||
./drogon_ctl \
|
||||
./dg_ctl \
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dg_ctl"
|
||||
DESTINATION ${INSTALL_BIN_DIR})
|
||||
endif(WIN32)
|
||||
set(ctl_targets _drogon_ctl drogon_ctl)
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_EXTENSIONS OFF)
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
*
|
||||
* CommandHandler.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CommandHandler : public virtual drogon::DrObjectBase
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) = 0;
|
||||
|
||||
virtual bool isTopCommand()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual std::string script()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string detail()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual ~CommandHandler()
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
make_directory("${INSTALL_BIN_DIR}")
|
||||
get_filename_component(CTL_PATH ${CTL_FILE} DIRECTORY)
|
||||
file(GLOB DLL_FILES ${CTL_PATH}/*.dll)
|
||||
file(COPY ${DLL_FILES} DESTINATION ${INSTALL_BIN_DIR})
|
||||
file(COPY ${CTL_FILE} DESTINATION ${INSTALL_BIN_DIR})
|
||||
if (RENAME_EXE)
|
||||
file(RENAME ${INSTALL_BIN_DIR}/drogon_ctl.exe ${INSTALL_BIN_DIR}/dg_ctl.exe)
|
||||
endif()
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
*
|
||||
* cmd.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cmd.h"
|
||||
#include "CommandHandler.h"
|
||||
#include <drogon/DrClassMap.h>
|
||||
#include <memory>
|
||||
using namespace drogon;
|
||||
|
||||
void exeCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.empty())
|
||||
{
|
||||
std::cout << "incomplete command!use help command to get usage!"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
std::string command = parameters[0];
|
||||
|
||||
std::string handlerName = std::string("drogon_ctl::").append(command);
|
||||
|
||||
parameters.erase(parameters.begin());
|
||||
|
||||
// new command handler to do cmd
|
||||
auto obj = std::shared_ptr<DrObjectBase>(
|
||||
drogon::DrClassMap::newObject(handlerName));
|
||||
if (obj)
|
||||
{
|
||||
auto ctl = std::dynamic_pointer_cast<CommandHandler>(obj);
|
||||
if (ctl)
|
||||
{
|
||||
ctl->handleCommand(parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "command not found!use help command to get usage!"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "command error!use help command to get usage!"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
*
|
||||
* cmd.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#define ARGS_ERROR_STR "args error!use help command to get usage!"
|
||||
void exeCommand(std::vector<std::string> ¶meters);
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
*
|
||||
* @file create.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/DrClassMap.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
using namespace drogon_ctl;
|
||||
|
||||
std::string create::detail()
|
||||
{
|
||||
return "Use create command to create some source files of drogon webapp\n\n"
|
||||
"Usage:drogon_ctl create <view|controller|filter|project|model> "
|
||||
"[-options] <object name>\n\n"
|
||||
"drogon_ctl create view <csp file name> [-o <output path>] [-n "
|
||||
"<namespace>] [--path-to-namespace] //create HttpView source files "
|
||||
"from csp files, namespace is prefixed of path-to-namespace\n\n"
|
||||
"drogon_ctl create controller [-s] <[namespace::]class_name> //"
|
||||
"create HttpSimpleController source files\n\n"
|
||||
"drogon_ctl create controller -h <[namespace::]class_name> //"
|
||||
"create HttpController source files\n\n"
|
||||
"drogon_ctl create controller -w <[namespace::]class_name> //"
|
||||
"create WebSocketController source files\n\n"
|
||||
"drogon_ctl create controller -r <[namespace::]class_name> "
|
||||
"[--resource=...]//"
|
||||
"create restful controller source files\n\n"
|
||||
"drogon_ctl create filter <[namespace::]class_name> //"
|
||||
"create a filter named class_name\n\n"
|
||||
"drogon_ctl create plugin <[namespace::]class_name> //"
|
||||
"create a plugin named class_name\n\n"
|
||||
"drogon_ctl create project <project_name> //"
|
||||
"create a project named project_name\n\n"
|
||||
"drogon_ctl create model <model_path> [-o <output path>] "
|
||||
"[--table=<table_name>] [-f]//"
|
||||
"create model classes in model_path\n";
|
||||
}
|
||||
|
||||
void create::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
// std::cout<<"create!"<<std::endl;
|
||||
auto createObjName = parameters[0];
|
||||
createObjName = std::string("create_") + createObjName;
|
||||
parameters[0] = createObjName;
|
||||
exeCommand(parameters);
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
*
|
||||
* create.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create : public DrObject<create>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create some source files(Use 'drogon_ctl help create' for more "
|
||||
"information)";
|
||||
}
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string detail() override;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
@@ -0,0 +1,471 @@
|
||||
/**
|
||||
*
|
||||
* create_controller.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create_controller.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
void create_controller::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
// std::cout<<"create!"<<std::endl;
|
||||
ControllerType type = Simple;
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)
|
||||
{
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
if (*iter == "-s" || (*iter == "--simple"))
|
||||
{
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else if (*iter == "-a" || *iter == "-h" || *iter == "--http")
|
||||
{
|
||||
type = Http;
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else if (*iter == "-w" || *iter == "--websocket")
|
||||
{
|
||||
type = WebSocket;
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else if (*iter == "-r" || *iter == "--restful")
|
||||
{
|
||||
type = Restful;
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << ARGS_ERROR_STR << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type != Restful)
|
||||
createController(parameters, type);
|
||||
else
|
||||
{
|
||||
std::string resource;
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)
|
||||
{
|
||||
if ((*iter).find("--resource=") == 0)
|
||||
{
|
||||
resource = (*iter).substr(strlen("--resource="));
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
std::cerr << "Error parameter for '" << (*iter) << "'"
|
||||
<< std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (parameters.size() > 1)
|
||||
{
|
||||
std::cerr << "Too many parameters" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
auto className = parameters[0];
|
||||
createARestfulController(className, resource);
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newSimpleControllerHeaderFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/HttpSimpleController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << "class " << class_name << " : public drogon::HttpSimpleController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " void asyncHandleHttpRequest(const HttpRequestPtr& "
|
||||
"req, std::function<void (const HttpResponsePtr &)> &&callback) "
|
||||
"override;\n";
|
||||
|
||||
file << " PATH_LIST_BEGIN\n";
|
||||
file << " // list path definitions here;\n";
|
||||
file << " "
|
||||
"// PATH_ADD(\"/"
|
||||
"path\", \"filter1\", \"filter2\", HttpMethod1, HttpMethod2...);\n";
|
||||
file << " PATH_LIST_END\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newSimpleControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
file << "void " << class_name
|
||||
<< "::asyncHandleHttpRequest(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback)\n";
|
||||
file << "{\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
void create_controller::newWebsockControllerHeaderFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/WebSocketController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << "class " << class_name << " : public drogon::WebSocketController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " void handleNewMessage(const WebSocketConnectionPtr&,\n";
|
||||
file << " std::string &&,\n";
|
||||
file << " const WebSocketMessageType &) "
|
||||
"override;\n";
|
||||
file << " void handleNewConnection(const HttpRequestPtr &,\n";
|
||||
file << " const "
|
||||
"WebSocketConnectionPtr&) override;\n";
|
||||
file << " void handleConnectionClosed(const "
|
||||
"WebSocketConnectionPtr&) override;\n";
|
||||
file << " WS_PATH_LIST_BEGIN\n";
|
||||
file << " // list path definitions here;\n";
|
||||
file << " // WS_PATH_ADD(\"/path\", \"filter1\", \"filter2\", ...);\n";
|
||||
file << " WS_PATH_LIST_END\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newWebsockControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
file << "void " << class_name
|
||||
<< "::handleNewMessage(const WebSocketConnectionPtr& wsConnPtr, "
|
||||
"std::string &&message, const WebSocketMessageType &type)\n";
|
||||
file << "{\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
file << "\n";
|
||||
file << "void " << class_name
|
||||
<< "::handleNewConnection(const HttpRequestPtr &req, const "
|
||||
"WebSocketConnectionPtr& wsConnPtr)\n";
|
||||
file << "{\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
file << "\n";
|
||||
file << "void " << class_name
|
||||
<< "::handleConnectionClosed(const WebSocketConnectionPtr& "
|
||||
"wsConnPtr)\n";
|
||||
file << "{\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
void create_controller::newHttpControllerHeaderFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/HttpController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << "class " << class_name << " : public drogon::HttpController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " METHOD_LIST_BEGIN\n";
|
||||
file << " // use METHOD_ADD to add your custom processing function "
|
||||
"here;\n";
|
||||
file << " // METHOD_ADD(" << class_name
|
||||
<< "::get, \"/{2}/{1}\", Get);"
|
||||
" // path is "
|
||||
<< namepace_path << class_name << "/{arg2}/{arg1}\n";
|
||||
file << " // METHOD_ADD(" << class_name
|
||||
<< "::your_method_name, \"/{1}/{2}/list\", Get);"
|
||||
" // path is "
|
||||
<< namepace_path << class_name << "/{arg1}/{arg2}/list\n";
|
||||
file << " // ADD_METHOD_TO(" << class_name
|
||||
<< "::your_method_name, \"/absolute/path/{1}/{2}/list\", Get);"
|
||||
" // path is /absolute/path/{arg1}/{arg2}/list\n";
|
||||
file << "\n";
|
||||
file << " METHOD_LIST_END\n";
|
||||
file << " // your declaration of processing function maybe like this:\n";
|
||||
file << " // void get(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback, int "
|
||||
"p1, std::string p2);\n";
|
||||
file << " // void your_method_name(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback, double "
|
||||
"p1, int p2) const;\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newHttpControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
|
||||
file << "// Add definition of your processing function here\n";
|
||||
}
|
||||
|
||||
void create_controller::createController(std::vector<std::string> &httpClasses,
|
||||
ControllerType type)
|
||||
{
|
||||
for (auto iter = httpClasses.begin(); iter != httpClasses.end(); ++iter)
|
||||
{
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
std::cout << ARGS_ERROR_STR << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto const &className : httpClasses)
|
||||
{
|
||||
createController(className, type);
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::createController(const std::string &className,
|
||||
ControllerType type)
|
||||
{
|
||||
std::regex regex("::");
|
||||
std::string ctlName =
|
||||
std::regex_replace(className, regex, std::string("_"));
|
||||
|
||||
std::string headFileName = ctlName + ".h";
|
||||
std::string sourceFilename = ctlName + ".cc";
|
||||
{
|
||||
std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);
|
||||
std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in);
|
||||
|
||||
if (iHeadFile || iSourceFile)
|
||||
{
|
||||
std::cout << "The file you want to create already exists, "
|
||||
"overwrite it(y/n)?"
|
||||
<< std::endl;
|
||||
auto in = getchar();
|
||||
(void)getchar(); // get the return key
|
||||
if (in != 'Y' && in != 'y')
|
||||
{
|
||||
std::cout << "Abort!" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
if (type == Http)
|
||||
{
|
||||
std::cout << "Create a http controller: " << className << std::endl;
|
||||
newHttpControllerHeaderFile(oHeadFile, className);
|
||||
newHttpControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
else if (type == Simple)
|
||||
{
|
||||
std::cout << "Create a http simple controller: " << className
|
||||
<< std::endl;
|
||||
newSimpleControllerHeaderFile(oHeadFile, className);
|
||||
newSimpleControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
else if (type == WebSocket)
|
||||
{
|
||||
std::cout << "Create a websocket controller: " << className
|
||||
<< std::endl;
|
||||
newWebsockControllerHeaderFile(oHeadFile, className);
|
||||
newWebsockControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::createARestfulController(const std::string &className,
|
||||
const std::string &resource)
|
||||
{
|
||||
std::regex regex("::");
|
||||
std::string ctlName =
|
||||
std::regex_replace(className, regex, std::string("_"));
|
||||
|
||||
std::string headFileName = ctlName + ".h";
|
||||
std::string sourceFilename = ctlName + ".cc";
|
||||
{
|
||||
std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);
|
||||
std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in);
|
||||
|
||||
if (iHeadFile || iSourceFile)
|
||||
{
|
||||
std::cout << "The file you want to create already exists, "
|
||||
"overwrite it(y/n)?"
|
||||
<< std::endl;
|
||||
auto in = getchar();
|
||||
(void)getchar(); // get the return key
|
||||
if (in != 'Y' && in != 'y')
|
||||
{
|
||||
std::cout << "Abort!" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
auto v = utils::splitString(className, "::");
|
||||
drogon::DrTemplateData data;
|
||||
data.insert("className", v[v.size() - 1]);
|
||||
v.pop_back();
|
||||
data.insert("namespaceVector", v);
|
||||
data.insert("resource", resource);
|
||||
data.insert("fileName", ctlName);
|
||||
if (resource.empty())
|
||||
{
|
||||
data.insert("ctlCommand",
|
||||
std::string("drogon_ctl create controller -r ") +
|
||||
className);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("ctlCommand",
|
||||
std::string("drogon_ctl create controller -r ") +
|
||||
className + " --resource=" + resource);
|
||||
}
|
||||
try
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("restful_controller_h.csp");
|
||||
oHeadFile << templ->genText(data);
|
||||
templ = DrTemplateBase::newTemplate("restful_controller_cc.csp");
|
||||
oSourceFile << templ->genText(data);
|
||||
}
|
||||
catch (const std::exception &err)
|
||||
{
|
||||
std::cerr << err.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "Create a http restful API controller: " << className
|
||||
<< std::endl;
|
||||
std::cout << "File name: " << ctlName << ".h and " << ctlName << ".cc"
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
*
|
||||
* create_controller.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_controller : public DrObject<create_controller>,
|
||||
public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create controller files";
|
||||
}
|
||||
|
||||
protected:
|
||||
enum ControllerType
|
||||
{
|
||||
Simple = 0,
|
||||
Http,
|
||||
WebSocket,
|
||||
Restful
|
||||
};
|
||||
|
||||
void createController(std::vector<std::string> &httpClasses,
|
||||
ControllerType type);
|
||||
void createController(const std::string &className, ControllerType type);
|
||||
void createARestfulController(const std::string &className,
|
||||
const std::string &resource);
|
||||
|
||||
void newSimpleControllerHeaderFile(std::ofstream &file,
|
||||
const std::string &className);
|
||||
void newSimpleControllerSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename);
|
||||
|
||||
void newWebsockControllerHeaderFile(std::ofstream &file,
|
||||
const std::string &className);
|
||||
void newWebsockControllerSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename);
|
||||
|
||||
void newHttpControllerHeaderFile(std::ofstream &file,
|
||||
const std::string &className);
|
||||
void newHttpControllerSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename);
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
*
|
||||
* create_filter.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create_filter.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
static void createFilterHeaderFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &fileName)
|
||||
{
|
||||
auto templ = drogon::DrTemplateBase::newTemplate("filter_h");
|
||||
HttpViewData data;
|
||||
if (className.find("::") != std::string::npos)
|
||||
{
|
||||
auto namespaceVector = utils::splitString(className, "::");
|
||||
data.insert("className", namespaceVector.back());
|
||||
namespaceVector.pop_back();
|
||||
data.insert("namespaceVector", namespaceVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("className", className);
|
||||
}
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
static void createFilterSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &fileName)
|
||||
{
|
||||
auto templ = drogon::DrTemplateBase::newTemplate("filter_cc");
|
||||
HttpViewData data;
|
||||
if (className.find("::") != std::string::npos)
|
||||
{
|
||||
auto pos = className.rfind("::");
|
||||
data.insert("namespaceString", className.substr(0, pos));
|
||||
data.insert("className", className.substr(pos + 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("className", className);
|
||||
}
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_filter::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() < 1)
|
||||
{
|
||||
std::cout << "Invalid parameters!" << std::endl;
|
||||
}
|
||||
for (auto className : parameters)
|
||||
{
|
||||
std::regex regex("::");
|
||||
std::string fileName =
|
||||
std::regex_replace(className, regex, std::string("_"));
|
||||
|
||||
std::string headFileName = fileName + ".h";
|
||||
std::string sourceFilename = fileName + ".cc";
|
||||
{
|
||||
std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);
|
||||
std::ifstream iSourceFile(sourceFilename.c_str(),
|
||||
std::ifstream::in);
|
||||
|
||||
if (iHeadFile || iSourceFile)
|
||||
{
|
||||
std::cout << "The file you want to create already exists, "
|
||||
"overwrite it(y/n)?"
|
||||
<< std::endl;
|
||||
auto in = getchar();
|
||||
(void)getchar(); // get the return key
|
||||
if (in != 'Y' && in != 'y')
|
||||
{
|
||||
std::cout << "Abort!" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::cout << "create a http filter:" << className << std::endl;
|
||||
createFilterHeaderFile(oHeadFile, className, fileName);
|
||||
createFilterSourceFile(oSourceFile, className, fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
*
|
||||
* create_filter.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_filter : public DrObject<create_filter>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create filter class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string outputPath_{"."};
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+1416
File diff suppressed because it is too large
Load Diff
+434
@@ -0,0 +1,434 @@
|
||||
/**
|
||||
*
|
||||
* create_model.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/config.h>
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <json/json.h>
|
||||
#include <drogon/orm/DbClient.h>
|
||||
using namespace drogon::orm;
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
struct ColumnInfo
|
||||
{
|
||||
std::string colName_;
|
||||
std::string colValName_;
|
||||
std::string colTypeName_;
|
||||
std::string colType_;
|
||||
std::string colDatabaseType_;
|
||||
std::string dbType_;
|
||||
ssize_t colLength_{0};
|
||||
size_t index_{0};
|
||||
bool isAutoVal_{false};
|
||||
bool isPrimaryKey_{false};
|
||||
bool notNull_{false};
|
||||
bool hasDefaultVal_{false};
|
||||
};
|
||||
|
||||
inline std::string nameTransform(const std::string &origName, bool isType)
|
||||
{
|
||||
auto str = origName;
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {
|
||||
return tolower(c);
|
||||
});
|
||||
std::string::size_type startPos = 0;
|
||||
std::string::size_type pos;
|
||||
std::string ret;
|
||||
do
|
||||
{
|
||||
pos = str.find("_", startPos);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = str.find(".", startPos);
|
||||
}
|
||||
if (pos != std::string::npos)
|
||||
ret += str.substr(startPos, pos - startPos);
|
||||
else
|
||||
{
|
||||
ret += str.substr(startPos);
|
||||
break;
|
||||
}
|
||||
while (str[pos] == '_' || str[pos] == '.')
|
||||
++pos;
|
||||
if (str[pos] >= 'a' && str[pos] <= 'z')
|
||||
str[pos] += ('A' - 'a');
|
||||
startPos = pos;
|
||||
} while (1);
|
||||
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
|
||||
ret[0] += ('A' - 'a');
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string escapeIdentifier(const std::string &identifier,
|
||||
const std::string &rdbms);
|
||||
|
||||
class PivotTable
|
||||
{
|
||||
public:
|
||||
PivotTable() = default;
|
||||
|
||||
PivotTable(const Json::Value &json)
|
||||
: tableName_(json.get("table_name", "").asString())
|
||||
{
|
||||
if (tableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("table_name can't be empty");
|
||||
}
|
||||
originalKey_ = json.get("original_key", "").asString();
|
||||
if (originalKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_key can't be empty");
|
||||
}
|
||||
targetKey_ = json.get("target_key", "").asString();
|
||||
if (targetKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_key can't be empty");
|
||||
}
|
||||
}
|
||||
|
||||
PivotTable reverse() const
|
||||
{
|
||||
PivotTable pivot;
|
||||
pivot.tableName_ = tableName_;
|
||||
pivot.originalKey_ = targetKey_;
|
||||
pivot.targetKey_ = originalKey_;
|
||||
return pivot;
|
||||
}
|
||||
|
||||
const std::string &tableName() const
|
||||
{
|
||||
return tableName_;
|
||||
}
|
||||
|
||||
const std::string &originalKey() const
|
||||
{
|
||||
return originalKey_;
|
||||
}
|
||||
|
||||
const std::string &targetKey() const
|
||||
{
|
||||
return targetKey_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string tableName_;
|
||||
std::string originalKey_;
|
||||
std::string targetKey_;
|
||||
};
|
||||
|
||||
class ConvertMethod
|
||||
{
|
||||
public:
|
||||
ConvertMethod(const Json::Value &convert)
|
||||
{
|
||||
tableName_ = convert.get("table", "*").asString();
|
||||
colName_ = convert.get("column", "*").asString();
|
||||
|
||||
auto method = convert["method"];
|
||||
if (method.isNull())
|
||||
{
|
||||
throw std::runtime_error("method - object is missing.");
|
||||
} // endif
|
||||
if (!method.isObject())
|
||||
{
|
||||
throw std::runtime_error("method is not an object.");
|
||||
} // endif
|
||||
methodBeforeDbWrite_ = method.get("before_db_write", "").asString();
|
||||
methodAfterDbRead_ = method.get("after_db_read", "").asString();
|
||||
|
||||
auto includeFiles = convert["includes"];
|
||||
if (includeFiles.isNull())
|
||||
{
|
||||
return;
|
||||
} // endif
|
||||
if (!includeFiles.isArray())
|
||||
{
|
||||
throw std::runtime_error("includes must be an array");
|
||||
} // endif
|
||||
for (auto &i : includeFiles)
|
||||
{
|
||||
includeFiles_.push_back(i.asString());
|
||||
} // for
|
||||
}
|
||||
|
||||
ConvertMethod() = default;
|
||||
|
||||
bool shouldConvert(const std::string &tableName,
|
||||
const std::string &colName) const;
|
||||
|
||||
const std::string &tableName() const
|
||||
{
|
||||
return tableName_;
|
||||
}
|
||||
|
||||
const std::string &colName() const
|
||||
{
|
||||
return colName_;
|
||||
}
|
||||
|
||||
const std::string &methodBeforeDbWrite() const
|
||||
{
|
||||
return methodBeforeDbWrite_;
|
||||
}
|
||||
|
||||
const std::string &methodAfterDbRead() const
|
||||
{
|
||||
return methodAfterDbRead_;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &includeFiles() const
|
||||
{
|
||||
return includeFiles_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string tableName_{"*"};
|
||||
std::string colName_{"*"};
|
||||
std::string methodBeforeDbWrite_;
|
||||
std::string methodAfterDbRead_;
|
||||
std::vector<std::string> includeFiles_;
|
||||
};
|
||||
|
||||
class Relationship
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
HasOne,
|
||||
HasMany,
|
||||
ManyToMany
|
||||
};
|
||||
|
||||
Relationship(const Json::Value &relationship)
|
||||
{
|
||||
auto type = relationship.get("type", "has one").asString();
|
||||
if (type == "has one")
|
||||
{
|
||||
type_ = Relationship::Type::HasOne;
|
||||
}
|
||||
else if (type == "has many")
|
||||
{
|
||||
type_ = Relationship::Type::HasMany;
|
||||
}
|
||||
else if (type == "many to many")
|
||||
{
|
||||
type_ = Relationship::Type::ManyToMany;
|
||||
}
|
||||
else
|
||||
{
|
||||
char message[128];
|
||||
snprintf(message,
|
||||
sizeof(message),
|
||||
"Invalid relationship type: %s",
|
||||
type.data());
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
originalTableName_ =
|
||||
relationship.get("original_table_name", "").asString();
|
||||
if (originalTableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_table_name can't be empty");
|
||||
}
|
||||
originalKey_ = relationship.get("original_key", "").asString();
|
||||
if (originalKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_key can't be empty");
|
||||
}
|
||||
originalTableAlias_ =
|
||||
relationship.get("original_table_alias", "").asString();
|
||||
targetTableName_ = relationship.get("target_table_name", "").asString();
|
||||
if (targetTableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_table_name can't be empty");
|
||||
}
|
||||
targetKey_ = relationship.get("target_key", "").asString();
|
||||
if (targetKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_key can't be empty");
|
||||
}
|
||||
targetTableAlias_ =
|
||||
relationship.get("target_table_alias", "").asString();
|
||||
enableReverse_ = relationship.get("enable_reverse", false).asBool();
|
||||
if (type_ == Type::ManyToMany)
|
||||
{
|
||||
auto &pivot = relationship["pivot_table"];
|
||||
if (pivot.isNull())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"ManyToMany relationship needs a pivot table");
|
||||
}
|
||||
pivotTable_ = PivotTable(pivot);
|
||||
}
|
||||
}
|
||||
|
||||
Relationship() = default;
|
||||
|
||||
Relationship reverse() const
|
||||
{
|
||||
Relationship r;
|
||||
if (type_ == Type::HasMany)
|
||||
{
|
||||
r.type_ = Type::HasOne;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.type_ = type_;
|
||||
}
|
||||
r.originalTableName_ = targetTableName_;
|
||||
r.originalTableAlias_ = targetTableAlias_;
|
||||
r.originalKey_ = targetKey_;
|
||||
r.targetTableName_ = originalTableName_;
|
||||
r.targetTableAlias_ = originalTableAlias_;
|
||||
r.targetKey_ = originalKey_;
|
||||
r.enableReverse_ = enableReverse_;
|
||||
r.pivotTable_ = pivotTable_.reverse();
|
||||
return r;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
bool enableReverse() const
|
||||
{
|
||||
return enableReverse_;
|
||||
}
|
||||
|
||||
const std::string &originalTableName() const
|
||||
{
|
||||
return originalTableName_;
|
||||
}
|
||||
|
||||
const std::string &originalTableAlias() const
|
||||
{
|
||||
return originalTableAlias_;
|
||||
}
|
||||
|
||||
const std::string &originalKey() const
|
||||
{
|
||||
return originalKey_;
|
||||
}
|
||||
|
||||
const std::string &targetTableName() const
|
||||
{
|
||||
return targetTableName_;
|
||||
}
|
||||
|
||||
const std::string &targetTableAlias() const
|
||||
{
|
||||
return targetTableAlias_;
|
||||
}
|
||||
|
||||
const std::string &targetKey() const
|
||||
{
|
||||
return targetKey_;
|
||||
}
|
||||
|
||||
const PivotTable &pivotTable() const
|
||||
{
|
||||
return pivotTable_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_{Type::HasOne};
|
||||
std::string originalTableName_;
|
||||
std::string originalTableAlias_;
|
||||
std::string targetTableName_;
|
||||
std::string targetTableAlias_;
|
||||
std::string originalKey_;
|
||||
std::string targetKey_;
|
||||
bool enableReverse_{false};
|
||||
PivotTable pivotTable_;
|
||||
};
|
||||
|
||||
class create_model : public DrObject<create_model>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create Model classes files";
|
||||
}
|
||||
|
||||
protected:
|
||||
void createModel(const std::string &path,
|
||||
const std::string &singleModelName);
|
||||
void createModel(const std::string &path,
|
||||
const Json::Value &config,
|
||||
const std::string &singleModelName);
|
||||
#if USE_POSTGRESQL
|
||||
void createModelClassFromPG(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const std::string &schema,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethods);
|
||||
|
||||
void createModelFromPG(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &schema,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
|
||||
#endif
|
||||
#if USE_MYSQL
|
||||
void createModelClassFromMysql(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethods);
|
||||
void createModelFromMysql(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
|
||||
#endif
|
||||
#if USE_SQLITE3
|
||||
void createModelClassFromSqlite3(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethod);
|
||||
void createModelFromSqlite3(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethod);
|
||||
#endif
|
||||
void createRestfulAPIController(const DrTemplateData &tableInfo,
|
||||
const Json::Value &restfulApiConfig);
|
||||
std::string dbname_;
|
||||
bool forceOverwrite_{false};
|
||||
std::string outputPath_;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
*
|
||||
* create_plugin.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create_plugin.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
static void createPluginHeaderFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &fileName)
|
||||
{
|
||||
auto templ = drogon::DrTemplateBase::newTemplate("plugin_h");
|
||||
HttpViewData data;
|
||||
if (className.find("::") != std::string::npos)
|
||||
{
|
||||
auto namespaceVector = utils::splitString(className, "::");
|
||||
data.insert("className", namespaceVector.back());
|
||||
namespaceVector.pop_back();
|
||||
data.insert("namespaceVector", namespaceVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("className", className);
|
||||
}
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
static void createPluginSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &fileName)
|
||||
{
|
||||
auto templ = drogon::DrTemplateBase::newTemplate("plugin_cc");
|
||||
HttpViewData data;
|
||||
if (className.find("::") != std::string::npos)
|
||||
{
|
||||
auto pos = className.rfind("::");
|
||||
data.insert("namespaceString", className.substr(0, pos));
|
||||
data.insert("className", className.substr(pos + 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("className", className);
|
||||
}
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_plugin::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() < 1)
|
||||
{
|
||||
std::cout << "Invalid parameters!" << std::endl;
|
||||
}
|
||||
for (auto className : parameters)
|
||||
{
|
||||
std::regex regex("::");
|
||||
std::string fileName =
|
||||
std::regex_replace(className, regex, std::string("_"));
|
||||
|
||||
std::string headFileName = fileName + ".h";
|
||||
std::string sourceFilename = fileName + ".cc";
|
||||
{
|
||||
std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);
|
||||
std::ifstream iSourceFile(sourceFilename.c_str(),
|
||||
std::ifstream::in);
|
||||
|
||||
if (iHeadFile || iSourceFile)
|
||||
{
|
||||
std::cout << "The file you want to create already exists, "
|
||||
"overwrite it(y/n)?"
|
||||
<< std::endl;
|
||||
auto in = getchar();
|
||||
(void)getchar(); // get the return key
|
||||
if (in != 'Y' && in != 'y')
|
||||
{
|
||||
std::cout << "Abort!" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::cout << "create a plugin:" << className << std::endl;
|
||||
createPluginHeaderFile(oHeadFile, className, fileName);
|
||||
createPluginSourceFile(oSourceFile, className, fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
*
|
||||
* create_plugin.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_plugin : public DrObject<create_plugin>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create plugin class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string outputPath_{"."};
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
*
|
||||
* create_project.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create_project.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
void create_project::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() < 1)
|
||||
{
|
||||
std::cout << "please input project name" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
auto pName = parameters[0];
|
||||
createProject(pName);
|
||||
}
|
||||
|
||||
static void newCmakeFile(std::ofstream &cmakeFile,
|
||||
const std::string &projectName)
|
||||
{
|
||||
HttpViewData data;
|
||||
data.insert("ProjectName", projectName);
|
||||
auto templ = DrTemplateBase::newTemplate("cmake.csp");
|
||||
cmakeFile << templ->genText(data);
|
||||
}
|
||||
|
||||
static void newMainFile(std::ofstream &mainFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("demoMain");
|
||||
mainFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newGitIgFile(std::ofstream &gitFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("gitignore.csp");
|
||||
gitFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newConfigJsonFile(std::ofstream &configJsonFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("config_json");
|
||||
configJsonFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newConfigYamlFile(std::ofstream &configYamlFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("config_yaml");
|
||||
configYamlFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newModelConfigFile(std::ofstream &configFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("model_json");
|
||||
configFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newTestMainFile(std::ofstream &mainFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("test_main");
|
||||
mainFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newTestCmakeFile(std::ofstream &testCmakeFile,
|
||||
const std::string &projectName)
|
||||
{
|
||||
HttpViewData data;
|
||||
data.insert("ProjectName", projectName);
|
||||
auto templ = DrTemplateBase::newTemplate("test_cmake");
|
||||
testCmakeFile << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_project::createProject(const std::string &projectName)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (_access(projectName.data(), 0) == 0)
|
||||
#else
|
||||
if (access(projectName.data(), 0) == 0)
|
||||
#endif
|
||||
{
|
||||
std::cerr
|
||||
<< "The directory already exists, please use another project name!"
|
||||
<< std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "create a project named " << projectName << std::endl;
|
||||
|
||||
drogon::utils::createPath(projectName);
|
||||
// 1.create CMakeLists.txt
|
||||
#ifdef _WIN32
|
||||
auto r = _chdir(projectName.data());
|
||||
#else
|
||||
auto r = chdir(projectName.data());
|
||||
#endif
|
||||
(void)(r);
|
||||
std::ofstream cmakeFile("CMakeLists.txt", std::ofstream::out);
|
||||
newCmakeFile(cmakeFile, projectName);
|
||||
std::ofstream mainFile("main.cc", std::ofstream::out);
|
||||
newMainFile(mainFile);
|
||||
drogon::utils::createPath("views");
|
||||
drogon::utils::createPath("controllers");
|
||||
drogon::utils::createPath("filters");
|
||||
drogon::utils::createPath("plugins");
|
||||
drogon::utils::createPath("build");
|
||||
drogon::utils::createPath("models");
|
||||
drogon::utils::createPath("test");
|
||||
|
||||
std::ofstream gitFile(".gitignore", std::ofstream::out);
|
||||
newGitIgFile(gitFile);
|
||||
std::ofstream configJsonFile("config.json", std::ofstream::out);
|
||||
newConfigJsonFile(configJsonFile);
|
||||
std::ofstream configYamlFile("config.yaml", std::ofstream::out);
|
||||
newConfigYamlFile(configYamlFile);
|
||||
std::ofstream modelConfigFile("models/model.json", std::ofstream::out);
|
||||
newModelConfigFile(modelConfigFile);
|
||||
std::ofstream testMainFile("test/test_main.cc", std::ofstream::out);
|
||||
newTestMainFile(testMainFile);
|
||||
std::ofstream testCmakeFile("test/CMakeLists.txt", std::ofstream::out);
|
||||
newTestCmakeFile(testCmakeFile, projectName);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
*
|
||||
* create_project.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_project : public DrObject<create_project>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create a project";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string outputPath_{"."};
|
||||
void createProject(const std::string &projectName);
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+554
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
*
|
||||
* @file create_view.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "create_view.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
|
||||
static const std::string cxx_include = "<%inc";
|
||||
static const std::string cxx_end = "%>";
|
||||
static const std::string cxx_lang = "<%c++";
|
||||
static const std::string cxx_view_data = "@@";
|
||||
static const std::string cxx_output = "$$";
|
||||
static const std::string cxx_val_start = "[[";
|
||||
static const std::string cxx_val_end = "]]";
|
||||
static const std::string sub_view_start = "<%view";
|
||||
static const std::string sub_view_end = "%>";
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
static std::string &replace_all(std::string &str,
|
||||
const std::string &old_value,
|
||||
const std::string &new_value)
|
||||
{
|
||||
std::string::size_type pos(0);
|
||||
while (true)
|
||||
{
|
||||
// std::cout<<str<<endl;
|
||||
// std::cout<<"pos="<<pos<<endl;
|
||||
if ((pos = str.find(old_value, pos)) != std::string::npos)
|
||||
{
|
||||
str = str.replace(pos, old_value.length(), new_value);
|
||||
pos += new_value.length() - old_value.length();
|
||||
++pos;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static void parseCxxLine(std::ofstream &oSrcFile,
|
||||
const std::string &line,
|
||||
const std::string &streamName,
|
||||
const std::string &viewDataName)
|
||||
{
|
||||
if (line.length() > 0)
|
||||
{
|
||||
std::string tmp = line;
|
||||
replace_all(tmp, cxx_output, streamName);
|
||||
replace_all(tmp, cxx_view_data, viewDataName);
|
||||
oSrcFile << tmp << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void outputVal(std::ofstream &oSrcFile,
|
||||
const std::string &streamName,
|
||||
const std::string &viewDataName,
|
||||
const std::string &keyName)
|
||||
{
|
||||
oSrcFile << "{\n";
|
||||
oSrcFile << " auto & val=" << viewDataName << "[\"" << keyName
|
||||
<< "\"];\n";
|
||||
oSrcFile << " if(val.type()==typeid(const char *)){\n";
|
||||
oSrcFile << " " << streamName
|
||||
<< "<<*(std::any_cast<const char *>(&val));\n";
|
||||
oSrcFile << " }else "
|
||||
"if(val.type()==typeid(std::string)||val.type()==typeid(const "
|
||||
"std::string)){\n";
|
||||
oSrcFile << " " << streamName
|
||||
<< "<<*(std::any_cast<const std::string>(&val));\n";
|
||||
oSrcFile << " }\n";
|
||||
oSrcFile << "}\n";
|
||||
}
|
||||
|
||||
static void outputSubView(std::ofstream &oSrcFile,
|
||||
const std::string &streamName,
|
||||
const std::string &viewDataName,
|
||||
const std::string &keyName)
|
||||
{
|
||||
oSrcFile << "{\n";
|
||||
oSrcFile << " auto templ=DrTemplateBase::newTemplate(\"" << keyName
|
||||
<< "\");\n";
|
||||
oSrcFile << " if(templ){\n";
|
||||
oSrcFile << " " << streamName << "<< templ->genText(" << viewDataName
|
||||
<< ");\n";
|
||||
oSrcFile << " }\n";
|
||||
oSrcFile << "}\n";
|
||||
}
|
||||
|
||||
static void parseLine(std::ofstream &oSrcFile,
|
||||
std::string &line,
|
||||
const std::string &streamName,
|
||||
const std::string &viewDataName,
|
||||
int &cxx_flag,
|
||||
int returnFlag = 1)
|
||||
{
|
||||
std::string::size_type pos(0);
|
||||
// std::cout<<line<<"("<<line.length()<<")\n";
|
||||
if (line.length() > 0 && line[line.length() - 1] == '\r')
|
||||
{
|
||||
line.resize(line.length() - 1);
|
||||
}
|
||||
if (line.length() == 0)
|
||||
{
|
||||
// std::cout<<"blank line!"<<std::endl;
|
||||
// std::cout<<streamName<<"<<\"\\n\";\n";
|
||||
if (returnFlag && !cxx_flag)
|
||||
oSrcFile << streamName << "<<\"\\n\";\n";
|
||||
return;
|
||||
}
|
||||
if (cxx_flag == 0)
|
||||
{
|
||||
// find cxx lang begin
|
||||
if ((pos = line.find(cxx_lang)) != std::string::npos)
|
||||
{
|
||||
std::string oldLine = line.substr(0, pos);
|
||||
if (oldLine.length() > 0)
|
||||
parseLine(
|
||||
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
||||
std::string newLine = line.substr(pos + cxx_lang.length());
|
||||
cxx_flag = 1;
|
||||
if (newLine.length() > 0)
|
||||
parseLine(oSrcFile,
|
||||
newLine,
|
||||
streamName,
|
||||
viewDataName,
|
||||
cxx_flag,
|
||||
returnFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pos = line.find(cxx_val_start)) != std::string::npos)
|
||||
{
|
||||
std::string oldLine = line.substr(0, pos);
|
||||
parseLine(
|
||||
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
||||
std::string newLine = line.substr(pos + cxx_val_start.length());
|
||||
if ((pos = newLine.find(cxx_val_end)) != std::string::npos)
|
||||
{
|
||||
std::string keyName = newLine.substr(0, pos);
|
||||
auto iter = keyName.begin();
|
||||
while (iter != keyName.end() && *iter == ' ')
|
||||
++iter;
|
||||
auto iterEnd = iter;
|
||||
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
||||
++iterEnd;
|
||||
keyName = std::string(iter, iterEnd);
|
||||
outputVal(oSrcFile, streamName, viewDataName, keyName);
|
||||
std::string tailLine =
|
||||
newLine.substr(pos + cxx_val_end.length());
|
||||
parseLine(oSrcFile,
|
||||
tailLine,
|
||||
streamName,
|
||||
viewDataName,
|
||||
cxx_flag,
|
||||
returnFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "format err!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ((pos = line.find(sub_view_start)) != std::string::npos)
|
||||
{
|
||||
std::string oldLine = line.substr(0, pos);
|
||||
parseLine(
|
||||
oSrcFile, oldLine, streamName, viewDataName, cxx_flag, 0);
|
||||
std::string newLine =
|
||||
line.substr(pos + sub_view_start.length());
|
||||
if ((pos = newLine.find(sub_view_end)) != std::string::npos)
|
||||
{
|
||||
std::string keyName = newLine.substr(0, pos);
|
||||
auto iter = keyName.begin();
|
||||
while (iter != keyName.end() && *iter == ' ')
|
||||
++iter;
|
||||
auto iterEnd = iter;
|
||||
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
||||
++iterEnd;
|
||||
keyName = std::string(iter, iterEnd);
|
||||
outputSubView(oSrcFile, streamName, viewDataName, keyName);
|
||||
std::string tailLine =
|
||||
newLine.substr(pos + sub_view_end.length());
|
||||
parseLine(oSrcFile,
|
||||
tailLine,
|
||||
streamName,
|
||||
viewDataName,
|
||||
cxx_flag,
|
||||
returnFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "format err!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line.length() > 0)
|
||||
{
|
||||
replace_all(line, "\\", "\\\\");
|
||||
replace_all(line, "\"", "\\\"");
|
||||
oSrcFile << "\t" << streamName << " << \"" << line;
|
||||
}
|
||||
if (returnFlag)
|
||||
oSrcFile << "\\n\";\n";
|
||||
else
|
||||
oSrcFile << "\";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pos = line.find(cxx_end)) != std::string::npos)
|
||||
{
|
||||
std::string newLine = line.substr(0, pos);
|
||||
parseCxxLine(oSrcFile, newLine, streamName, viewDataName);
|
||||
std::string oldLine = line.substr(pos + cxx_end.length());
|
||||
cxx_flag = 0;
|
||||
if (oldLine.length() > 0)
|
||||
parseLine(oSrcFile,
|
||||
oldLine,
|
||||
streamName,
|
||||
viewDataName,
|
||||
cxx_flag,
|
||||
returnFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
parseCxxLine(oSrcFile, line, streamName, viewDataName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_view::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
for (auto iter = parameters.begin(); iter != parameters.end();)
|
||||
{
|
||||
auto &file = *iter;
|
||||
if (file == "-o" || file == "--output")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
if (iter != parameters.end())
|
||||
{
|
||||
outputPath_ = *iter;
|
||||
iter = parameters.erase(iter);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (file == "-n" || file == "--namespace")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
if (iter != parameters.end())
|
||||
{
|
||||
namespaces_ = utils::splitString(*iter, "::");
|
||||
iter = parameters.erase(iter);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (file == "--path-to-namespace")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
pathToNamespaceFlag_ = true;
|
||||
continue;
|
||||
}
|
||||
else if (file[0] == '-')
|
||||
{
|
||||
std::cout << ARGS_ERROR_STR << std::endl;
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
createViewFiles(parameters);
|
||||
}
|
||||
|
||||
void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
|
||||
{
|
||||
for (auto const &file : cspFileNames)
|
||||
{
|
||||
std::cout << "create view:" << file << std::endl;
|
||||
if (createViewFile(file) != 0)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int create_view::createViewFile(const std::string &script_filename)
|
||||
{
|
||||
std::cout << "create HttpView Class file by " << script_filename
|
||||
<< std::endl;
|
||||
if (pathToNamespaceFlag_)
|
||||
{
|
||||
std::string::size_type pos1 = 0, pos2 = 0;
|
||||
if (script_filename.length() >= 2 && script_filename[0] == '.' &&
|
||||
(script_filename[1] == '/' || script_filename[1] == '\\'))
|
||||
{
|
||||
pos1 = pos2 = 2;
|
||||
}
|
||||
else if (script_filename.length() >= 1 &&
|
||||
(script_filename[0] == '/' || script_filename[0] == '\\'))
|
||||
{
|
||||
pos1 = pos2 = 1;
|
||||
}
|
||||
while (pos2 < script_filename.length() - 1)
|
||||
{
|
||||
if (script_filename[pos2] == '/' || script_filename[pos2] == '\\')
|
||||
{
|
||||
if (pos2 > pos1)
|
||||
{
|
||||
namespaces_.push_back(
|
||||
script_filename.substr(pos1, pos2 - pos1));
|
||||
}
|
||||
pos1 = ++pos2;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pos2;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string npPrefix;
|
||||
for (auto &np : namespaces_)
|
||||
{
|
||||
npPrefix += np;
|
||||
npPrefix += "_";
|
||||
}
|
||||
std::ifstream infile(script_filename.c_str(), std::ifstream::in);
|
||||
if (infile)
|
||||
{
|
||||
std::string::size_type pos = script_filename.rfind('.');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string className = script_filename.substr(0, pos);
|
||||
if ((pos = className.rfind('/')) != std::string::npos)
|
||||
{
|
||||
className = className.substr(pos + 1);
|
||||
}
|
||||
std::cout << "className=" << className << std::endl;
|
||||
std::string headFileName =
|
||||
outputPath_ + "/" + npPrefix + className + ".h";
|
||||
std::string sourceFilename =
|
||||
outputPath_ + "/" + npPrefix + className + ".cc";
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(),
|
||||
std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
std::cerr << "Can't open " << headFileName << " or "
|
||||
<< sourceFilename << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
newViewHeaderFile(oHeadFile, className);
|
||||
newViewSourceFile(oSourceFile, className, npPrefix, infile);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "can't open file " << script_filename << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void create_view::newViewHeaderFile(std::ofstream &file,
|
||||
const std::string &className)
|
||||
{
|
||||
file << "//this file is generated by program automatically,don't modify "
|
||||
"it!\n";
|
||||
file << "#include <drogon/DrTemplate.h>\n";
|
||||
for (auto &np : namespaces_)
|
||||
{
|
||||
file << "namespace " << np << "\n";
|
||||
file << "{\n";
|
||||
}
|
||||
file << "class " << className << ":public drogon::DrTemplate<" << className
|
||||
<< ">\n";
|
||||
file << "{\npublic:\n\t" << className << "(){};\n\tvirtual ~" << className
|
||||
<< "(){};\n\t"
|
||||
"virtual std::string genText(const drogon::DrTemplateData &) "
|
||||
"override;\n};\n";
|
||||
for (std::size_t i = 0; i < namespaces_.size(); ++i)
|
||||
{
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_view::newViewSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &namespacePrefix,
|
||||
std::ifstream &infile)
|
||||
{
|
||||
file << "//this file is generated by program(drogon_ctl) "
|
||||
"automatically,don't modify it!\n";
|
||||
file << "#include \"" << namespacePrefix << className << ".h\"\n";
|
||||
file << "#include <drogon/utils/OStringStream.h>\n";
|
||||
file << "#include <drogon/utils/Utilities.h>\n";
|
||||
file << "#include <string>\n";
|
||||
file << "#include <map>\n";
|
||||
file << "#include <vector>\n";
|
||||
file << "#include <set>\n";
|
||||
file << "#include <iostream>\n";
|
||||
file << "#include <unordered_map>\n";
|
||||
file << "#include <unordered_set>\n";
|
||||
file << "#include <algorithm>\n";
|
||||
file << "#include <list>\n";
|
||||
file << "#include <deque>\n";
|
||||
file << "#include <queue>\n";
|
||||
|
||||
// Find layout tag
|
||||
std::string layoutName;
|
||||
std::regex layoutReg("<%layout[ \\t]+(((?!%\\}).)*[^ \\t])[ \\t]*%>");
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
std::smatch results;
|
||||
if (std::regex_search(buffer, results, layoutReg))
|
||||
{
|
||||
if (results.size() > 1)
|
||||
{
|
||||
layoutName = results[1].str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
infile.clear();
|
||||
infile.seekg(0, std::ifstream::beg);
|
||||
bool import_flag{false};
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
std::string::size_type pos(0);
|
||||
|
||||
if (!import_flag)
|
||||
{
|
||||
std::string lowerBuffer = buffer;
|
||||
std::transform(lowerBuffer.begin(),
|
||||
lowerBuffer.end(),
|
||||
lowerBuffer.begin(),
|
||||
[](unsigned char c) { return tolower(c); });
|
||||
if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)
|
||||
{
|
||||
// std::cout<<"haha find it!"<<endl;
|
||||
std::string newLine = buffer.substr(pos + cxx_include.length());
|
||||
import_flag = true;
|
||||
if ((pos = newLine.find(cxx_end)) != std::string::npos)
|
||||
{
|
||||
newLine = newLine.substr(0, pos);
|
||||
file << newLine << "\n";
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
file << newLine << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cout<<buffer<<endl;
|
||||
if ((pos = buffer.find(cxx_end)) != std::string::npos)
|
||||
{
|
||||
std::string newLine = buffer.substr(0, pos);
|
||||
file << newLine << "\n";
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cout<<"to source file"<<buffer<<endl;
|
||||
file << buffer << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
// std::cout<<"import_flag="<<import_flag<<std::endl;
|
||||
if (!import_flag)
|
||||
{
|
||||
infile.clear();
|
||||
infile.seekg(0, std::ifstream::beg);
|
||||
}
|
||||
|
||||
if (!namespaces_.empty())
|
||||
{
|
||||
file << "using namespace ";
|
||||
for (std::size_t i = 0; i < namespaces_.size(); ++i)
|
||||
{
|
||||
if (i != namespaces_.size() - 1)
|
||||
{
|
||||
file << namespaces_[i] << "::";
|
||||
}
|
||||
else
|
||||
{
|
||||
file << namespaces_[i] << ";";
|
||||
}
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
file << "using namespace drogon;\n";
|
||||
std::string viewDataName = className + "_view_data";
|
||||
// virtual std::string genText(const DrTemplateData &)
|
||||
file << "std::string " << className << "::genText(const DrTemplateData& "
|
||||
<< viewDataName << ")\n{\n";
|
||||
// std::string bodyName=className+"_bodystr";
|
||||
std::string streamName = className + "_tmp_stream";
|
||||
|
||||
// oSrcFile <<"\tstd::string "<<bodyName<<";\n";
|
||||
file << "\tdrogon::OStringStream " << streamName << ";\n";
|
||||
file << "\tstd::string layoutName{\"" << layoutName << "\"};\n";
|
||||
int cxx_flag = 0;
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
if (buffer.length() > 0)
|
||||
{
|
||||
std::smatch results;
|
||||
if (std::regex_search(buffer, results, layoutReg))
|
||||
{
|
||||
if (results.size() > 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::regex re("\\{%[ \\t]*(((?!%\\}).)*[^ \\t])[ \\t]*%\\}");
|
||||
buffer = std::regex_replace(buffer, re, "<%c++$$$$<<$1;%>");
|
||||
}
|
||||
parseLine(file, buffer, streamName, viewDataName, cxx_flag);
|
||||
}
|
||||
file << "if(layoutName.empty())\n{\n";
|
||||
file << "std::string ret{std::move(" << streamName << ".str())};\n";
|
||||
file << "return ret;\n}else\n{\n";
|
||||
file << "auto templ = DrTemplateBase::newTemplate(layoutName);\n";
|
||||
file << "if(!templ) return \"\";\n";
|
||||
file << "HttpViewData data = " << viewDataName << ";\n";
|
||||
file << "auto str = std::move(" << streamName << ".str());\n";
|
||||
file << "if(!str.empty() && str[str.length()-1] == '\\n') "
|
||||
"str.resize(str.length()-1);\n";
|
||||
file << "data[\"\"] = std::move(str);\n";
|
||||
file << "return templ->genText(data);\n";
|
||||
file << "}\n}\n";
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
*
|
||||
* @file create_view.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_view : public DrObject<create_view>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create view class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string outputPath_{"."};
|
||||
std::vector<std::string> namespaces_;
|
||||
bool pathToNamespaceFlag_{false};
|
||||
void createViewFiles(std::vector<std::string> &cspFileNames);
|
||||
int createViewFile(const std::string &script_filename);
|
||||
void newViewHeaderFile(std::ofstream &file, const std::string &className);
|
||||
void newViewSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &namespacePrefix,
|
||||
std::ifstream &infile);
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
*
|
||||
* help.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "help.h"
|
||||
#include <drogon/DrClassMap.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
using namespace drogon_ctl;
|
||||
|
||||
void help::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() == 0)
|
||||
{
|
||||
std::cout << "usage: drogon_ctl [-v | --version] [-h | --help] "
|
||||
"<command> [<args>]"
|
||||
<< std::endl;
|
||||
std::cout << "commands list:" << std::endl;
|
||||
for (auto &className : drogon::DrClassMap::getAllClassName())
|
||||
{
|
||||
auto classPtr = std::shared_ptr<DrObjectBase>(
|
||||
drogon::DrClassMap::newObject(className));
|
||||
if (classPtr)
|
||||
{
|
||||
auto cmdHdlPtr =
|
||||
std::dynamic_pointer_cast<CommandHandler>(classPtr);
|
||||
if (cmdHdlPtr)
|
||||
{
|
||||
if (!cmdHdlPtr->isTopCommand())
|
||||
continue;
|
||||
auto pos = className.rfind("::");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
className = className.substr(pos + 2);
|
||||
}
|
||||
while (className.length() < 24)
|
||||
className.append(" ");
|
||||
std::cout << className << cmdHdlPtr->script() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cmd = std::string("drogon_ctl::") + parameters[0];
|
||||
|
||||
auto classPtr =
|
||||
std::shared_ptr<DrObjectBase>(drogon::DrClassMap::newObject(cmd));
|
||||
if (classPtr)
|
||||
{
|
||||
auto cmdHdlPtr =
|
||||
std::dynamic_pointer_cast<CommandHandler>(classPtr);
|
||||
if (cmdHdlPtr)
|
||||
{
|
||||
if (cmdHdlPtr->isTopCommand())
|
||||
std::cout << cmdHdlPtr->detail() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
*
|
||||
* help.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class help : public DrObject<help>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "display this message";
|
||||
}
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
*
|
||||
* @file main.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cmd.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
if (argc < 2)
|
||||
{
|
||||
args = {"help"};
|
||||
exeCommand(args);
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
if (args.size() > 0)
|
||||
{
|
||||
auto &arg = args[0];
|
||||
if (arg == "-h" || arg == "--help")
|
||||
{
|
||||
arg = "help";
|
||||
}
|
||||
else if (arg == "-v" || arg == "--version")
|
||||
{
|
||||
arg = "version";
|
||||
}
|
||||
}
|
||||
|
||||
exeCommand(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+472
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
*
|
||||
* press.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by the MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "press.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/DrClassMap.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <iomanip>
|
||||
#include <cstdlib>
|
||||
#include <json/json.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
std::string press::detail()
|
||||
{
|
||||
return "Use press command to do stress testing\n"
|
||||
"Usage:drogon_ctl press <options> <url>\n"
|
||||
" -n num number of requests(default : 1)\n"
|
||||
" -t num number of threads(default : 1)\n"
|
||||
" -c num concurrent connections(default : 1)\n"
|
||||
" -k disable SSL certificate validation(default: enable)\n"
|
||||
" -f customize http request json file(default: disenable)\n"
|
||||
" -q no progress indication(default: show)\n\n"
|
||||
"example: drogon_ctl press -n 10000 -c 100 -t 4 -q "
|
||||
"http://localhost:8080/index.html -f ./http_request.json\n";
|
||||
}
|
||||
|
||||
void outputErrorAndExit(const std::string_view &err)
|
||||
{
|
||||
std::cout << err << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
|
||||
{
|
||||
auto ¶m = *iter;
|
||||
if (param.find("-n") == 0)
|
||||
{
|
||||
if (param == "-n")
|
||||
{
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of requests!");
|
||||
}
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
numOfRequests_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of requests!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
numOfRequests_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of requests!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (param.find("-t") == 0)
|
||||
{
|
||||
if (param == "-t")
|
||||
{
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of threads!");
|
||||
}
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
numOfThreads_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of threads!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
numOfThreads_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of threads!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (param.find("-c") == 0)
|
||||
{
|
||||
if (param == "-c")
|
||||
{
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of connections!");
|
||||
}
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
numOfConnections_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of connections!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
numOfConnections_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
outputErrorAndExit("Invalid number of connections!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (param.find("-f") == 0)
|
||||
{
|
||||
if (param == "-f")
|
||||
{
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No http request json file!");
|
||||
}
|
||||
httpRequestJsonFile_ = *iter;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpRequestJsonFile_ = param.substr(2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (param == "-k")
|
||||
{
|
||||
certValidation_ = false;
|
||||
continue;
|
||||
}
|
||||
else if (param == "-q")
|
||||
{
|
||||
processIndication_ = false;
|
||||
}
|
||||
else if (param[0] != '-')
|
||||
{
|
||||
url_ = param;
|
||||
}
|
||||
}
|
||||
// std::cout << "n=" << numOfRequests_ << std::endl;
|
||||
// std::cout << "t=" << numOfThreads_ << std::endl;
|
||||
// std::cout << "c=" << numOfConnections_ << std::endl;
|
||||
// std::cout << "q=" << processIndication_ << std::endl;
|
||||
// std::cout << "url=" << url_ << std::endl;
|
||||
if (url_.empty() || url_.compare(0, 4, "http") != 0 ||
|
||||
(url_.compare(4, 3, "://") != 0 && url_.compare(4, 4, "s://") != 0))
|
||||
{
|
||||
outputErrorAndExit("Invalid URL");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pos = url_.find("://");
|
||||
auto posOfPath = url_.find('/', pos + 3);
|
||||
if (posOfPath == std::string::npos)
|
||||
{
|
||||
host_ = url_;
|
||||
path_ = "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
host_ = url_.substr(0, posOfPath);
|
||||
path_ = url_.substr(posOfPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
http_request.json
|
||||
{
|
||||
"method": "POST",
|
||||
"header": {
|
||||
"token": "e2e9d0fe-dd14-4eaf-8ac1-0997730a805d"
|
||||
},
|
||||
"body": {
|
||||
"passwd": "123456",
|
||||
"account": "10001"
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (!httpRequestJsonFile_.empty())
|
||||
{
|
||||
Json::Value httpRequestJson;
|
||||
std::ifstream httpRequestFile(httpRequestJsonFile_,
|
||||
std::ifstream::binary);
|
||||
if (!httpRequestFile.is_open())
|
||||
{
|
||||
outputErrorAndExit(std::string{"No "} + httpRequestJsonFile_);
|
||||
}
|
||||
httpRequestFile >> httpRequestJson;
|
||||
|
||||
if (!httpRequestJson.isMember("method"))
|
||||
{
|
||||
outputErrorAndExit("No contain method");
|
||||
}
|
||||
|
||||
auto methodStr = httpRequestJson["method"].asString();
|
||||
std::transform(methodStr.begin(),
|
||||
methodStr.end(),
|
||||
methodStr.begin(),
|
||||
::toupper);
|
||||
|
||||
auto toHttpMethod = [&]() -> drogon::HttpMethod {
|
||||
if (methodStr == "GET")
|
||||
{
|
||||
return drogon::HttpMethod::Get;
|
||||
}
|
||||
else if (methodStr == "POST")
|
||||
{
|
||||
return drogon::HttpMethod::Post;
|
||||
}
|
||||
else if (methodStr == "HEAD")
|
||||
{
|
||||
return drogon::HttpMethod::Head;
|
||||
}
|
||||
else if (methodStr == "PUT")
|
||||
{
|
||||
return drogon::HttpMethod::Put;
|
||||
}
|
||||
else if (methodStr == "DELETE")
|
||||
{
|
||||
return drogon::HttpMethod::Delete;
|
||||
}
|
||||
else if (methodStr == "OPTIONS")
|
||||
{
|
||||
return drogon::HttpMethod::Options;
|
||||
}
|
||||
else if (methodStr == "PATCH")
|
||||
{
|
||||
return drogon::HttpMethod::Patch;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputErrorAndExit("invalid method");
|
||||
}
|
||||
return drogon::HttpMethod::Get;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::string> header;
|
||||
if (httpRequestJson.isMember("header"))
|
||||
{
|
||||
auto &jsonValue = httpRequestJson["header"];
|
||||
for (const auto &key : jsonValue.getMemberNames())
|
||||
{
|
||||
if (jsonValue[key].isString())
|
||||
{
|
||||
header[key] = jsonValue[key].asString();
|
||||
}
|
||||
else
|
||||
{
|
||||
header[key] = jsonValue[key].toStyledString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string body;
|
||||
if (httpRequestJson.isMember("body"))
|
||||
{
|
||||
Json::FastWriter fastWriter;
|
||||
body = fastWriter.write(httpRequestJson["body"]);
|
||||
}
|
||||
|
||||
createHttpRequestFunc_ = [this,
|
||||
method = toHttpMethod(),
|
||||
body = std::move(body),
|
||||
header =
|
||||
std::move(header)]() -> HttpRequestPtr {
|
||||
auto request = HttpRequest::newHttpRequest();
|
||||
request->setPath(path_);
|
||||
request->setMethod(method);
|
||||
for (const auto &[field, val] : header)
|
||||
request->addHeader(field, val);
|
||||
if (!body.empty())
|
||||
request->setBody(body);
|
||||
return request;
|
||||
};
|
||||
}
|
||||
|
||||
// std::cout << "host=" << host_ << std::endl;
|
||||
// std::cout << "path=" << path_ << std::endl;
|
||||
doTesting();
|
||||
}
|
||||
|
||||
void press::doTesting()
|
||||
{
|
||||
createRequestAndClients();
|
||||
if (clients_.empty())
|
||||
{
|
||||
outputErrorAndExit("No connection!");
|
||||
}
|
||||
statistics_.startDate_ = trantor::Date::now();
|
||||
for (auto &client : clients_)
|
||||
{
|
||||
sendRequest(client);
|
||||
}
|
||||
loopPool_->wait();
|
||||
}
|
||||
|
||||
void press::createRequestAndClients()
|
||||
{
|
||||
loopPool_ = std::make_unique<trantor::EventLoopThreadPool>(numOfThreads_);
|
||||
loopPool_->start();
|
||||
for (size_t i = 0; i < numOfConnections_; ++i)
|
||||
{
|
||||
auto client = HttpClient::newHttpClient(host_,
|
||||
loopPool_->getNextLoop(),
|
||||
false,
|
||||
certValidation_);
|
||||
client->enableCookies();
|
||||
clients_.push_back(client);
|
||||
}
|
||||
}
|
||||
|
||||
void press::sendRequest(const HttpClientPtr &client)
|
||||
{
|
||||
auto numOfRequest = statistics_.numOfRequestsSent_++;
|
||||
if (numOfRequest >= numOfRequests_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequestPtr request;
|
||||
if (createHttpRequestFunc_)
|
||||
{
|
||||
request = createHttpRequestFunc_();
|
||||
}
|
||||
else
|
||||
{
|
||||
request = HttpRequest::newHttpRequest();
|
||||
request->setPath(path_);
|
||||
request->setMethod(Get);
|
||||
}
|
||||
|
||||
// std::cout << "send!" << std::endl;
|
||||
client->sendRequest(
|
||||
request,
|
||||
[this, client, request](ReqResult r, const HttpResponsePtr &resp) {
|
||||
size_t goodNum, badNum;
|
||||
if (r == ReqResult::Ok)
|
||||
{
|
||||
// std::cout << "OK" << std::endl;
|
||||
goodNum = ++statistics_.numOfGoodResponse_;
|
||||
badNum = statistics_.numOfBadResponse_;
|
||||
statistics_.bytesRecieved_ += resp->body().length();
|
||||
auto delay = trantor::Date::now().microSecondsSinceEpoch() -
|
||||
request->creationDate().microSecondsSinceEpoch();
|
||||
statistics_.totalDelay_ += delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
goodNum = statistics_.numOfGoodResponse_;
|
||||
badNum = ++statistics_.numOfBadResponse_;
|
||||
if (badNum > numOfRequests_ / 10)
|
||||
{
|
||||
outputErrorAndExit("Too many errors");
|
||||
}
|
||||
}
|
||||
if (goodNum + badNum >= numOfRequests_)
|
||||
{
|
||||
outputResults();
|
||||
}
|
||||
if (r == ReqResult::Ok)
|
||||
sendRequest(client);
|
||||
else
|
||||
{
|
||||
client->getLoop()->runAfter(1, [this, client]() {
|
||||
sendRequest(client);
|
||||
});
|
||||
}
|
||||
|
||||
if (processIndication_)
|
||||
{
|
||||
auto rec = goodNum + badNum;
|
||||
if (rec % 100000 == 0)
|
||||
{
|
||||
std::cout << rec << " responses are received" << std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void press::outputResults()
|
||||
{
|
||||
size_t totalSent = 0;
|
||||
size_t totalRecv = 0;
|
||||
for (auto &client : clients_)
|
||||
{
|
||||
totalSent += client->bytesSent();
|
||||
totalRecv += client->bytesReceived();
|
||||
}
|
||||
auto now = trantor::Date::now();
|
||||
auto microSecs = now.microSecondsSinceEpoch() -
|
||||
statistics_.startDate_.microSecondsSinceEpoch();
|
||||
double seconds = (double)microSecs / 1000000.0;
|
||||
auto rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds);
|
||||
std::cout << std::endl;
|
||||
std::cout << "TOTALS: " << numOfConnections_ << " connect, "
|
||||
<< numOfRequests_ << " requests, "
|
||||
<< statistics_.numOfGoodResponse_ << " success, "
|
||||
<< statistics_.numOfBadResponse_ << " fail" << std::endl;
|
||||
|
||||
std::cout << "TRAFFIC: "
|
||||
<< statistics_.bytesRecieved_ / statistics_.numOfGoodResponse_
|
||||
<< " avg bytes, "
|
||||
<< (totalRecv - statistics_.bytesRecieved_) /
|
||||
statistics_.numOfGoodResponse_
|
||||
<< " avg overhead, " << statistics_.bytesRecieved_ << " bytes, "
|
||||
<< totalRecv - statistics_.bytesRecieved_ << " overhead"
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(3)
|
||||
<< "TIMING: " << seconds << " seconds, " << rps << " rps, "
|
||||
<< (double)(statistics_.totalDelay_) /
|
||||
statistics_.numOfGoodResponse_ / 1000
|
||||
<< " ms avg req time" << std::endl;
|
||||
|
||||
std::cout << "SPEED: download " << totalRecv / seconds / 1000
|
||||
<< " kBps, upload " << totalSent / seconds / 1000 << " kBps"
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
exit(0);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
*
|
||||
* press.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by the MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommandHandler.h"
|
||||
#include <drogon/DrObject.h>
|
||||
#include <drogon/HttpAppFramework.h>
|
||||
#include <drogon/HttpClient.h>
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
struct Statistics
|
||||
{
|
||||
std::atomic_size_t numOfRequestsSent_{0};
|
||||
std::atomic_size_t bytesRecieved_{0};
|
||||
std::atomic_size_t numOfGoodResponse_{0};
|
||||
std::atomic_size_t numOfBadResponse_{0};
|
||||
std::atomic_size_t totalDelay_{0};
|
||||
trantor::Date startDate_;
|
||||
trantor::Date endDate_;
|
||||
};
|
||||
|
||||
class press : public DrObject<press>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "Do stress testing(Use 'drogon_ctl help press' for more "
|
||||
"information)";
|
||||
}
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string detail() override;
|
||||
|
||||
private:
|
||||
size_t numOfThreads_{1};
|
||||
size_t numOfRequests_{1};
|
||||
size_t numOfConnections_{1};
|
||||
std::string httpRequestJsonFile_;
|
||||
std::function<HttpRequestPtr()> createHttpRequestFunc_;
|
||||
bool certValidation_{true};
|
||||
bool processIndication_{true};
|
||||
std::string url_;
|
||||
std::string host_;
|
||||
std::string path_;
|
||||
void doTesting();
|
||||
void createRequestAndClients();
|
||||
void sendRequest(const HttpClientPtr &client);
|
||||
void outputResults();
|
||||
std::unique_ptr<trantor::EventLoopThreadPool> loopPool_;
|
||||
std::vector<HttpClientPtr> clients_;
|
||||
Statistics statistics_;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
@@ -0,0 +1,75 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project([[ProjectName]] CXX)
|
||||
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
check_include_file_cxx(any HAS_ANY)
|
||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
||||
check_include_file_cxx(coroutine HAS_COROUTINE)
|
||||
if (NOT "${CMAKE_CXX_STANDARD}" STREQUAL "")
|
||||
# Do nothing
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
else ()
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif ()
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_executable(${PROJECT_NAME} main.cc)
|
||||
|
||||
# ##############################################################################
|
||||
# If you include the drogon source code locally in your project, use this method
|
||||
# to add drogon
|
||||
# add_subdirectory(drogon)
|
||||
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
#
|
||||
# and comment out the following lines
|
||||
find_package(Drogon CONFIG REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
||||
|
||||
# ##############################################################################
|
||||
|
||||
if (CMAKE_CXX_STANDARD LESS 17)
|
||||
message(FATAL_ERROR "c++17 or higher is required")
|
||||
elseif (CMAKE_CXX_STANDARD LESS 20)
|
||||
message(STATUS "use c++17")
|
||||
else ()
|
||||
message(STATUS "use c++20")
|
||||
endif ()
|
||||
|
||||
aux_source_directory(controllers CTL_SRC)
|
||||
aux_source_directory(filters FILTER_SRC)
|
||||
aux_source_directory(plugins PLUGIN_SRC)
|
||||
aux_source_directory(models MODEL_SRC)
|
||||
|
||||
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
# use the following line to create views with namespaces.
|
||||
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
# ${CMAKE_CURRENT_BINARY_DIR} TRUE)
|
||||
# use the following line to create views with namespace CHANGE_ME prefixed
|
||||
# and path namespaces.
|
||||
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
# ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/models)
|
||||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${SRC_DIR}
|
||||
${CTL_SRC}
|
||||
${FILTER_SRC}
|
||||
${PLUGIN_SRC}
|
||||
${MODEL_SRC})
|
||||
# ##############################################################################
|
||||
# uncomment the following line for dynamically loading views
|
||||
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
|
||||
|
||||
# ##############################################################################
|
||||
|
||||
add_subdirectory(test)
|
||||
@@ -0,0 +1,350 @@
|
||||
/* This is a JSON format configuration file
|
||||
*/
|
||||
{
|
||||
/*
|
||||
//ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
// "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
"ssl": {
|
||||
"cert": "../../trantor/trantor/tests/server.crt",
|
||||
"key": "../../trantor/trantor/tests/server.key",
|
||||
"conf": [
|
||||
//["Options", "-SessionTicket"],
|
||||
//["Options", "Compression"]
|
||||
]
|
||||
},
|
||||
"listeners": [
|
||||
{
|
||||
//address: Ip address,0.0.0.0 by default
|
||||
"address": "0.0.0.0",
|
||||
//port: Port number
|
||||
"port": 80,
|
||||
//https: If true, use https for security,false by default
|
||||
"https": false
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"port": 443,
|
||||
"https": true,
|
||||
//cert,key: Cert file path and key file path, empty by default,
|
||||
//if empty, use the global setting
|
||||
"cert": "",
|
||||
"key": "",
|
||||
//use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
"use_old_tls": false,
|
||||
"ssl_conf": [
|
||||
//["MinProtocol", "TLSv1.3"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"db_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
"rdbms": "postgresql",
|
||||
//filename: Sqlite3 db file name
|
||||
//"filename":"",
|
||||
//host: Server address,localhost by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 5432 by default
|
||||
"port": 5432,
|
||||
//dbname: Database name
|
||||
"dbname": "test",
|
||||
//user: 'postgres' by default
|
||||
"user": "",
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0,
|
||||
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
//the wiki for more details.
|
||||
"auto_batch": false
|
||||
//connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
//For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
//"connect_options": { "statement_timeout": "1s" }
|
||||
}
|
||||
],
|
||||
"redis_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//host: Server IP, 127.0.0.1 by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 6379 by default
|
||||
"port": 6379,
|
||||
//username: '' by default which means 'default' in redis ACL
|
||||
"username": "",
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//db index: 0 by default
|
||||
"db": 0,
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0
|
||||
}
|
||||
],*/
|
||||
"app": {
|
||||
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//is the number of CPU cores
|
||||
"number_of_threads": 1,
|
||||
//enable_session: False by default
|
||||
"enable_session": false,
|
||||
"session_timeout": 0,
|
||||
//string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
"session_same_site" : "Null",
|
||||
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
"session_cookie_key": "JSESSIONID",
|
||||
//session_max_age: The max age of the session cookie, -1 by default
|
||||
"session_max_age": -1,
|
||||
//document_root: Root path of HTTP document, default path is ./
|
||||
"document_root": "./",
|
||||
//home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
//to the request for "/".
|
||||
"home_page": "index.html",
|
||||
//use_implicit_page: enable implicit pages if true, true by default
|
||||
"use_implicit_page": true,
|
||||
//implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
//For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
"implicit_page": "index.html",
|
||||
//static_file_headers: Headers for static files
|
||||
/*"static_file_headers": [
|
||||
{
|
||||
"name": "field-name",
|
||||
"value": "field-value"
|
||||
}
|
||||
],*/
|
||||
//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
|
||||
"upload_path": "uploads",
|
||||
/* file_types:
|
||||
* HTTP download file types,The file types supported by drogon
|
||||
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
||||
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
||||
* "gif", "bmp", "ico", "icns", etc. */
|
||||
"file_types": [
|
||||
"gif",
|
||||
"png",
|
||||
"jpg",
|
||||
"js",
|
||||
"css",
|
||||
"html",
|
||||
"ico",
|
||||
"swf",
|
||||
"xap",
|
||||
"apk",
|
||||
"cur",
|
||||
"xml",
|
||||
"webp",
|
||||
"svg"
|
||||
],
|
||||
// mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
// note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
"mime": {
|
||||
// "text/markdown": "md",
|
||||
// "text/gemini": ["gmi", "gemini"]
|
||||
},
|
||||
//locations: An array of locations of static files for GET requests.
|
||||
"locations": [
|
||||
{
|
||||
//uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
//"uri_prefix": "/.well-known/acme-challenge/",
|
||||
//default_content_type: The default content type of the static files without
|
||||
//an extension. empty string by default.
|
||||
"default_content_type": "text/plain",
|
||||
//alias: The location in file system, if it is prefixed with "/", it
|
||||
//presents an absolute path, otherwise it presents a relative path to
|
||||
//the document_root path.
|
||||
//The default value is "" which means use the document root path as the location base path.
|
||||
"alias": "",
|
||||
//is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
"is_case_sensitive": false,
|
||||
//allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
"allow_all": true,
|
||||
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
"is_recursive": true,
|
||||
//filters: string array, the filters applied to the location.
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
//max_connections: maximum number of connections, 100000 by default
|
||||
"max_connections": 100000,
|
||||
//max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
"max_connections_per_ip": 0,
|
||||
//Load_dynamic_views: False by default, when set to true, drogon
|
||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
||||
//by "dynamic_views_path"
|
||||
"load_dynamic_views": false,
|
||||
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
||||
//it is relative path of document_root path
|
||||
"dynamic_views_path": [
|
||||
"./views"
|
||||
],
|
||||
//dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
//files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
//path of the current working directory.
|
||||
"dynamic_views_output_path": "",
|
||||
//json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
"json_parser_stack_limit": 1000,
|
||||
//enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
"enable_unicode_escaping_in_json": true,
|
||||
//float_precision_in_json: set precision of float number in json.
|
||||
"float_precision_in_json": {
|
||||
//precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
"precision": 0,
|
||||
//precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
//setting max number of significant digits in string, "decimal" means setting max number of
|
||||
//digits after "." in string
|
||||
"precision_type": "significant"
|
||||
},
|
||||
//log: Set log output, drogon output logs to stdout by default
|
||||
"log": {
|
||||
//use_spdlog: Use spdlog library to log
|
||||
"use_spdlog": false,
|
||||
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
//"log_path": "./",
|
||||
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
//drogon.log ...
|
||||
"logfile_base_name": "",
|
||||
//log_size_limit: 100000000 bytes by default,
|
||||
//When the log file size reaches "log_size_limit", the log file is switched.
|
||||
"log_size_limit": 100000000,
|
||||
//max_files: 0 by default,
|
||||
//When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
"max_files": 0,
|
||||
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
//The TRACE level is only valid when built in DEBUG mode.
|
||||
"log_level": "DEBUG",
|
||||
//display_local_time: false by default, if true, the log time is displayed in local time
|
||||
"display_local_time": false
|
||||
},
|
||||
//run_as_daemon: False by default
|
||||
"run_as_daemon": false,
|
||||
//handle_sig_term: True by default
|
||||
"handle_sig_term": true,
|
||||
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
"relaunch_on_error": false,
|
||||
//use_sendfile: True by default, if true, the program
|
||||
//uses sendfile() system-call to send static files to clients;
|
||||
"use_sendfile": true,
|
||||
//use_gzip: True by default, use gzip to compress the response body's content;
|
||||
"use_gzip": true,
|
||||
//use_brotli: False by default, use brotli to compress the response body's content;
|
||||
"use_brotli": false,
|
||||
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
//0 means cache forever, the negative value means no cache
|
||||
"static_files_cache_time": 5,
|
||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
//"simple_controllers_map": [
|
||||
// {
|
||||
// "path": "/path/name",
|
||||
// "controller": "controllerClassName",
|
||||
// "http_methods": [
|
||||
// "get",
|
||||
// "post"
|
||||
// ],
|
||||
// "filters": [
|
||||
// "FilterClassName"
|
||||
// ]
|
||||
// }
|
||||
//],
|
||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
//of the connection without read or write
|
||||
"idle_connection_timeout": 60,
|
||||
//server_header_field: Set the 'Server' header field in each response sent by drogon,
|
||||
//empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
|
||||
"server_header_field": "",
|
||||
//enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
|
||||
//value is true.
|
||||
"enable_server_header": true,
|
||||
//enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
|
||||
//value is true.
|
||||
"enable_date_header": true,
|
||||
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
||||
//After the maximum number of requests are made, the connection is closed.
|
||||
//The default value of 0 means no limit.
|
||||
"keepalive_requests": 0,
|
||||
//pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
||||
//After the maximum number of requests are made, the connection is closed.
|
||||
//The default value of 0 means no limit.
|
||||
"pipelining_requests": 0,
|
||||
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
//file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
//The default value of gzip_static is true.
|
||||
"gzip_static": true,
|
||||
//br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
//file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
//The default value of br_static is true.
|
||||
"br_static": true,
|
||||
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_body_size": "1M",
|
||||
//max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
||||
//If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
||||
//Setting it to "" means no limit.
|
||||
"client_max_memory_body_size": "64K",
|
||||
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_websocket_message_size": "128K",
|
||||
//reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
"reuse_port": false,
|
||||
// enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
// Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
// will be rejected.
|
||||
"enabled_compressed_request": false,
|
||||
// enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
// See the wiki for more details.
|
||||
"enable_request_stream": false,
|
||||
},
|
||||
//plugins: Define all plugins running in the application
|
||||
"plugins": [
|
||||
{
|
||||
//name: The class name of the plugin
|
||||
"name": "drogon::plugin::PromExporter",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"path": "/metrics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "drogon::plugin::AccessLogger",
|
||||
"dependencies": [],
|
||||
"config": {
|
||||
"use_spdlog": false,
|
||||
"log_path": "",
|
||||
"log_format": "",
|
||||
"log_file": "access.log",
|
||||
"log_size_limit": 0,
|
||||
"use_local_time": true,
|
||||
"log_index": 0,
|
||||
// "show_microseconds": true,
|
||||
// "custom_time_format": "",
|
||||
// "use_real_ip": false
|
||||
}
|
||||
}
|
||||
],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
"custom_config": {}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
# This is a YAML format configuration file
|
||||
|
||||
# ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
# "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
# ssl:
|
||||
# cert: ../../trantor/trantor/tests/server.crt
|
||||
# key: ../../trantor/trantor/tests/server.key
|
||||
# conf: [
|
||||
# # [Options, -SessionTicket],
|
||||
# # [Options, Compression]
|
||||
# ]
|
||||
# listeners:
|
||||
# # address: Ip address,0.0.0.0 by default
|
||||
# - address: 0.0.0.0
|
||||
# # port: Port number
|
||||
# port: 80
|
||||
# # https: If true, use https for security,false by default
|
||||
# https: false
|
||||
# - address: 0.0.0.0
|
||||
# port: 443
|
||||
# https: true
|
||||
# # cert,key: Cert file path and key file path, empty by default,
|
||||
# # if empty, use the global setting
|
||||
# cert: ''
|
||||
# key: ''
|
||||
# # use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
# use_old_tls: false
|
||||
# ssl_conf: [
|
||||
# # [MinProtocol, TLSv1.3]
|
||||
# ]
|
||||
# db_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
# rdbms: postgresql
|
||||
# # filename: Sqlite3 db file name
|
||||
# # filename: ''
|
||||
# # host: Server address,localhost by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 5432 by default
|
||||
# port: 5432
|
||||
# # dbname: Database name
|
||||
# dbname: test
|
||||
# # user: 'postgres' by default
|
||||
# user: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # client_encoding: The character set used by the client. it is empty string by default which
|
||||
# # means use the default character set.
|
||||
# # client_encoding: ''
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
# # the wiki for more details.
|
||||
# auto_batch: false
|
||||
# # connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
# # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
# # connect_options:
|
||||
# # statement_timeout: '1s'
|
||||
# redis_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # host: Server IP, 127.0.0.1 by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 6379 by default
|
||||
# port: 6379
|
||||
# # username: '' by default which means 'default' in redis ACL
|
||||
# username: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # db index: 0 by default
|
||||
# db: 0
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
app:
|
||||
# number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
# is the number of CPU cores
|
||||
number_of_threads: 1
|
||||
# enable_session: False by default
|
||||
enable_session: false
|
||||
session_timeout: 0
|
||||
# string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
session_same_site: 'Null'
|
||||
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
session_cookie_key: 'JSESSIONID'
|
||||
# session_max_age: The max age of the session cookie, -1 by default
|
||||
session_max_age: -1
|
||||
# document_root: Root path of HTTP document, default path is ./
|
||||
document_root: ./
|
||||
# home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
# If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
# to the request for "/".
|
||||
home_page: index.html
|
||||
# use_implicit_page: enable implicit pages if true, true by default
|
||||
use_implicit_page: true
|
||||
# implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
# For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
implicit_page: index.html
|
||||
# static_file_headers: Headers for static files
|
||||
# static_file_headers:
|
||||
# - name: field-name
|
||||
# value: field-value
|
||||
# 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
|
||||
upload_path: uploads
|
||||
# file_types:
|
||||
# HTTP download file types,The file types supported by drogon
|
||||
# by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
||||
# "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
||||
# "gif", "bmp", "ico", "icns", etc.
|
||||
file_types:
|
||||
- gif
|
||||
- png
|
||||
- jpg
|
||||
- js
|
||||
- css
|
||||
- html
|
||||
- ico
|
||||
- swf
|
||||
- xap
|
||||
- apk
|
||||
- cur
|
||||
- xml
|
||||
# mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
# note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
mime: {
|
||||
# text/markdown: md
|
||||
# text/gemini:
|
||||
# - gmi
|
||||
# - gemini
|
||||
}
|
||||
# locations: An array of locations of static files for GET requests.
|
||||
locations:
|
||||
# uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
- uri_prefix: '' # /.well-known/acme-challenge/
|
||||
# default_content_type: The default content type of the static files without
|
||||
# an extension. empty string by default.
|
||||
default_content_type: text/plain
|
||||
# alias: The location in file system, if it is prefixed with "/", it
|
||||
# presents an absolute path, otherwise it presents a relative path to
|
||||
# the document_root path.
|
||||
# The default value is "" which means use the document root path as the location base path.
|
||||
alias: ''
|
||||
# is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
is_case_sensitive: false
|
||||
# allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
allow_all: true
|
||||
# is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
is_recursive: true
|
||||
# filters: string array, the filters applied to the location.
|
||||
filters: []
|
||||
# max_connections: maximum number of connections, 100000 by default
|
||||
max_connections: 100000
|
||||
# max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
max_connections_per_ip: 0
|
||||
# Load_dynamic_views: False by default, when set to true, drogon
|
||||
# compiles and loads dynamically "CSP View Files" in directories defined
|
||||
# by "dynamic_views_path"
|
||||
load_dynamic_views: false
|
||||
# dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
||||
# it is relative path of document_root path
|
||||
dynamic_views_path:
|
||||
- ./views
|
||||
# dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
# files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
# path of the current working directory.
|
||||
dynamic_views_output_path: ''
|
||||
# json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
json_parser_stack_limit: 1000
|
||||
# enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
enable_unicode_escaping_in_json: true
|
||||
# float_precision_in_json: set precision of float number in json.
|
||||
float_precision_in_json:
|
||||
# precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
precision: 0
|
||||
# precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
# setting max number of significant digits in string, "decimal" means setting max number of
|
||||
# digits after "." in string
|
||||
precision_type: significant
|
||||
# log: Set log output, drogon output logs to stdout by default
|
||||
log:
|
||||
# use_spdlog: Use spdlog library to log
|
||||
use_spdlog: false
|
||||
# log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
# log_path: ./
|
||||
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
# drogon.log ...
|
||||
logfile_base_name: ''
|
||||
# log_size_limit: 100000000 bytes by default,
|
||||
# When the log file size reaches "log_size_limit", the log file is switched.
|
||||
log_size_limit: 100000000
|
||||
# max_files: 0 by default,
|
||||
# When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
max_files: 0
|
||||
# log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
# The TRACE level is only valid when built in DEBUG mode.
|
||||
log_level: DEBUG
|
||||
# display_local_time: false by default, if true, the log time is displayed in local time
|
||||
display_local_time: false
|
||||
# run_as_daemon: False by default
|
||||
run_as_daemon: false
|
||||
# handle_sig_term: True by default
|
||||
handle_sig_term: true
|
||||
# relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
relaunch_on_error: false
|
||||
# use_sendfile: True by default, if true, the program
|
||||
# uses sendfile() system-call to send static files to clients;
|
||||
use_sendfile: true
|
||||
# use_gzip: True by default, use gzip to compress the response body's content;
|
||||
use_gzip: true
|
||||
# use_brotli: False by default, use brotli to compress the response body's content;
|
||||
use_brotli: false
|
||||
# static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
# 0 means cache forever, the negative value means no cache
|
||||
static_files_cache_time: 5
|
||||
# simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
# simple_controllers_map:
|
||||
# - path: /path/name
|
||||
# controller: controllerClassName
|
||||
# http_methods:
|
||||
# - get
|
||||
# - post
|
||||
# filters:
|
||||
# - FilterClassName
|
||||
# idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
# of the connection without read or write
|
||||
idle_connection_timeout: 60
|
||||
# server_header_field: Set the 'Server' header field in each response sent by drogon,
|
||||
# empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
|
||||
server_header_field: ''
|
||||
# enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_server_header: true
|
||||
# enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_date_header: true
|
||||
# keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
keepalive_requests: 0
|
||||
# pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
pipelining_requests: 0
|
||||
# gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
# The default value of gzip_static is true.
|
||||
gzip_static: true
|
||||
# br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
# The default value of br_static is true.
|
||||
br_static: true
|
||||
# client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_body_size: 1M
|
||||
# max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
||||
# If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
||||
# Setting it to "" means no limit.
|
||||
client_max_memory_body_size: 64K
|
||||
# client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_websocket_message_size: 128K
|
||||
# reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
reuse_port: false
|
||||
# enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
# Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
# will be rejected.
|
||||
enabled_compressed_request: false
|
||||
# enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
# See the wiki for more details.
|
||||
enable_request_stream: false
|
||||
# plugins: Define all plugins running in the application
|
||||
plugins:
|
||||
# name: The class name of the plugin
|
||||
- name: drogon::plugin::PromExporter
|
||||
# dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
dependencies: []
|
||||
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
# It can be commented out
|
||||
config:
|
||||
path: /metrics
|
||||
- name: drogon::plugin::AccessLogger
|
||||
dependencies: []
|
||||
config:
|
||||
use_spdlog: false
|
||||
log_path: ''
|
||||
log_format: ''
|
||||
log_file: access.log
|
||||
log_size_limit: 0
|
||||
use_local_time: true
|
||||
log_index: 0
|
||||
# show_microseconds: true
|
||||
# custom_time_format: ''
|
||||
# use_real_ip: false
|
||||
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
custom_config: {}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <drogon/drogon.h>
|
||||
int main() {
|
||||
//Set HTTP listener address and port
|
||||
drogon::app().addListener("0.0.0.0", 5555);
|
||||
//Load config file
|
||||
//drogon::app().loadConfigFile("../config.json");
|
||||
//drogon::app().loadConfigFile("../config.yaml");
|
||||
//Run HTTP framework,the method will block in the internal event loop
|
||||
drogon::app().run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
*
|
||||
* [[filename]].cc
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[filename]].h"
|
||||
|
||||
using namespace drogon;
|
||||
<%c++auto namespaceStr=@@.get<std::string>("namespaceString");
|
||||
if(!namespaceStr.empty())
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
%>
|
||||
|
||||
void [[className]]::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
{
|
||||
//Edit your logic here
|
||||
if (1)
|
||||
{
|
||||
//Passed
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
//Check failed
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k500InternalServerError);
|
||||
fcb(res);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
*
|
||||
* [[filename]].h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpFilter.h>
|
||||
using namespace drogon;
|
||||
<%c++
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
if(namespaceVector.empty())
|
||||
$$<<"\n";
|
||||
for(auto &namespaceName:namespaceVector){%>
|
||||
namespace {%namespaceName%}
|
||||
|
||||
{
|
||||
<%c++}
|
||||
$$<<"\n";%>
|
||||
class [[className]] : public HttpFilter<[[className]]>
|
||||
{
|
||||
public:
|
||||
[[className]]() {}
|
||||
void doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb) override;
|
||||
};
|
||||
|
||||
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>
|
||||
}
|
||||
<%c++}%>
|
||||
@@ -0,0 +1,561 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
||||
|
||||
### C ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
|
||||
# Precompiled Headers
|
||||
|
||||
# Linker files
|
||||
|
||||
# Debugger Files
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
|
||||
# Executables
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
CMakeUserPresets.json
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
### Intellij+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### VisualStudio ###
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.meta
|
||||
*.iobj
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*[.json, .xml, .info]
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
### VisualStudio Patch ###
|
||||
# Additional files built by Visual Studio
|
||||
*.tlog
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,570 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[className]].h
|
||||
* DO NOT EDIT. This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <drogon/orm/Result.h>
|
||||
#include <drogon/orm/Row.h>
|
||||
#include <drogon/orm/Field.h>
|
||||
#include <drogon/orm/SqlBinder.h>
|
||||
#include <drogon/orm/Mapper.h>
|
||||
#include <drogon/orm/BaseBuilder.h>
|
||||
#ifdef __cpp_impl_coroutine
|
||||
#include <drogon/orm/CoroMapper.h>
|
||||
#endif
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <json/json.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace orm
|
||||
{
|
||||
class DbClient;
|
||||
using DbClientPtr = std::shared_ptr<DbClient>;
|
||||
}
|
||||
}
|
||||
namespace drogon_model
|
||||
{
|
||||
namespace [[dbName]]
|
||||
{
|
||||
<%c++
|
||||
auto &schema=@@.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"namespace "<<schema<<"\n";
|
||||
$$<<"{\n";
|
||||
}
|
||||
std::vector<std::string> relationshipClassNames;
|
||||
auto &relationships=@@.get<std::vector<Relationship>>("relationships");
|
||||
for(auto &relationship : relationships)
|
||||
{
|
||||
auto &name = relationship.targetTableName();
|
||||
auto relationshipClassName = nameTransform(name, true);
|
||||
relationshipClassNames.push_back(relationshipClassName);
|
||||
if(relationship.type() == Relationship::Type::ManyToMany)
|
||||
{
|
||||
auto &pivotTableName = relationship.pivotTable().tableName();
|
||||
auto pivotTableClassName = nameTransform(pivotTableName, true);
|
||||
relationshipClassNames.push_back(pivotTableClassName);
|
||||
}
|
||||
}
|
||||
std::sort(relationshipClassNames.begin(), relationshipClassNames.end());
|
||||
relationshipClassNames.erase(std::unique(relationshipClassNames.begin(), relationshipClassNames.end()), relationshipClassNames.end());
|
||||
for(std::string &relationshipClassName : relationshipClassNames)
|
||||
{
|
||||
%>
|
||||
class {%relationshipClassName%};
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
|
||||
class [[className]]
|
||||
{
|
||||
public:
|
||||
struct Cols
|
||||
{
|
||||
<%c++
|
||||
auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
$$<<" static const std::string _"<<cols[i].colName_<<";\n";
|
||||
}
|
||||
%>
|
||||
};
|
||||
|
||||
static const int primaryKeyNumber;
|
||||
static const std::string tableName;
|
||||
static const bool hasPrimaryKey;
|
||||
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
|
||||
static const std::string primaryKeyName;
|
||||
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
using PrimaryKeyType = [[primaryKeyType]];
|
||||
const PrimaryKeyType &getPrimaryKey() const;
|
||||
<%c++}else{%>
|
||||
using PrimaryKeyType = void;
|
||||
int getPrimaryKey() const { assert(false); return 0; }
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkTypes=@@.get<std::vector<std::string>>("primaryKeyType");
|
||||
std::string typelist;
|
||||
for(size_t i=0;i<pkTypes.size();i++)
|
||||
{
|
||||
typelist += pkTypes[i];
|
||||
if(i<(pkTypes.size()-1))
|
||||
typelist += ",";
|
||||
}
|
||||
%>
|
||||
static const std::vector<std::string> primaryKeyName;
|
||||
using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
$$<<pkName[i];
|
||||
if(i<(pkName.size()-1))
|
||||
$$<<",";
|
||||
}
|
||||
%>
|
||||
|
||||
PrimaryKeyType getPrimaryKey() const;
|
||||
<%c++}%>
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param r One row of records in the SQL query result.
|
||||
* @param indexOffset Set the offset to -1 to access all columns by column names,
|
||||
* otherwise access all columns by offsets.
|
||||
* @note If the SQL is not a style of 'select * from table_name ...' (select all
|
||||
* columns by an asterisk), please set the offset to -1.
|
||||
*/
|
||||
explicit [[className]](const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param pJson The json object to construct a new instance.
|
||||
*/
|
||||
explicit [[className]](const Json::Value &pJson) noexcept(false);
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param pJson The json object to construct a new instance.
|
||||
* @param pMasqueradingVector The aliases of table columns.
|
||||
*/
|
||||
[[className]](const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
|
||||
|
||||
[[className]]() = default;
|
||||
|
||||
void updateByJson(const Json::Value &pJson) noexcept(false);
|
||||
void updateByMasqueradedJson(const Json::Value &pJson,
|
||||
const std::vector<std::string> &pMasqueradingVector) noexcept(false);
|
||||
static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
|
||||
static bool validateMasqueradedJsonForCreation(const Json::Value &,
|
||||
const std::vector<std::string> &pMasqueradingVector,
|
||||
std::string &err);
|
||||
static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
|
||||
static bool validateMasqueradedJsonForUpdate(const Json::Value &,
|
||||
const std::vector<std::string> &pMasqueradingVector,
|
||||
std::string &err);
|
||||
static bool validJsonOfField(size_t index,
|
||||
const std::string &fieldName,
|
||||
const Json::Value &pJson,
|
||||
std::string &err,
|
||||
bool isForCreation);
|
||||
|
||||
<%c++
|
||||
for(const auto &col:cols)
|
||||
{
|
||||
$$<<" /** For column "<<col.colName_<<" */\n";
|
||||
if(!col.colType_.empty())
|
||||
{
|
||||
$$<<" ///Get the value of the column "<<col.colName_<<", returns the default value if the column is null\n";
|
||||
$$<<" const "<<col.colType_<<" &getValueOf"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
if(col.colType_=="std::vector<char>")
|
||||
{
|
||||
$$<<" ///Return the column value by std::string with binary data\n";
|
||||
$$<<" std::string getValueOf"<<col.colTypeName_<<"AsString() const noexcept;\n";
|
||||
}
|
||||
$$<<" ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null\n";
|
||||
$$<<" const std::shared_ptr<"<<col.colType_<<"> &get"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
|
||||
$$<<" ///Set the value of the column "<<col.colName_<<"\n";
|
||||
$$<<" void set"<<col.colTypeName_<<"(const "<<col.colType_<<" &p"<<col.colTypeName_<<") noexcept;\n";
|
||||
if(col.colType_=="std::string")
|
||||
$$<<" void set"<<col.colTypeName_<<"("<<col.colType_<<" &&p"<<col.colTypeName_<<") noexcept;\n";
|
||||
if(col.colType_=="std::vector<char>")
|
||||
{
|
||||
$$<<" void set"<<col.colTypeName_<<"(const std::string &p"<<col.colTypeName_<<") noexcept;\n";
|
||||
}
|
||||
if(!col.notNull_)
|
||||
{
|
||||
$$<<" void set"<<col.colTypeName_<<"ToNull() noexcept;\n";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
$$<<" //FIXME!!"<<" getValueOf"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
$$<<"\n";
|
||||
}
|
||||
%>
|
||||
|
||||
static size_t getColumnNumber() noexcept { return {% cols.size() %}; }
|
||||
static const std::string &getColumnName(size_t index) noexcept(false);
|
||||
|
||||
Json::Value toJson() const;
|
||||
std::string toString() const;
|
||||
Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
|
||||
/// Relationship interfaces
|
||||
<%c++
|
||||
for(auto &relationship : relationships)
|
||||
{
|
||||
if(relationship.targetKey().empty() || relationship.originalKey().empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto &name=relationship.targetTableName();
|
||||
auto relationshipClassName=nameTransform(name, true);
|
||||
auto relationshipValName=nameTransform(name, false);
|
||||
auto alias=relationship.targetTableAlias();
|
||||
auto aliasValName=nameTransform(alias, false);
|
||||
if(relationship.type() == Relationship::Type::HasOne)
|
||||
{
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
{%relationshipClassName%} get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void({%relationshipClassName%})> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
{%relationshipClassName%} get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else if(relationship.type() == Relationship::Type::HasMany)
|
||||
{
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
std::vector<{%relationshipClassName%}> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
std::vector<{%relationshipClassName%}> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else if(relationship.type() == Relationship::Type::ManyToMany)
|
||||
{
|
||||
auto &pivotTableName=relationship.pivotTable().tableName();
|
||||
auto pivotTableClassName=nameTransform(pivotTableName, true);
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
}
|
||||
%>
|
||||
private:
|
||||
friend drogon::orm::Mapper<[[className]]>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], true, true>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], true, false>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], false, true>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], false, false>;
|
||||
#ifdef __cpp_impl_coroutine
|
||||
friend drogon::orm::CoroMapper<[[className]]>;
|
||||
#endif
|
||||
static const std::vector<std::string> &insertColumns() noexcept;
|
||||
void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
|
||||
const std::vector<std::string> updateColumns() const;
|
||||
void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
|
||||
///For mysql or sqlite3
|
||||
void updateId(const uint64_t id);
|
||||
<%c++
|
||||
for(auto col:cols)
|
||||
{
|
||||
if(!col.colType_.empty())
|
||||
$$<<" std::shared_ptr<"<<col.colType_<<"> "<<col.colValName_<<"_;\n";
|
||||
}
|
||||
%>
|
||||
struct MetaData
|
||||
{
|
||||
const std::string colName_;
|
||||
const std::string colType_;
|
||||
const std::string colDatabaseType_;
|
||||
const ssize_t colLength_;
|
||||
const bool isAutoVal_;
|
||||
const bool isPrimaryKey_;
|
||||
const bool notNull_;
|
||||
};
|
||||
static const std::vector<MetaData> metaData_;
|
||||
bool dirtyFlag_[{%cols.size()%}]={ false };
|
||||
public:
|
||||
static const std::string &sqlForFindingByPrimaryKey()
|
||||
{
|
||||
<%c++
|
||||
auto rdbms=@@.get<std::string>("rdbms");
|
||||
if(@@.get<int>("hasPrimaryKey")<=1){
|
||||
if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
static const std::string sql="select * from " + tableName + " where [[primaryKeyName]] = {%(rdbms=="postgresql"?"$1":"?")%}";
|
||||
<%c++}else{%>
|
||||
static const std::string sql="";
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
%>
|
||||
static const std::string sql="select * from " + tableName + " where <%c++
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
$$<<pkName[i]<<" = $"<<i+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<pkName[i]<<" = ?";
|
||||
}
|
||||
if(i<(pkName.size()-1))
|
||||
$$<<" and ";
|
||||
}
|
||||
$$<<"\";\n";
|
||||
}
|
||||
%>
|
||||
return sql;
|
||||
}
|
||||
|
||||
static const std::string &sqlForDeletingByPrimaryKey()
|
||||
{
|
||||
<%c++
|
||||
if(@@.get<int>("hasPrimaryKey")<=1){
|
||||
if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
static const std::string sql="delete from " + tableName + " where [[primaryKeyName]] = {%(rdbms=="postgresql"?"$1":"?")%}";
|
||||
<%c++}else{%>
|
||||
static const std::string sql="";
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
%>
|
||||
static const std::string sql="delete from " + tableName + " where <%c++
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
$$<<pkName[i]<<" = $"<<i+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<pkName[i]<<" = ?";
|
||||
}
|
||||
if(i<(pkName.size()-1))
|
||||
$$<<" and ";
|
||||
}
|
||||
$$<<"\";\n";
|
||||
}
|
||||
%>
|
||||
return sql;
|
||||
}
|
||||
std::string sqlForInserting(bool &needSelection) const
|
||||
{
|
||||
std::string sql="insert into " + tableName + " (";
|
||||
size_t parametersCount = 0;
|
||||
needSelection = false;
|
||||
<%c++
|
||||
bool selFlag=false;
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
if(cols[i].isAutoVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")=="sqlite3")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(@@.get<int>("hasPrimaryKey")>0)
|
||||
{
|
||||
selFlag = true;
|
||||
}
|
||||
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
|
||||
$$<<" ++parametersCount;\n";
|
||||
continue;
|
||||
}
|
||||
if(cols[i].colType_.empty())
|
||||
continue;
|
||||
if(cols[i].hasDefaultVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
|
||||
$$<<" ++parametersCount;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
sql += "{%cols[i].colName_%},";
|
||||
++parametersCount;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
if(@@.get<int>("hasPrimaryKey")>0||@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
if(!dirtyFlag_[{%i%}])
|
||||
{
|
||||
needSelection=true;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
sql += "{%cols[i].colName_%},";
|
||||
++parametersCount;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
if(selFlag)
|
||||
{
|
||||
$$<<" needSelection=true;\n";
|
||||
}
|
||||
%>
|
||||
if(parametersCount > 0)
|
||||
{
|
||||
sql[sql.length()-1]=')';
|
||||
sql += " values (";
|
||||
}
|
||||
else
|
||||
sql += ") values (";
|
||||
|
||||
<%c++
|
||||
if(@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
int placeholder=1;
|
||||
char placeholderStr[64];
|
||||
size_t n=0;
|
||||
<%c++
|
||||
}
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
if(cols[i].isAutoVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
%>
|
||||
sql +="default,";
|
||||
<%c++
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(cols[i].colType_.empty())
|
||||
continue;
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
<%c++
|
||||
if(@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
n = snprintf(placeholderStr,sizeof(placeholderStr),"$%d,",placeholder++);
|
||||
sql.append(placeholderStr, n);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
%>
|
||||
sql.append("?,");
|
||||
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
}
|
||||
<%c++
|
||||
if(cols[i].hasDefaultVal_&&@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
%>
|
||||
else
|
||||
{
|
||||
sql +="default,";
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
if(parametersCount > 0)
|
||||
{
|
||||
sql.resize(sql.length() - 1);
|
||||
}
|
||||
<%c++
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
%>
|
||||
if(needSelection)
|
||||
{
|
||||
sql.append(") returning *");
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.append(1, ')');
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<" sql.append(1, ')');\n";
|
||||
}
|
||||
%>
|
||||
LOG_TRACE << sql;
|
||||
return sql;
|
||||
}
|
||||
};
|
||||
<%c++
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"} // namespace "<<schema<<"\n";
|
||||
}
|
||||
%>
|
||||
} // namespace [[dbName]]
|
||||
} // namespace drogon_model
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
//rdbms: server type, postgresql,mysql or sqlite3
|
||||
"rdbms": "postgresql",
|
||||
//filename: sqlite3 db file name
|
||||
//"filename":"",
|
||||
//host: server address,localhost by default;
|
||||
"host": "127.0.0.1",
|
||||
//port: server port, 5432 by default;
|
||||
"port": 5432,
|
||||
//dbname: Database name;
|
||||
"dbname": "",
|
||||
//schema: valid for postgreSQL, "public" by default;
|
||||
"schema": "public",
|
||||
//user: User name
|
||||
"user": "",
|
||||
//password or passwd: Password
|
||||
"password": "",
|
||||
//client_encoding: The character set used by drogon_ctl. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
|
||||
"tables": [],
|
||||
//convert: the value can be changed by a function call before it is stored into database or
|
||||
//after it is read from database
|
||||
"convert": {
|
||||
"enabled": false,
|
||||
"items":[{
|
||||
"table": "user",
|
||||
"column": "password",
|
||||
"method": {
|
||||
//after_db_read: name of the method which is called after reading from database, signature: void([const] std::shared_ptr [&])
|
||||
"after_db_read": "decrypt_password",
|
||||
//before_db_write: name of the method which is called before writing to database, signature: void([const] std::shared_ptr [&])
|
||||
"before_db_write": "encrypt_password"
|
||||
},
|
||||
"includes": [
|
||||
"\"file_local_search_path.h\"","<file_in_global_search_path.h>"
|
||||
]
|
||||
}]
|
||||
},
|
||||
"relationships": {
|
||||
"enabled": false,
|
||||
"items": [{
|
||||
"type": "has one",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "skus",
|
||||
"target_table_alias": "SKU",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "has many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "reviews",
|
||||
"target_table_alias": "",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "many to many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "",
|
||||
"original_key": "id",
|
||||
"pivot_table": {
|
||||
"table_name": "carts_products",
|
||||
"original_key": "product_id",
|
||||
"target_key": "cart_id"
|
||||
},
|
||||
"target_table_name": "carts",
|
||||
"target_table_alias": "",
|
||||
"target_key": "id",
|
||||
"enable_reverse": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"restful_api_controllers": {
|
||||
"enabled": false,
|
||||
// resource_uri: The URI to access the resource, the default value
|
||||
// is '/*' in which the asterisk represents the table name.
|
||||
// If this option is set to a empty string, the URI is composed of the namespaces and the class name.
|
||||
"resource_uri": "/*",
|
||||
// class_name: "Restful*Ctrl" by default, the asterisk represents the table name.
|
||||
// This option can contain namespaces.
|
||||
"class_name": "Restful*Ctrl",
|
||||
// filters: an array of filter names.
|
||||
"filters": [],
|
||||
// db_client: the database client used by the controller. this option must be consistent with
|
||||
// the configuration of the application.
|
||||
"db_client": {
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//is_fast:
|
||||
"is_fast": false
|
||||
},
|
||||
// directory: The directory where the controller source files are stored.
|
||||
"directory": "controllers",
|
||||
// generate_base_only: false by default. Set to true to avoid overwriting custom subclasses.
|
||||
"generate_base_only": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
*
|
||||
* [[filename]].cc
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[filename]].h"
|
||||
|
||||
using namespace drogon;
|
||||
<%c++auto namespaceStr=@@.get<std::string>("namespaceString");
|
||||
if(!namespaceStr.empty())
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
%>
|
||||
|
||||
void [[className]]::initAndStart(const Json::Value &config)
|
||||
{
|
||||
/// Initialize and start the plugin
|
||||
}
|
||||
|
||||
void [[className]]::shutdown()
|
||||
{
|
||||
/// Shutdown the plugin
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
* [[filename]].h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/plugins/Plugin.h>
|
||||
<%c++
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
if(namespaceVector.empty())
|
||||
$$<<"\n";
|
||||
for(auto &namespaceName:namespaceVector){%>
|
||||
namespace {%namespaceName%}
|
||||
|
||||
{
|
||||
<%c++}
|
||||
$$<<"\n";%>
|
||||
class [[className]] : public drogon::Plugin<[[className]]>
|
||||
{
|
||||
public:
|
||||
[[className]]() {}
|
||||
/// This method must be called by drogon to initialize and start the plugin.
|
||||
/// It must be implemented by the user.
|
||||
void initAndStart(const Json::Value &config) override;
|
||||
|
||||
/// This method must be called by drogon to shutdown the plugin.
|
||||
/// It must be implemented by the user.
|
||||
void shutdown() override;
|
||||
};
|
||||
|
||||
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>
|
||||
}
|
||||
<%c++}%>
|
||||
@@ -0,0 +1,494 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
|
||||
/**
|
||||
*
|
||||
* [[fileName]]Base.cc
|
||||
* DO NOT EDIT. This file is generated by drogon_ctl automatically.
|
||||
* Users should implement business logic in the derived class.
|
||||
*/
|
||||
|
||||
#include "[[fileName]]Base.h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
<%c++
|
||||
if(hasPrimaryKey)
|
||||
{%>
|
||||
void [[className]]Base::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.findByPrimaryKey(
|
||||
id,
|
||||
[req, callbackPtr, this]({%modelName%} r) {
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(makeJson(req, r)));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
const drogon::orm::UnexpectedRows *s=dynamic_cast<const drogon::orm::UnexpectedRows *>(&e.base());
|
||||
if(s)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
return;
|
||||
}
|
||||
LOG_ERROR<<e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void [[className]]Base::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
auto jsonPtr=req->jsonObject();
|
||||
if(!jsonPtr)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No json object is found in the request";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
{%modelName%} object;
|
||||
std::string err;
|
||||
if(!doCustomValidations(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if(isMasquerading())
|
||||
{
|
||||
if(!{%modelName%}::validateMasqueradedJsonForUpdate(*jsonPtr, masqueradingVector(), err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
object.updateByMasqueradedJson(*jsonPtr, masqueradingVector());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!{%modelName%}::validateJsonForUpdate(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
object.updateByJson(*jsonPtr);
|
||||
}
|
||||
}
|
||||
catch(const Json::Exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"]="Field type error";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
if(object.getPrimaryKey() != id)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="Bad primary key";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
|
||||
mapper.update(
|
||||
object,
|
||||
[callbackPtr](const size_t count)
|
||||
{
|
||||
if(count == 1)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k202Accepted);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else if(count == 0)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No resources are updated";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL << "More than one resource is updated: " << count;
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void [[className]]Base::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.deleteByPrimaryKey(
|
||||
id,
|
||||
[callbackPtr](const size_t count) {
|
||||
if(count == 1)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k204NoContent);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else if(count == 0)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = "No resources deleted";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL << "Delete more than one records: " << count;
|
||||
Json::Value ret;
|
||||
ret["error"] = "Database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
<%c++}%>
|
||||
|
||||
void [[className]]Base::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
auto dbClientPtr = getDbClient();
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
auto ¶meters = req->parameters();
|
||||
auto iter = parameters.find("sort");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
auto sortFields = drogon::utils::splitString(iter->second, ",");
|
||||
for(auto &field : sortFields)
|
||||
{
|
||||
if(field.empty())
|
||||
continue;
|
||||
if(field[0] == '+')
|
||||
{
|
||||
field = field.substr(1);
|
||||
mapper.orderBy(field, SortOrder::ASC);
|
||||
}
|
||||
else if(field[0] == '-')
|
||||
{
|
||||
field = field.substr(1);
|
||||
mapper.orderBy(field, SortOrder::DESC);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.orderBy(field, SortOrder::ASC);
|
||||
}
|
||||
}
|
||||
}
|
||||
iter = parameters.find("offset");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
try{
|
||||
auto offset = std::stoll(iter->second);
|
||||
mapper.offset(offset);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
iter = parameters.find("limit");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
try{
|
||||
auto limit = std::stoll(iter->second);
|
||||
mapper.limit(limit);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
auto jsonPtr = req->jsonObject();
|
||||
if(jsonPtr && jsonPtr->isMember("filter"))
|
||||
{
|
||||
try{
|
||||
auto criteria = makeCriteria((*jsonPtr)["filter"]);
|
||||
mapper.findBy(criteria,
|
||||
[req, callbackPtr, this](const std::vector<{%modelName%}> &v) {
|
||||
Json::Value ret;
|
||||
ret.resize(0);
|
||||
for (auto &obj : v)
|
||||
{
|
||||
ret.append(makeJson(req, obj));
|
||||
}
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"] = e.what();
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
(*callbackPtr)(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.findAll([req, callbackPtr, this](const std::vector<{%modelName%}> &v) {
|
||||
Json::Value ret;
|
||||
ret.resize(0);
|
||||
for (auto &obj : v)
|
||||
{
|
||||
ret.append(makeJson(req, obj));
|
||||
}
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void [[className]]Base::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
auto jsonPtr=req->jsonObject();
|
||||
if(!jsonPtr)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No json object is found in the request";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
std::string err;
|
||||
if(!doCustomValidations(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
if(isMasquerading())
|
||||
{
|
||||
if(!{%modelName%}::validateMasqueradedJsonForCreation(*jsonPtr, masqueradingVector(), err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!{%modelName%}::validateJsonForCreation(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
{%modelName%} object =
|
||||
(isMasquerading()?
|
||||
{%modelName%}(*jsonPtr, masqueradingVector()) :
|
||||
{%modelName%}(*jsonPtr));
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.insert(
|
||||
object,
|
||||
[req, callbackPtr, this]({%modelName%} newObject){
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(
|
||||
makeJson(req, newObject)));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e){
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
catch(const Json::Exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"]="Field type error";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void [[className]]Base::update(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
[[className]]Base::[[className]]Base()
|
||||
: RestfulController({
|
||||
<%c++
|
||||
tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
const auto &cols=tableInfo.get<std::vector<ColumnInfo>>("columns");
|
||||
for(size_t i=0; i<cols.size(); ++i)
|
||||
{
|
||||
auto &col = cols[i];
|
||||
if(i < (cols.size()-1))
|
||||
{
|
||||
%>
|
||||
"{%col.colName_%}",
|
||||
<%c++
|
||||
}else{
|
||||
%>
|
||||
"{%col.colName_%}"
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
})
|
||||
{
|
||||
/**
|
||||
* The items in the vector are aliases of column names in the table.
|
||||
* if one item is set to an empty string, the related column is not sent
|
||||
* to clients.
|
||||
*/
|
||||
enableMasquerading({
|
||||
<%c++
|
||||
for(size_t i=0; i<cols.size(); ++i)
|
||||
{
|
||||
auto &col = cols[i];
|
||||
if(i < (cols.size()-1))
|
||||
{
|
||||
%>
|
||||
"{%col.colName_%}", // the alias for the {%col.colName_%} column.
|
||||
<%c++
|
||||
}else{
|
||||
%>
|
||||
"{%col.colName_%}" // the alias for the {%col.colName_%} column.
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]]Base.h
|
||||
* DO NOT EDIT. This file is generated by drogon_ctl automatically.
|
||||
* Users should implement business logic in the derived class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
#include <drogon/orm/RestfulController.h>
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
$$<<"#include \""<<modelName<<".h\"\n";
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
$$<<"using namespace drogon;\n";
|
||||
$$<<"using namespace drogon::orm;\n";
|
||||
|
||||
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
|
||||
auto &schema=tableInfo.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"::"<<schema<<";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<";\n";
|
||||
}
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++}%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command.
|
||||
* this class is a restful API controller for reading and writing the [[tableName]] table.
|
||||
*/
|
||||
|
||||
class [[className]]Base : public RestfulController
|
||||
{
|
||||
public:
|
||||
<%c++if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
<%c++}
|
||||
%>
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
|
||||
// void update(const HttpRequestPtr &req,
|
||||
// std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
orm::DbClientPtr getDbClient()
|
||||
{
|
||||
return drogon::app().get{%(@@.get<bool>("isFastDbClient")?"Fast":"")%}DbClient(dbClientName_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Ensure that subclasses inherited from this class are instantiated.
|
||||
[[className]]Base();
|
||||
const std::string dbClientName_{"[[dbClientName]]"};
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
*
|
||||
* [[fileName]].cc
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[fileName]].h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
|
||||
void [[className]]::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
||||
|
||||
void [[className]]::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
}
|
||||
void [[className]]::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
}
|
||||
void [[className]]::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
void [[className]]::update(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
void [[className]]::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
*
|
||||
* [[fileName]].cc
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[fileName]].h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
|
||||
<%c++
|
||||
if(hasPrimaryKey)
|
||||
{%>
|
||||
void [[className]]::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::getOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
|
||||
|
||||
void [[className]]::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::updateOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
|
||||
|
||||
void [[className]]::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::deleteOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
<%c++}%>
|
||||
|
||||
void [[className]]::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
[[className]]Base::get(req, std::move(callback));
|
||||
}
|
||||
|
||||
void [[className]]::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
[[className]]Base::create(req, std::move(callback));
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]].h
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
#include "[[className]]Base.h"
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
$$<<"#include \""<<modelName<<".h\"\n";
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
$$<<"using namespace drogon;\n";
|
||||
|
||||
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
|
||||
auto &schema=tableInfo.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"::"<<schema<<";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<";\n";
|
||||
}
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++}%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command.
|
||||
* this class is a restful API controller for reading and writing the [[tableName]] table.
|
||||
*/
|
||||
|
||||
class [[className]]: public drogon::HttpController<[[className]]>, public [[className]]Base
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
<%c++
|
||||
auto resource=@@.get<std::string>("resource");
|
||||
if(resource.empty())
|
||||
{
|
||||
if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
METHOD_ADD([[className]]::getOne,"/{1}",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::updateOne,"/{1}",Put,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::deleteOne,"/{1}",Delete,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_ADD([[className]]::get,"",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::create,"",Post,Options[[filters]]);
|
||||
//METHOD_ADD([[className]]::update,"",Put,Options[[filters]]);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
ADD_METHOD_TO([[className]]::getOne,"{%resource%}/{1}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::updateOne,"{%resource%}/{1}",Put,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::deleteOne,"{%resource%}/{1}",Delete,Options[[filters]]);
|
||||
<%c++}%>
|
||||
ADD_METHOD_TO([[className]]::get,"{%resource%}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::create,"{%resource%}",Post,Options[[filters]]);
|
||||
//ADD_METHOD_TO([[className]]::update,"{%resource%}",Put,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_LIST_END
|
||||
|
||||
<%c++if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
<%c++}
|
||||
%>
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
||||
@@ -0,0 +1,80 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]].h
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
<%c++
|
||||
$$<<"using namespace drogon;\n";
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command ([[ctlCommand]]).
|
||||
* this class is a restful API controller.
|
||||
*/
|
||||
class [[className]]: public drogon::HttpController<[[className]]>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
// use METHOD_ADD to add your custom processing function here;
|
||||
<%c++
|
||||
auto resource=@@.get<std::string>("resource");
|
||||
if(resource.empty())
|
||||
{
|
||||
%>
|
||||
METHOD_ADD([[className]]::getOne,"/{1}",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::get,"",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::create,"",Post,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::updateOne,"/{1}",Put,Options[[filters]]);
|
||||
//METHOD_ADD([[className]]::update,"",Put,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::deleteOne,"/{1}",Delete,Options[[filters]]);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
%>
|
||||
ADD_METHOD_TO([[className]]::getOne,"{%resource%}/{1}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::updateOne,"{%resource%}/{1}",Put,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::deleteOne,"{%resource%}/{1}",Delete,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::get,"{%resource%}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::create,"{%resource%}",Post,Options[[filters]]);
|
||||
//ADD_METHOD_TO([[className]]::update,"{%resource%}",Put,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_LIST_END
|
||||
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
// void update(const HttpRequestPtr &req,
|
||||
// std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
||||
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project([[ProjectName]]_test CXX)
|
||||
|
||||
add_executable(${PROJECT_NAME} test_main.cc)
|
||||
|
||||
# ##############################################################################
|
||||
# If you include the drogon source code locally in your project, use this method
|
||||
# to add drogon
|
||||
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
#
|
||||
# and comment out the following lines
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
||||
|
||||
ParseAndAddDrogonTests(${PROJECT_NAME})
|
||||
@@ -0,0 +1,32 @@
|
||||
#define DROGON_TEST_MAIN
|
||||
#include <drogon/drogon_test.h>
|
||||
#include <drogon/drogon.h>
|
||||
|
||||
DROGON_TEST(BasicTest)
|
||||
{
|
||||
// Add your tests here
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace drogon;
|
||||
|
||||
std::promise<void> p1;
|
||||
std::future<void> f1 = p1.get_future();
|
||||
|
||||
// Start the main loop on another thread
|
||||
std::thread thr([&]() {
|
||||
// Queues the promise to be fulfilled after starting the loop
|
||||
app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });
|
||||
app().run();
|
||||
});
|
||||
|
||||
// The future is only satisfied after the event loop started
|
||||
f1.get();
|
||||
int status = test::run(argc, argv);
|
||||
|
||||
// Ask the event loop to shutdown and wait
|
||||
app().getLoop()->queueInLoop([]() { app().quit(); });
|
||||
thr.join();
|
||||
return status;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
*
|
||||
* version.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "version.h"
|
||||
#include <drogon/config.h>
|
||||
#include <drogon/version.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <trantor/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
static const char banner[] =
|
||||
R"( _
|
||||
__| |_ __ ___ __ _ ___ _ __
|
||||
/ _` | '__/ _ \ / _` |/ _ \| '_ \
|
||||
| (_| | | | (_) | (_| | (_) | | | |
|
||||
\__,_|_| \___/ \__, |\___/|_| |_|
|
||||
|___/
|
||||
)";
|
||||
|
||||
void version::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
const auto tlsBackend = trantor::utils::tlsBackend();
|
||||
const bool tlsSupported = drogon::utils::supportsTls();
|
||||
std::cout << banner << std::endl;
|
||||
std::cout << "A utility for drogon" << std::endl;
|
||||
std::cout << "Version: " << DROGON_VERSION << std::endl;
|
||||
std::cout << "Git commit: " << DROGON_VERSION_SHA1 << std::endl;
|
||||
std::cout << "Compilation: \n Compiler: " << COMPILER_COMMAND
|
||||
<< "\n Compiler ID: " << COMPILER_ID
|
||||
<< "\n Compilation flags: " << COMPILATION_FLAGS
|
||||
<< INCLUDING_DIRS << std::endl;
|
||||
std::cout << "Libraries: \n postgresql: "
|
||||
<< (USE_POSTGRESQL ? "yes" : "no") << " (pipeline mode: "
|
||||
<< (LIBPQ_SUPPORTS_BATCH_MODE ? "yes)\n" : "no)\n")
|
||||
<< " mariadb: " << (USE_MYSQL ? "yes\n" : "no\n")
|
||||
<< " sqlite3: " << (USE_SQLITE3 ? "yes\n" : "no\n");
|
||||
std::cout << " ssl/tls backend: " << tlsBackend << "\n";
|
||||
#ifdef USE_BROTLI
|
||||
std::cout << " brotli: yes\n";
|
||||
#else
|
||||
std::cout << " brotli: no\n";
|
||||
#endif
|
||||
#ifdef USE_REDIS
|
||||
std::cout << " hiredis: yes\n";
|
||||
#else
|
||||
std::cout << " hiredis: no\n";
|
||||
#endif
|
||||
std::cout << " c-ares: "
|
||||
<< (trantor::Resolver::isCAresUsed() ? "yes\n" : "no\n");
|
||||
#ifdef HAS_YAML_CPP
|
||||
std::cout << " yaml-cpp: yes\n";
|
||||
#else
|
||||
std::cout << " yaml-cpp: no\n";
|
||||
#endif
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
*
|
||||
* version.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class version : public DrObject<version>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "display version of this tool";
|
||||
}
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
version()
|
||||
{
|
||||
}
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
Reference in New Issue
Block a user