use wt dbo.
Some checks failed
Deploy / Build (push) Failing after 3m33s

This commit is contained in:
amass 2024-11-26 22:58:54 +08:00
parent 85b93fbbe3
commit 059d6d3879
21 changed files with 195 additions and 324 deletions

View File

@ -1,14 +1,23 @@
find_package(Wt REQUIRED Dbo)
add_library(Database add_library(Database
Database.h Database.cpp Database.h Database.cpp
Task.h Task.cpp Session.h Session.cpp
HomeBox.h HomeBox.cpp Task.h
User.h
HomeBox.h
) )
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
target_include_directories(Database target_include_directories(Database
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} INTERFACE ${PARENT_DIR}
) )
target_link_libraries(Database target_link_libraries(Database
PUBLIC sqlite3 PUBLIC sqlite3
PUBLIC Universal PUBLIC Universal
PUBLIC Wt::Wt
PUBLIC Wt::Dbo
PRIVATE Wt::DboSqlite3
) )

View File

@ -1,9 +1,29 @@
#include "Database.h" #include "Database.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Session.h"
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3.h>
#include <sqlite3.h> #include <sqlite3.h>
#include <sstream> #include <sstream>
std::unique_ptr<Session> Database::session() {
if (!m_sqlConnectionPool) return {};
return std::make_unique<Session>(*m_sqlConnectionPool);
}
bool Database::open(const std::string &path) { bool Database::open(const std::string &path) {
try {
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(path);
connection->setProperty("show-queries", "true");
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime,
Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
m_sqlConnectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
session()->createTables();
} catch (const Wt::Dbo::Exception &e) {
LOG(error) << e.code() << ": " << e.what();
}
bool ret = true; bool ret = true;
int result = sqlite3_open(path.c_str(), &m_sqlite3); int result = sqlite3_open(path.c_str(), &m_sqlite3);
if (result != SQLITE_OK) { if (result != SQLITE_OK) {
@ -14,97 +34,6 @@ bool Database::open(const std::string &path) {
return ret; return ret;
} }
static int selectTaskCallback(void *data, int argc, char **argv, char **columnName) {
auto tasks = reinterpret_cast<Tasks *>(data);
Task task;
for (int i = 0; i < argc; i++) {
if (argv[i] == nullptr) continue;
if (strcmp(columnName[i], "id") == 0) {
task.id = std::atol(argv[i]);
} else if (strcmp(columnName[i], "create_time") == 0) {
task.createTime = std::atol(argv[i]);
} else if (strcmp(columnName[i], "content") == 0) {
task.content = argv[i];
} else if (strcmp(columnName[i], "comment") == 0) {
task.comment = argv[i];
} else if (strcmp(columnName[i], "finished") == 0) {
task.finished = std::atol(argv[i]);
} else if (strcmp(columnName[i], "parent_id") == 0) {
task.parentId = std::atol(argv[i]);
}
}
tasks->push_back(task);
return 0;
}
Tasks Database::tasks() {
Tasks ret;
char *error = nullptr;
if (sqlite3_exec(m_sqlite3, "select * from tasks", selectTaskCallback, &ret, &error) != SQLITE_OK) {
LOG(error) << "sqlite3_exec() failed: " << error << std::endl;
sqlite3_free(error);
}
std::unordered_map<int, Task *> tasks;
for (auto iterator = ret.begin(); iterator != ret.end();) {
if (iterator->parentId >= 0) {
if (tasks.count(iterator->parentId) > 0) {
auto parentTask = tasks.at(iterator->parentId);
parentTask->children.push_back(*iterator);
tasks.insert({iterator->id, &parentTask->children.back()});
} else {
LOG(warning) << "task`s parent id " << iterator->parentId << " not existed.";
}
iterator = ret.erase(iterator);
} else {
tasks.insert({iterator->id, &(*iterator)});
++iterator;
}
}
return ret;
}
bool Database::addTask(uint64_t createTime, const std::string &content, const std::string &comment, int parentId,
bool finished) {
bool ret = true;
std::ostringstream oss;
oss << "INSERT INTO tasks (create_time,content,comment,parent_id,finished) VALUES (" << createTime << ",\""
<< content << "\",\"" << comment << "\"," << parentId << "," << finished << ");";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
bool Database::removeTask(int id) {
bool ret = true;
std::ostringstream oss;
oss << "DELETE FROM tasks WHERE id = " << id << ";";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
void Database::setTaskFinished(int id, bool finished, uint64_t finishedTime) {
std::ostringstream oss;
oss << "UPDATE tasks SET finished = " << finished << ", finished_time = " << finishedTime << " WHERE id = " << id;
auto sql = oss.str();
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << sqlite3_errmsg(m_sqlite3) << ", sql: " << sql;
return;
}
}
void Database::updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent, void Database::updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent,
int64_t time) { int64_t time) {
@ -278,72 +207,9 @@ VisitAnalysis Database::visitAnalysisData(const std::string &url) {
return ret; return ret;
} }
bool Database::addHomeBoxItem(const std::string &name, const std::string &location, int cost) {
bool ret = true;
std::ostringstream oss;
oss << "INSERT INTO homebox (name,location,cost) VALUES (\"" << name << "\",\"" << location << "\"," << cost
<< ");";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
static int selectHomeBoxItemCallback(void *data, int argc, char **argv, char **columnName) {
auto items = reinterpret_cast<HomeBox::Items *>(data);
HomeBox::Item item;
for (int i = 0; i < argc; i++) {
if (argv[i] == nullptr) continue;
if (strcmp(columnName[i], "id") == 0) {
item.id = std::atol(argv[i]);
} else if (strcmp(columnName[i], "name") == 0) {
item.name = argv[i];
} else if (strcmp(columnName[i], "location") == 0) {
item.location = argv[i];
} else if (strcmp(columnName[i], "cost") == 0) {
item.cost = std::atol(argv[i]);
}
}
items->push_back(item);
return 0;
}
HomeBox::Items Database::homeBoxItems() {
HomeBox::Items ret;
char *error = nullptr;
if (sqlite3_exec(m_sqlite3, "select * from homebox", selectHomeBoxItemCallback, &ret, &error) != SQLITE_OK) {
LOG(error) << "sqlite3_exec() failed: " << error << std::endl;
sqlite3_free(error);
}
return ret;
}
void Database::initialize() { void Database::initialize() {
const char *sql =
"CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY AUTOINCREMENT, create_time INTEGER NOT NULL, "
"parent_id INTEGER, content VARCHAR(512) NOT NULL, comment VARCHAR(512) NOT NULL, finished BOLL, finished_time "
"INTEGER);";
int result = sqlite3_exec(m_sqlite3, sql, NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3);
return;
}
const char *homeBoxSql = "CREATE TABLE IF NOT EXISTS homebox (id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(512) NOT NULL, location VARCHAR(512) NOT NULL, cost INTEGER);";
result = sqlite3_exec(m_sqlite3, homeBoxSql, NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3);
return;
}
char *message = nullptr; char *message = nullptr;
sql = R"( auto sql = R"(
CREATE TABLE IF NOT EXISTS visit_analysis ( CREATE TABLE IF NOT EXISTS visit_analysis (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL, url TEXT NOT NULL,
@ -353,7 +219,7 @@ void Database::initialize() {
page_view_count INTEGER NOT NULL page_view_count INTEGER NOT NULL
); );
)"; )";
result = sqlite3_exec(m_sqlite3, sql, 0, 0, &message); int result = sqlite3_exec(m_sqlite3, sql, 0, 0, &message);
if (result != SQLITE_OK) { if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << message << std::endl; LOG(error) << "Failed to create table: " << message << std::endl;
sqlite3_free(message); sqlite3_free(message);

View File

@ -6,6 +6,12 @@
#include "Task.h" #include "Task.h"
#include <string> #include <string>
namespace Wt {
namespace Dbo {
class SqlConnectionPool;
}
} // namespace Wt
class VisitAnalysis { class VisitAnalysis {
public: public:
std::string url; std::string url;
@ -15,34 +21,28 @@ public:
}; };
struct sqlite3; struct sqlite3;
class Session;
class Database { class Database {
friend class Amass::Singleton<Database>; friend class Amass::Singleton<Database>;
public: public:
~Database(); ~Database();
std::unique_ptr<Session> session();
bool open(const std::string &path); bool open(const std::string &path);
Tasks tasks();
bool addTask(uint64_t createTime, const std::string &content, const std::string &comment = "", int parentId = -1,
bool finished = false);
bool removeTask(int id);
void setTaskFinished(int id, bool finished, uint64_t finishedTime);
void updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent, void updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent, int64_t time);
int64_t time);
void clearVisitRecord(); void clearVisitRecord();
VisitAnalysis visitAnalysisData(const std::string &url); VisitAnalysis visitAnalysisData(const std::string &url);
VisitAnalysis siteVisitAnalysisData(); VisitAnalysis siteVisitAnalysisData();
std::list<VisitAnalysis> mostViewedUrls(int size); std::list<VisitAnalysis> mostViewedUrls(int size);
std::list<VisitAnalysis> latestViewedUrls(int size); std::list<VisitAnalysis> latestViewedUrls(int size);
HomeBox::Items homeBoxItems();
bool addHomeBoxItem(const std::string &name, const std::string &location, int cost);
protected: protected:
void initialize(); void initialize();
private: private:
std::unique_ptr<Wt::Dbo::SqlConnectionPool> m_sqlConnectionPool;
sqlite3 *m_sqlite3 = nullptr; sqlite3 *m_sqlite3 = nullptr;
}; };

View File

@ -1 +0,0 @@
#include "HomeBox.h"

View File

@ -1,20 +1,23 @@
#ifndef __HOMEBOX_H__ #ifndef __HOMEBOX_H__
#define __HOMEBOX_H__ #define __HOMEBOX_H__
#include <list> #include <Wt/Dbo/Dbo.h>
#include <string> #include <string>
class HomeBox { namespace HomeBox {
public:
class Item { class Item {
public: public:
int id = -1;
std::string name; std::string name;
std::string location; std::string location;
int cost; int cost;
};
using Items = std::list<Item>; template <class Action>
void persist(Action &a) {
Wt::Dbo::field(a, name, "name");
Wt::Dbo::field(a, location, "location");
Wt::Dbo::field(a, cost, "cost");
}
}; };
} // namespace HomeBox
#endif // __HOMEBOX_H__ #endif // __HOMEBOX_H__

View File

@ -1,30 +1,17 @@
#include "Session.h" #include "Session.h"
#include "BoostLog.h"
#include <Wt/Auth/Dbo/AuthInfo.h>
#include <Wt/Auth/Dbo/UserDatabase.h> #include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/FacebookService.h>
#include <Wt/Auth/GoogleService.h>
#include <Wt/Auth/HashFunction.h>
#include <Wt/Auth/PasswordService.h>
#include <Wt/Auth/PasswordStrengthValidator.h>
#include <Wt/Auth/PasswordVerifier.h>
#include <Wt/Dbo/backend/Sqlite3.h>
Session::Session(Wt::Dbo::SqlConnectionPool &connectionPool) : m_connectionPool(connectionPool) { Session::Session(Wt::Dbo::SqlConnectionPool &connectionPool) {
setConnectionPool(m_connectionPool); setConnectionPool(connectionPool);
mapClass<Task>("task");
mapClass<HomeBox::Item>("homebox_item");
mapClass<User>("user"); mapClass<User>("user");
mapClass<AuthInfo>("auth_info"); mapClass<AuthInfo>("auth_info");
mapClass<AuthInfo::AuthIdentityType>("auth_identity"); mapClass<AuthInfo::AuthIdentityType>("auth_identity");
mapClass<AuthInfo::AuthTokenType>("auth_token"); mapClass<AuthInfo::AuthTokenType>("auth_token");
try {
createTables();
LOG(info) << "Created database.";
} catch (Wt::Dbo::Exception &e) {
LOG(error) << e.what() << ", using existing database";
}
m_users = std::make_unique<UserDatabase>(*this); m_users = std::make_unique<UserDatabase>(*this);
} }
@ -46,3 +33,7 @@ Wt::Auth::AbstractUserDatabase &Session::users() {
Wt::Auth::Login &Session::login() { Wt::Auth::Login &Session::login() {
return m_login; return m_login;
} }
DBO_INSTANTIATE_TEMPLATES(User)
DBO_INSTANTIATE_TEMPLATES(Task)
DBO_INSTANTIATE_TEMPLATES(HomeBox::Item)

View File

@ -1,6 +1,8 @@
#ifndef __SESSION_H__ #ifndef __SESSION_H__
#define __SESSION_H__ #define __SESSION_H__
#include "HomeBox.h"
#include "Task.h"
#include "User.h" #include "User.h"
#include <Wt/Auth/Login.h> #include <Wt/Auth/Login.h>
#include <Wt/Dbo/Session.h> #include <Wt/Dbo/Session.h>
@ -16,7 +18,6 @@ public:
Wt::Auth::Login &login(); Wt::Auth::Login &login();
private: private:
Wt::Dbo::SqlConnectionPool &m_connectionPool;
std::unique_ptr<UserDatabase> m_users; std::unique_ptr<UserDatabase> m_users;
Wt::Auth::Login m_login; Wt::Auth::Login m_login;
}; };

View File

@ -1,34 +0,0 @@
#include "Task.h"
#include <boost/json/array.hpp>
#include <boost/json/object.hpp>
#include <boost/json/serialize.hpp>
namespace boost {
namespace json {
static boost::json::object serialize(const Task &task) {
boost::json::object ret;
ret["id"] = task.id;
ret["parentId"] = task.parentId;
ret["finished"] = task.finished;
ret["createTime"] = task.createTime;
ret["content"] = task.content;
ret["comment"] = task.comment;
boost::json::array children;
for (auto &child : task.children) {
children.push_back(serialize(child));
}
ret["children"] = std::move(children);
return ret;
}
std::string serialize(const Tasks &tasks) {
boost::json::array ret;
for (auto &task : tasks) {
ret.push_back(serialize(task));
}
return boost::json::serialize(ret);
}
} // namespace json
} // namespace boost

View File

@ -1,31 +1,33 @@
#ifndef __TASK_H__ #ifndef __TASK_H__
#define __TASK_H__ #define __TASK_H__
#include <list> #include <Wt/Dbo/Dbo.h>
#include <string> #include <string>
class Task; class Task;
using Tasks = std::list<Task>; using Tasks = Wt::Dbo::collection<Wt::Dbo::ptr<Task>>;
class Task { class Task {
public: public:
int id = -1;
int parentId = -1;
bool finished = false; bool finished = false;
int32_t createTime = 0; int32_t createTime = 0;
std::string content; std::string content;
std::string comment; std::string comment;
Wt::Dbo::ptr<Task> parent;
Tasks children; Tasks children;
template <class Action> template <class Action>
void persist(Action &a) { void persist(Action &a) {
Wt::Dbo::field(a, content, "content");
Wt::Dbo::field(a, comment, "comment");
Wt::Dbo::field(a, finished, "finished");
Wt::Dbo::field(a, createTime, "create_time");
Wt::Dbo::belongsTo(a, parent, "parent");
Wt::Dbo::hasMany(a, children, Wt::Dbo::ManyToOne, "parent");
} }
}; };
namespace boost {
namespace json {
std::string serialize(const Tasks &tasks);
}
} // namespace boost
#endif // __TASK_H__ #endif // __TASK_H__

View File

@ -1,5 +1,6 @@
#include "Application.h" #include "Application.h"
#include "Database.h" #include "Database/Database.h"
#include "Database/Session.h"
#include "DateTime.h" #include "DateTime.h"
#include "HttpSession.h" #include "HttpSession.h"
#include "IoContext.h" #include "IoContext.h"
@ -8,6 +9,7 @@
#include "ServiceManager.h" #include "ServiceManager.h"
#include "SystemUsage.h" #include "SystemUsage.h"
#include "WeChatContext/CorporationContext.h" #include "WeChatContext/CorporationContext.h"
#include <Wt/Dbo/Json.h>
#include <boost/json/object.hpp> #include <boost/json/object.hpp>
#include <boost/json/serialize.hpp> #include <boost/json/serialize.hpp>
#include <boost/stacktrace.hpp> #include <boost/stacktrace.hpp>
@ -60,22 +62,23 @@ Application::Application(const std::string &path)
m_router->insert("/api/v1/tasklist", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { m_router->insert("/api/v1/tasklist", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
using namespace boost::beast; using namespace boost::beast;
auto database = Amass::Singleton<Database>::instance(); auto database = Amass::Singleton<Database>::instance()->session();
auto tasks = database->tasks(); Tasks tasks = database->find<Task>();
std::ostringstream oss;
Wt::Dbo::jsonSerialize(tasks, oss);
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()}; http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
s.set(http::field::content_type, "application/json;charset=UTF-8"); s.set(http::field::content_type, "application/json;charset=UTF-8");
s.keep_alive(request.keep_alive()); s.keep_alive(request.keep_alive());
s.body() = boost::json::serialize(tasks); s.body() = oss.str();
s.prepare_payload(); s.prepare_payload();
session.reply(std::move(s)); session.reply(std::move(s));
}); });
m_router->insert("/api/v1/task/add", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { m_router->insert("/api/v1/task/add", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) mutable {
using namespace boost::beast; using namespace boost::beast;
LOG(info) << "add task: " << request.body(); LOG(info) << "add task: " << request.body();
auto database = Amass::Singleton<Database>::instance();
auto rootJson = boost::json::parse(request.body()); auto rootJson = boost::json::parse(request.body());
auto &root = rootJson.as_object(); auto &root = rootJson.as_object();
@ -83,10 +86,18 @@ Application::Application(const std::string &path)
if (root.contains("content")) { if (root.contains("content")) {
content = root.at("content").as_string(); content = root.at("content").as_string();
} }
bool ret = database->addTask(root.at("createTime").as_int64(), content, auto database = Amass::Singleton<Database>::instance()->session();
std::string(root.at("comment").as_string()), root.at("parentId").as_int64()); auto task = std::make_unique<Task>();
task->createTime = root.at("createTime").as_int64();
task->content = content;
task->comment = std::string(root.at("comment").as_string());
auto t = database->add(std::move(task));
Wt::Dbo::ptr<Task> parent = database->find<Task>("where id=?").bind(root.at("parentId").as_int64());
if (parent) {
parent.modify()->children.insert(t);
}
boost::json::object reply; boost::json::object reply;
reply["status"] = ret ? 0 : -1; reply["status"] = 0;
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()}; http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
s.set(http::field::content_type, "application/json;charset=UTF-8"); s.set(http::field::content_type, "application/json;charset=UTF-8");
@ -99,11 +110,15 @@ Application::Application(const std::string &path)
m_router->insert("/api/v1/task/delete/{id}", [this](HttpSession &session, const Request &request,const boost::urls::matches &matches) { m_router->insert("/api/v1/task/delete/{id}", [this](HttpSession &session, const Request &request,const boost::urls::matches &matches) {
using namespace boost::beast; using namespace boost::beast;
LOG(info) << "delete task: " << matches.at("id"); LOG(info) << "delete task: " << matches.at("id");
auto database = Amass::Singleton<Database>::instance(); auto database = Amass::Singleton<Database>::instance()->session();
auto status = database->removeTask(std::stoi(matches.at("id"))); Wt::Dbo::ptr<Task> joe = database->find<Task>().where("id = ?").bind(std::stoi(matches.at("id")));
int status = -1;
if (joe) {
joe.remove();
status = 0;
}
boost::json::object reply; boost::json::object reply;
reply["status"] = status ? 0 : -1; reply["status"] = status;
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()}; http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
s.set(http::field::content_type, "application/json;charset=UTF-8"); s.set(http::field::content_type, "application/json;charset=UTF-8");
@ -125,8 +140,7 @@ Application::Application(const std::string &path)
ServiceLogic::make_200<boost::beast::http::string_body>(request, "notify successed.\n", "text/html")); ServiceLogic::make_200<boost::beast::http::string_body>(request, "notify successed.\n", "text/html"));
}); });
// clang-format on // clang-format on
m_router->insert("/api/v1/visit_analysis", [this](HttpSession &session, const Request &request, m_router->insert("/api/v1/visit_analysis", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
const boost::urls::matches &matches) {
using namespace boost::beast; using namespace boost::beast;
auto rootJson = boost::json::parse(request.body()); auto rootJson = boost::json::parse(request.body());
auto &root = rootJson.as_object(); auto &root = rootJson.as_object();
@ -166,8 +180,7 @@ Application::Application(const std::string &path)
session.reply(std::move(s)); session.reply(std::move(s));
}); });
m_router->insert("/api/v1/most_viewed_urls", [this](HttpSession &session, const Request &request, m_router->insert("/api/v1/most_viewed_urls", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
const boost::urls::matches &matches) {
using namespace boost::beast; using namespace boost::beast;
int size = 5; int size = 5;
std::error_code error; std::error_code error;
@ -200,8 +213,7 @@ Application::Application(const std::string &path)
session.reply(std::move(s)); session.reply(std::move(s));
}); });
m_router->insert("/api/v1/latest_viewed_urls", [this](HttpSession &session, const Request &request, m_router->insert("/api/v1/latest_viewed_urls", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) {
const boost::urls::matches &matches) {
using namespace boost::beast; using namespace boost::beast;
int size = 5; int size = 5;
std::error_code error; std::error_code error;
@ -290,21 +302,21 @@ void Application::alarmTask() {
LOG(error) << error.message(); LOG(error) << error.message();
return; return;
} }
auto database = Amass::Singleton<Database>::instance(); auto session = Amass::Singleton<Database>::instance()->session();
auto tasks = database->tasks(); Tasks tasks = session->find<Task>();
bool founded = false; bool founded = false;
std::string content; std::string content;
for (auto &task : tasks) { for (auto &task : tasks) {
if (founded) break; if (founded) break;
for (auto &child : task.children) { for (auto child : task->children) {
if (!child.finished) { if (!child->finished) {
content = child.content; content = child->content;
founded = true; founded = true;
break; break;
} }
} }
if (!founded && !task.finished) { if (!founded && !task->finished) {
content = task.content; content = task->content;
founded = true; founded = true;
} }
} }

View File

@ -1,6 +1,6 @@
#include "Application.h" #include "Application.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Database.h" #include "Database/Database.h"
#include "IoContext.h" #include "IoContext.h"
#include "Listener.h" #include "Listener.h"
#include "Live2dBackend.h" #include "Live2dBackend.h"
@ -11,6 +11,7 @@
#include "WeChatContext/CorporationContext.h" #include "WeChatContext/CorporationContext.h"
#include "WeChatContext/WeChatContext.h" #include "WeChatContext/WeChatContext.h"
#include "WebApplication.h" #include "WebApplication.h"
#include <Wt/Dbo/SqlConnectionPool.h>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>

View File

@ -1,5 +1,7 @@
#include "Database.h" #include "Database/Database.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Database/Session.h"
#include <Wt/Dbo/SqlConnectionPool.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <filesystem> #include <filesystem>
@ -11,19 +13,66 @@ BOOST_AUTO_TEST_CASE(DatabaseTest) {
std::filesystem::remove(path); std::filesystem::remove(path);
} }
Database database; Database database;
BOOST_TEST(database.open(path)); database.open(path);
// BOOST_TEST(database.open(path));
database.addTask(1234, "Hello"); auto session = database.session();
database.addTask(1234, "这是一个测试", "", true); Wt::Dbo ::Transaction transaction(*session);
database.addHomeBoxItem("手机", "抽屉", 1499); auto task = std::make_unique<Task>();
auto items = database.homeBoxItems(); task->comment = "my_comment";
BOOST_CHECK_EQUAL(items.size(), 1); task->content = "my_content";
auto p = session->add(std::move(task));
{
task = std::make_unique<Task>();
task->comment = "my_comment1";
task->content = "my_content1";
auto c = session->add(std::move(task));
p.modify()->children.insert(c);
}
{
task = std::make_unique<Task>();
task->comment = "my_comment2";
task->content = "my_content2";
auto c = session->add(std::move(task));
p.modify()->children.insert(c);
{
task = std::make_unique<Task>();
task->comment = "my_comment3";
task->content = "my_content3";
auto d = session->add(std::move(task));
c.modify()->children.insert(d);
}
{
task = std::make_unique<Task>();
task->comment = "my_comment4";
task->content = "my_content4";
auto d = session->add(std::move(task));
c.modify()->children.insert(d);
}
}
Wt::Dbo::ptr<Task> tt = session->find<Task>("where id = 3");
LOG(info) << tt->parent->content;
LOG(info) << tt->children.size();
{
auto item = std::make_unique<HomeBox::Item>();
item->cost = 1499;
item->location = "抽屉";
item->name = "手机";
auto d = session->add(std::move(item));
}
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now); std::time_t now_time = std::chrono::system_clock::to_time_t(now);
database.setTaskFinished(1, true, now_time);
database.updateVisitCount("/note", "uuid_123", "chrome", now_time); database.updateVisitCount("/note", "uuid_123", "chrome", now_time);
database.updateVisitCount("/note/1", "uuid_1232", "chrome", now_time); database.updateVisitCount("/note/1", "uuid_1232", "chrome", now_time);

