add example for db

This commit is contained in:
jie 2021-11-26 20:29:10 +08:00
parent 4cca3e4a7c
commit 811888ba7d
8 changed files with 393 additions and 1 deletions

View File

@ -62,6 +62,15 @@ namespace bserv::utils {
namespace file { 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::string read_bin(const std::string& filename);
std::nullopt_t serve( std::nullopt_t serve(

View File

@ -1,5 +1,6 @@
#include "pch.h" #include "pch.h"
#include "bserv/utils.hpp" #include "bserv/utils.hpp"
#include "bserv/router.hpp"
#include <mutex> #include <mutex>
#include <sstream> #include <sstream>
@ -238,6 +239,7 @@ namespace bserv::utils {
std::string read_bin(const std::string& filename) { std::string read_bin(const std::string& filename) {
std::ifstream fin(filename, std::ios_base::in | std::ios_base::binary); std::ifstream fin(filename, std::ios_base::in | std::ios_base::binary);
if (!fin.is_open()) throw file_not_found{ filename };
std::string res; std::string res;
while (true) { while (true) {
char c = (char)fin.get(); char c = (char)fin.get();
@ -285,7 +287,12 @@ namespace bserv::utils {
response_type& response, response_type& response,
const std::string& filename) { const std::string& filename) {
response.set(bserv::http::field::content_type, mime_type(filename)); response.set(bserv::http::field::content_type, mime_type(filename));
try {
response.body() = read_bin(filename); response.body() = read_bin(filename);
}
catch (const file_not_found&) {
throw url_not_found_exception{};
}
response.prepare_payload(); response.prepare_payload();
return std::nullopt; return std::nullopt;
} }

148
examples/DB.vcxproj Normal file
View File

@ -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>

View File

@ -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>

4
examples/DB.vcxproj.user Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@ -15,6 +15,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Routing", "Routing.vcxproj"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bserv", "..\bserv\bserv.vcxproj", "{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bserv", "..\bserv\bserv.vcxproj", "{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 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|x64.Build.0 = Release|x64
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x86.ActiveCfg = Release|Win32 {F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x86.ActiveCfg = Release|Win32
{F5C0CF6D-7BF9-40A5-AF2E-8FC36A1D7296}.Release|x86.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

178
examples/db.cpp Normal file
View File

@ -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)
} };
}

11
examples/db.sql Normal file
View File

@ -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)
);