add example for db
This commit is contained in:
parent
4cca3e4a7c
commit
811888ba7d
|
@ -62,6 +62,15 @@ namespace bserv::utils {
|
|||
|
||||
namespace file {
|
||||
|
||||
class file_not_found : public std::exception {
|
||||
private:
|
||||
std::string msg_;
|
||||
public:
|
||||
file_not_found(const std::string& filename)
|
||||
: msg_{ std::string{ "'" } + filename + "' does not exist" } {}
|
||||
const char* what() const { return msg_.c_str(); }
|
||||
};
|
||||
|
||||
std::string read_bin(const std::string& filename);
|
||||
|
||||
std::nullopt_t serve(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "pch.h"
|
||||
#include "bserv/utils.hpp"
|
||||
#include "bserv/router.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
@ -238,6 +239,7 @@ namespace bserv::utils {
|
|||
|
||||
std::string read_bin(const std::string& filename) {
|
||||
std::ifstream fin(filename, std::ios_base::in | std::ios_base::binary);
|
||||
if (!fin.is_open()) throw file_not_found{ filename };
|
||||
std::string res;
|
||||
while (true) {
|
||||
char c = (char)fin.get();
|
||||
|
@ -285,7 +287,12 @@ namespace bserv::utils {
|
|||
response_type& response,
|
||||
const std::string& filename) {
|
||||
response.set(bserv::http::field::content_type, mime_type(filename));
|
||||
response.body() = read_bin(filename);
|
||||
try {
|
||||
response.body() = read_bin(filename);
|
||||
}
|
||||
catch (const file_not_found&) {
|
||||
throw url_not_found_exception{};
|
||||
}
|
||||
response.prepare_payload();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{9c6ee570-c51b-46b0-aa25-8aa1752ca68a}</ProjectGuid>
|
||||
<RootNamespace>DB</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\bserv-debug-x64.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="db.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="db.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
|
@ -15,6 +15,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Routing", "Routing.vcxproj"
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bserv", "..\bserv\bserv.vcxproj", "{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DB", "DB.vcxproj", "{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296} = {F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
|
@ -47,6 +52,14 @@ Global
|
|||
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x64.Build.0 = Release|x64
|
||||
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x86.Build.0 = Release|Win32
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Debug|x64.Build.0 = Debug|x64
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Debug|x86.Build.0 = Debug|Win32
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Release|x64.ActiveCfg = Release|x64
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Release|x64.Build.0 = Release|x64
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9C6EE570-C51B-46B0-AA25-8AA1752CA68A}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
#include <bserv/common.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
bserv::db_relation_to_object orm_user{
|
||||
bserv::make_db_field<int>("id"),
|
||||
bserv::make_db_field<std::string>("username"),
|
||||
bserv::make_db_field<std::string>("password"),
|
||||
bserv::make_db_field<bool>("is_active"),
|
||||
bserv::make_db_field<bool>("is_superuser"),
|
||||
bserv::make_db_field<std::optional<std::string>>("first_name"),
|
||||
bserv::make_db_field<std::optional<std::string>>("last_name"),
|
||||
bserv::make_db_field<std::optional<std::string>>("email")
|
||||
};
|
||||
std::optional<boost::json::object> get_user(
|
||||
bserv::db_transaction& tx,
|
||||
const boost::json::value& username) {
|
||||
bserv::db_result r = tx.exec(
|
||||
"select * from ex_auth_user where username = ?;", username);
|
||||
lginfo << r.query();
|
||||
return orm_user.convert_to_optional(r);
|
||||
}
|
||||
std::optional<boost::json::value> get_or_empty(
|
||||
boost::json::object& obj,
|
||||
const std::string& key) {
|
||||
if (!obj.contains(key)) return std::nullopt;
|
||||
return obj[key];
|
||||
}
|
||||
boost::json::object greet(
|
||||
std::shared_ptr<bserv::session_type> session_ptr) {
|
||||
bserv::session_type& session = *session_ptr;
|
||||
if (session.count("user")) {
|
||||
boost::json::object& user = session["user"].as_object();
|
||||
boost::json::object obj;
|
||||
// the first way to check non-null (!= nullptr)
|
||||
if (user["first_name"] != nullptr && user["last_name"] != nullptr) {
|
||||
obj["welcome"] = std::string{ user["first_name"].as_string() }
|
||||
+ ' ' + std::string{ user["last_name"].as_string() };
|
||||
}
|
||||
else obj["welcome"] = user["username"];
|
||||
// the second way (!is_null())
|
||||
if (!user["email"].is_null()) {
|
||||
obj["email"] = user["email"];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
else return { {"hello", "world"} };
|
||||
}
|
||||
boost::json::object user_register(
|
||||
bserv::request_type& request,
|
||||
boost::json::object&& params,
|
||||
std::shared_ptr<bserv::db_connection> conn) {
|
||||
if (request.method() != boost::beast::http::verb::post)
|
||||
throw bserv::url_not_found_exception{};
|
||||
if (!params.contains("username")) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "`username` is required"}
|
||||
};
|
||||
}
|
||||
if (!params.contains("password")) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "`password` is required"}
|
||||
};
|
||||
}
|
||||
auto username = params["username"];
|
||||
bserv::db_transaction tx{ conn };
|
||||
auto opt_user = get_user(tx, username);
|
||||
if (opt_user) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "`username` existed"}
|
||||
};
|
||||
}
|
||||
auto password = params["password"].as_string();
|
||||
bserv::db_result r = tx.exec(
|
||||
"insert into ex_auth_user "
|
||||
"(username, password, is_active, is_superuser, "
|
||||
"first_name, last_name, email) values "
|
||||
"(?, ?, ?, ?, ?, ?, ?);",
|
||||
username,
|
||||
bserv::utils::security::encode_password(
|
||||
password.c_str()),
|
||||
params.contains("is_active") ? params["is_active"] : true,
|
||||
params.contains("is_superuser") ? params["is_superuser"] : false,
|
||||
get_or_empty(params, "first_name"),
|
||||
get_or_empty(params, "last_name"),
|
||||
get_or_empty(params, "email"));
|
||||
lginfo << r.query();
|
||||
tx.commit(); // commit must be done explicitly
|
||||
return {
|
||||
{"success", true},
|
||||
{"message", "user registered"}
|
||||
};
|
||||
}
|
||||
boost::json::object user_login(
|
||||
bserv::request_type& request,
|
||||
boost::json::object&& params,
|
||||
std::shared_ptr<bserv::db_connection> conn,
|
||||
std::shared_ptr<bserv::session_type> session_ptr) {
|
||||
if (request.method() != boost::beast::http::verb::post)
|
||||
throw bserv::url_not_found_exception{};
|
||||
if (!params.contains("username")) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "`username` is required"}
|
||||
};
|
||||
}
|
||||
if (!params.contains("password")) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "`password` is required"}
|
||||
};
|
||||
}
|
||||
auto username = params["username"];
|
||||
bserv::db_transaction tx{ conn };
|
||||
auto opt_user = get_user(tx, username);
|
||||
if (!opt_user) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "invalid username/password"}
|
||||
};
|
||||
}
|
||||
auto& user = opt_user.value();
|
||||
if (!user["is_active"].as_bool()) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "invalid username/password"}
|
||||
};
|
||||
}
|
||||
auto password = params["password"].as_string();
|
||||
auto encoded_password = user["password"].as_string();
|
||||
if (!bserv::utils::security::check_password(
|
||||
password.c_str(), encoded_password.c_str())) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"message", "invalid username/password"}
|
||||
};
|
||||
}
|
||||
bserv::session_type& session = *session_ptr;
|
||||
session["user"] = user;
|
||||
return {
|
||||
{"success", true},
|
||||
{"message", "login successfully"}
|
||||
};
|
||||
}
|
||||
boost::json::object user_logout(
|
||||
std::shared_ptr<bserv::session_type> session_ptr) {
|
||||
bserv::session_type& session = *session_ptr;
|
||||
if (session.count("user"))
|
||||
session.erase("user");
|
||||
return {
|
||||
{"success", true},
|
||||
{"message", "logout successfully"}
|
||||
};
|
||||
}
|
||||
int main() {
|
||||
std::string config_content = bserv::utils::file::read_bin("../config.json");
|
||||
boost::json::object config_obj = boost::json::parse(config_content).as_object();
|
||||
bserv::server_config config;
|
||||
config.set_db_conn_str(config_obj["conn-str"].as_string().c_str());
|
||||
bserv::server{ config, {
|
||||
bserv::make_path("/greet", &greet,
|
||||
bserv::placeholders::session),
|
||||
bserv::make_path("/register", &user_register,
|
||||
bserv::placeholders::request,
|
||||
bserv::placeholders::json_params,
|
||||
bserv::placeholders::db_connection_ptr),
|
||||
bserv::make_path("/login", &user_login,
|
||||
bserv::placeholders::request,
|
||||
bserv::placeholders::json_params,
|
||||
bserv::placeholders::db_connection_ptr,
|
||||
bserv::placeholders::session),
|
||||
bserv::make_path("/logout", &user_logout,
|
||||
bserv::placeholders::session)
|
||||
} };
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
DROP TABLE IF EXISTS ex_auth_user;
|
||||
CREATE TABLE ex_auth_user (
|
||||
id serial PRIMARY KEY,
|
||||
username character varying(255) NOT NULL UNIQUE,
|
||||
password character varying(255) NOT NULL,
|
||||
is_active boolean NOT NULL,
|
||||
is_superuser boolean NOT NULL,
|
||||
first_name character varying(255),
|
||||
last_name character varying(255),
|
||||
email character varying(255)
|
||||
);
|
Loading…
Reference in New Issue