View File

@ -3,11 +3,9 @@ find_package(Wt REQUIRED Wt)
add_library(WebApplication add_library(WebApplication
WebApplication.h WebApplication.cpp WebApplication.h WebApplication.cpp
LoginWidget.h LoginWidget.cpp LoginWidget.h LoginWidget.cpp
User.h User.cpp
Hello.h Hello.cpp Hello.h Hello.cpp
Restful.h Restful.cpp Restful.h Restful.cpp
Dialog.h Dialog.cpp Dialog.h Dialog.cpp
Session.h Session.cpp
) )
target_include_directories(WebApplication target_include_directories(WebApplication
@ -16,8 +14,6 @@ target_include_directories(WebApplication
target_link_libraries(WebApplication target_link_libraries(WebApplication
PUBLIC Universal PUBLIC Universal
PRIVATE Wt::Wt
PRIVATE Wt::Dbo
PRIVATE Wt::HTTP PRIVATE Wt::HTTP
PRIVATE Wt::DboSqlite3 PRIVATE Database
) )

View File

@ -1,8 +1,9 @@
#include "Hello.h" #include "Hello.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Database/Database.h"
#include "Database/Session.h"
#include "Dialog.h" #include "Dialog.h"
#include "LoginWidget.h" #include "LoginWidget.h"
#include "Session.h"
#include "WebApplication.h" #include "WebApplication.h"
#include <Wt/Auth/Identity.h> #include <Wt/Auth/Identity.h>
#include <Wt/WBootstrap2Theme.h> #include <Wt/WBootstrap2Theme.h>
@ -12,14 +13,13 @@
#include <Wt/WPushButton.h> #include <Wt/WPushButton.h>
#include <Wt/WText.h> #include <Wt/WText.h>
Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connectionPool, bool embedded) Hello::Hello(const Wt::WEnvironment &env, bool embedded) : Wt::WApplication(env) {
: Wt::WApplication(env) {
messageResourceBundle().use(appRoot() + "wt"); messageResourceBundle().use(appRoot() + "wt");
messageResourceBundle().use(appRoot() + "auth_strings"); messageResourceBundle().use(appRoot() + "auth_strings");
messageResourceBundle().use(appRoot() + "auth_css_theme"); messageResourceBundle().use(appRoot() + "auth_css_theme");
useStyleSheet("/resources/app.css"); useStyleSheet("/resources/app.css");
LOG(info) << "app root: " << appRoot(); LOG(info) << "app root: " << appRoot();
m_session = std::make_unique<Session>(connectionPool); m_session = Amass::Singleton<Database>::instance()->session();
m_session->login().changed().connect(this, &Hello::authEvent); m_session->login().changed().connect(this, &Hello::authEvent);
setTitle("Hello world"); setTitle("Hello world");
if (!embedded) { if (!embedded) {
@ -35,7 +35,6 @@ Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connection
} else { } else {
LOG(error) << "Missing: parameter: 'div'"; LOG(error) << "Missing: parameter: 'div'";
} }
internalPathChanged().connect(this, &Hello::handlePathChange);
auto externalPath = env.getParameter("path"); auto externalPath = env.getParameter("path");
if (externalPath != nullptr) { if (externalPath != nullptr) {
m_externalPath = *externalPath; m_externalPath = *externalPath;
@ -55,6 +54,7 @@ Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connection
root()->addWidget(std::make_unique<Wt::WText>("<p><emph>Note: you can also run this application " root()->addWidget(std::make_unique<Wt::WText>("<p><emph>Note: you can also run this application "
"from within <a href=\"hello.html\">a web page</a>.</emph></p>")); "from within <a href=\"hello.html\">a web page</a>.</emph></p>"));
} }
LOG(info) << "internal path: " << internalPath();
m_root->addWidget(std::make_unique<Wt::WText>("Your name, please ? ")); m_root->addWidget(std::make_unique<Wt::WText>("Your name, please ? "));
m_nameEdit = m_root->addWidget(std::make_unique<Wt::WLineEdit>()); m_nameEdit = m_root->addWidget(std::make_unique<Wt::WLineEdit>());
@ -73,10 +73,8 @@ Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connection
auto app = Amass::Singleton<WebApplication>::instance(); auto app = Amass::Singleton<WebApplication>::instance();
m_root->addWidget(std::make_unique<Dialog>()); m_root->addWidget(std::make_unique<Dialog>());
internalPathChanged().connect(this, &Hello::handlePathChange);
if (!m_externalPath.empty()) { handlePathChange(m_externalPath.empty() ? internalPath() : m_externalPath);
handlePathChange(m_externalPath);
}
} }
Hello::~Hello() { Hello::~Hello() {

View File

@ -7,7 +7,7 @@ class Session;
class Hello : public Wt::WApplication { class Hello : public Wt::WApplication {
public: public:
Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connectionPool, bool embedded); Hello(const Wt::WEnvironment &env, bool embedded);
~Hello(); ~Hello();
protected: protected:

View File

@ -1,5 +1,6 @@
#include "Restful.h" #include "Restful.h"
#include "Session.h" #include "Database/Database.h"
#include "Database/Session.h"
#include <Wt/Dbo/Impl.h> #include <Wt/Dbo/Impl.h>
#include <Wt/Dbo/Json.h> #include <Wt/Dbo/Json.h>
#include <Wt/Dbo/backend/Sqlite3.h> #include <Wt/Dbo/backend/Sqlite3.h>
@ -10,7 +11,7 @@ DBO_INSTANTIATE_TEMPLATES(MyMessage)
DbStruct *m_dbStruct; DbStruct *m_dbStruct;
void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) { void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
Session session(m_connectionPool); auto session = Amass::Singleton<Database>::instance()->session();
response.setMimeType("application/json"); response.setMimeType("application/json");
response.addHeader("Server", "Wt"); response.addHeader("Server", "Wt");
@ -48,8 +49,7 @@ int DbStruct::rand() {
return distribution(rng); return distribution(rng);
} }
AuthenticationResource::AuthenticationResource(Wt::Dbo::SqlConnectionPool &connectionPool) AuthenticationResource::AuthenticationResource() {
: m_connectionPool(connectionPool) {
} }
DbStruct::DbStruct(const std::string &db) : rng(clock()), distribution(1, 10000) { DbStruct::DbStruct(const std::string &db) : rng(clock()), distribution(1, 10000) {

View File

@ -46,11 +46,8 @@ struct DbStruct {
class AuthenticationResource : public Wt::WResource { class AuthenticationResource : public Wt::WResource {
public: public:
AuthenticationResource(Wt::Dbo::SqlConnectionPool &connectionPool); AuthenticationResource();
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) final; void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) final;
private:
Wt::Dbo::SqlConnectionPool &m_connectionPool;
}; };
class PlaintextResource : public Wt::WResource { class PlaintextResource : public Wt::WResource {

View File

@ -1,5 +0,0 @@
#include "User.h"
#include <Wt/Dbo/Impl.h>
#include <Wt/Auth/Dbo/AuthInfo.h>
DBO_INSTANTIATE_TEMPLATES(User)

View File

@ -1,8 +1,8 @@
#include "WebApplication.h" #include "WebApplication.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Database/Session.h"
#include "Hello.h" #include "Hello.h"
#include "Restful.h" #include "Restful.h"
#include "Session.h"
#include <Wt/Auth/AuthService.h> #include <Wt/Auth/AuthService.h>
#include <Wt/Auth/HashFunction.h> #include <Wt/Auth/HashFunction.h>
#include <Wt/Auth/PasswordService.h> #include <Wt/Auth/PasswordService.h>
@ -23,13 +23,12 @@ WebApplication::WebApplication(uint16_t port, const std::string &documentRoot) {
initializeAuthenticationService(); initializeAuthenticationService();
m_server = std::make_unique<Wt::WServer>(std::format("{}/resources", documentRoot), args); m_server = std::make_unique<Wt::WServer>(std::format("{}/resources", documentRoot), args);
m_sqlConnectionPool = createConnectionPool(std::format("{}/database.sqlite", documentRoot));
m_server->addEntryPoint(Wt::EntryPointType::Application, m_server->addEntryPoint(Wt::EntryPointType::Application,
std::bind(&WebApplication::createApplication, this, std::placeholders::_1, false)); std::bind(&WebApplication::createApplication, this, std::placeholders::_1, false));
m_server->addEntryPoint(Wt::EntryPointType::WidgetSet, m_server->addEntryPoint(Wt::EntryPointType::WidgetSet,
std::bind(&WebApplication::createApplication, this, std::placeholders::_1, true), "/wt/app.js"); std::bind(&WebApplication::createApplication, this, std::placeholders::_1, true), "/wt/app.js");
m_server->addResource(std::make_shared<AuthenticationResource>(*m_sqlConnectionPool), "/auth"); m_server->addResource(std::make_shared<AuthenticationResource>(), "/auth");
m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext"); m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext");
m_server->addResource(std::make_shared<DbResource>(std::format("{}/database.sqlite", documentRoot)), "/db"); m_server->addResource(std::make_shared<DbResource>(std::format("{}/database.sqlite", documentRoot)), "/db");
@ -39,15 +38,8 @@ WebApplication::WebApplication(uint16_t port, const std::string &documentRoot) {
} }
} }
std::unique_ptr<Wt::Dbo::SqlConnectionPool> WebApplication::createConnectionPool(const std::string &sqlite3) {
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(sqlite3);
connection->setProperty("show-queries", "true");
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime, Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
return std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
}
std::unique_ptr<Wt::WApplication> WebApplication::createApplication(const Wt::WEnvironment &env, bool embedded) { std::unique_ptr<Wt::WApplication> WebApplication::createApplication(const Wt::WEnvironment &env, bool embedded) {
return std::make_unique<Hello>(env, *m_sqlConnectionPool, embedded); return std::make_unique<Hello>(env, embedded);
} }
WebApplication::~WebApplication() { WebApplication::~WebApplication() {

View File

@ -9,10 +9,6 @@ class WServer;
class WApplication; class WApplication;
class WEnvironment; class WEnvironment;
namespace Dbo {
class SqlConnectionPool;
}
namespace Auth { namespace Auth {
class AuthService; class AuthService;
class PasswordService; class PasswordService;
@ -32,12 +28,10 @@ public:
protected: protected:
WebApplication(uint16_t port, const std::string &documentRoot); WebApplication(uint16_t port, const std::string &documentRoot);
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(const std::string &sqlite3);
std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment &env, bool embedded); std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment &env, bool embedded);
private: private:
std::unique_ptr<Wt::WServer> m_server; std::unique_ptr<Wt::WServer> m_server;
std::unique_ptr<Wt::Dbo::SqlConnectionPool> m_sqlConnectionPool;
std::unique_ptr<Wt::Auth::AuthService> m_authService; std::unique_ptr<Wt::Auth::AuthService> m_authService;
std::unique_ptr<Wt::Auth::PasswordService> m_passwordService; std::unique_ptr<Wt::Auth::PasswordService> m_passwordService;