243 lines
9.3 KiB
C++
243 lines
9.3 KiB
C++
#include "Application.h"
|
|
#include "BoostLog.h"
|
|
#include "BulmaTheme.h"
|
|
#include "Database/Session.h"
|
|
#include "HomePage.h"
|
|
#include "LoginPage.h"
|
|
#include "NavigationBar.h"
|
|
#include "Restful.h"
|
|
#include "VisitorRecordsPage.h"
|
|
#include "model/AuthModel.h"
|
|
#include <Wt/Auth/AuthService.h>
|
|
#include <Wt/Auth/HashFunction.h>
|
|
#include <Wt/Auth/Identity.h>
|
|
#include <Wt/Auth/PasswordService.h>
|
|
#include <Wt/Auth/PasswordStrengthValidator.h>
|
|
#include <Wt/Auth/PasswordVerifier.h>
|
|
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
|
#include <Wt/Dbo/SqlConnectionPool.h>
|
|
#include <Wt/Dbo/backend/Sqlite3.h>
|
|
#include <Wt/Http/Cookie.h>
|
|
#include <Wt/WContainerWidget.h>
|
|
#include <Wt/WEnvironment.h>
|
|
#include <Wt/WServer.h>
|
|
#include <format>
|
|
|
|
namespace WebToolkit {
|
|
Application::Application(const Wt::WEnvironment &env, bool embedded) : Wt::WApplication(env) {
|
|
messageResourceBundle().use(appRoot() + "wt");
|
|
messageResourceBundle().use(appRoot() + "auth_strings");
|
|
messageResourceBundle().use(appRoot() + "auth_css_theme");
|
|
useStyleSheet("/resources/app.css");
|
|
LOG(info) << "app root: " << appRoot();
|
|
m_session = Database::session();
|
|
m_session->login().changed().connect(this, &Application::authEvent);
|
|
setTheme(std::make_shared<BulmaTheme>("bulma", !embedded));
|
|
if (!embedded) {
|
|
m_navigationBar = root()->addNew<NavigationBar>();
|
|
m_navigationBar->registerClicked.connect([this]() {
|
|
if (m_loginPage) {
|
|
m_loginPage->registerNewUser();
|
|
} else if (m_loginPageRef) {
|
|
m_loginPageRef->registerNewUser();
|
|
}
|
|
});
|
|
m_root = root()->addNew<Wt::WContainerWidget>();
|
|
} else {
|
|
std::unique_ptr<Wt::WContainerWidget> topPtr = std::make_unique<Wt::WContainerWidget>();
|
|
m_root = topPtr.get();
|
|
const std::string *div = env.getParameter("div");
|
|
if (div) {
|
|
setJavaScriptClass(*div);
|
|
bindWidget(std::move(topPtr), *div);
|
|
} else {
|
|
LOG(error) << "Missing: parameter: 'div'";
|
|
m_root = nullptr;
|
|
}
|
|
auto externalPath = env.getParameter("path");
|
|
if (externalPath != nullptr) {
|
|
m_externalPath = *externalPath;
|
|
LOG(info) << "external path: " << m_externalPath;
|
|
} else {
|
|
auto parameters = env.getParameterMap();
|
|
for (auto &p : parameters) {
|
|
LOG(info) << p.first;
|
|
}
|
|
}
|
|
}
|
|
LOG(info) << "url: " << url();
|
|
LOG(info) << "relative resources url: " << relativeResourcesUrl();
|
|
LOG(info) << "resources url: " << resourcesUrl();
|
|
LOG(info) << "internal path: " << internalPath();
|
|
LOG(info) << "bookmark url: " << bookmarkUrl("/");
|
|
LOG(info) << "relative url: " << resolveRelativeUrl("/");
|
|
|
|
auto app = Amass::Singleton<WebToolkit::Server>::instance();
|
|
m_loginPage = std::make_unique<LoginPage>(app->authService(), m_session->users(), m_session->login());
|
|
if (m_externalPath.empty()) {
|
|
m_loginPage->processEnvironment();
|
|
} else {
|
|
m_loginPage->processExternalEnvironment(m_externalPath, app->authService());
|
|
}
|
|
|
|
handlePathChange(m_externalPath.empty() ? internalPath() : m_externalPath);
|
|
internalPathChanged().connect(this, &Application::handlePathChange);
|
|
}
|
|
|
|
Application::~Application() {
|
|
}
|
|
|
|
void Application::authEvent() {
|
|
if (m_session->login().loggedIn()) {
|
|
const Wt::Auth::User &u = m_session->login().user();
|
|
LOG(info) << "User " << u.id() << " (" << u.identity(Wt::Auth::Identity::LoginName) << ")"
|
|
<< " logged in.";
|
|
if (m_loginPage) {
|
|
if (m_navigationBar != nullptr) {
|
|
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
|
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
|
m_loginPageRef->removeStyleClass("bulma-container");
|
|
}
|
|
} else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
|
|
m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef);
|
|
if (m_navigationBar != nullptr) {
|
|
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
|
|
m_loginPageRef->removeStyleClass("bulma-m-auto");
|
|
m_loginPageRef->removeStyleClass("bulma-container");
|
|
}
|
|
}
|
|
setInternalPath("/", true);
|
|
|
|
auto app = Amass::Singleton<WebToolkit::Server>::instance();
|
|
auto &service = app->authService();
|
|
auto &env = environment();
|
|
auto token = env.getCookie(service.authTokenCookieName());
|
|
if (token == nullptr) {
|
|
Wt::Http::Cookie cookie(service.authTokenCookieName(), service.createAuthToken(u));
|
|
cookie.setPath(AuthModel::CookiePath);
|
|
cookie.setExpires(Wt::WDateTime());
|
|
setCookie(cookie);
|
|
}
|
|
} else {
|
|
m_loginPage = m_navigationBar->removeLoginItem();
|
|
LOG(info) << "User logged out.";
|
|
}
|
|
}
|
|
|
|
void Application::handlePathChange(const std::string &path) {
|
|
LOG(info) << "handlePathChange: " << path;
|
|
if (m_root == nullptr) {
|
|
LOG(error) << "root container is null.";
|
|
return;
|
|
}
|
|
if (path.starts_with("/wt/login")) {
|
|
if (m_session->login().loggedIn()) {
|
|
LOG(info) << "already logged in.";
|
|
} else {
|
|
if (m_loginPage) {
|
|
m_root->clear();
|
|
m_loginPageRef = m_root->addWidget(std::move(m_loginPage));
|
|
m_loginPageRef->addStyleClass("bulma-m-auto bulma-container");
|
|
}
|
|
}
|
|
} else {
|
|
if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
|
|
m_loginPage = m_root->removeWidget(m_loginPageRef);
|
|
}
|
|
|
|
if (path.starts_with("/wt/register")) {
|
|
} else if (path.starts_with("/wt/visitor/analysis")) {
|
|
m_root->clear();
|
|
m_root->addNew<VisitorRecordsPage>(*m_session);
|
|
} else {
|
|
m_root->clear();
|
|
m_root->addNew<HomePage>();
|
|
}
|
|
}
|
|
}
|
|
|
|
Server::Server(uint16_t port, const std::string &applicationRoot, const std::string &documentRoot) {
|
|
try {
|
|
std::vector<std::string> args;
|
|
args.push_back(std::format("--docroot={};/resources", documentRoot));
|
|
args.push_back(std::format("--approot={}/resources", applicationRoot));
|
|
args.push_back(std::format("--http-listen=127.0.0.1:{}", port));
|
|
initializeAuthenticationService();
|
|
|
|
m_server = std::make_unique<Wt::WServer>(std::format("{}/resources", applicationRoot), args);
|
|
|
|
m_server->addEntryPoint(Wt::EntryPointType::Application,
|
|
std::bind(&Server::createApplication, this, std::placeholders::_1, false));
|
|
m_server->addEntryPoint(Wt::EntryPointType::WidgetSet,
|
|
std::bind(&Server::createApplication, this, std::placeholders::_1, true), "/wt/app.js");
|
|
m_server->addResource(std::make_shared<AuthenticationResource>(), "/auth/verify");
|
|
m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext");
|
|
|
|
m_server->start();
|
|
} catch (const std::exception &e) {
|
|
LOG(error) << e.what();
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Wt::WApplication> Server::createApplication(const Wt::WEnvironment &env, bool embedded) {
|
|
return std::make_unique<Application>(env, embedded);
|
|
}
|
|
|
|
Server::~Server() {
|
|
}
|
|
|
|
void Server::initializeAuthenticationService() {
|
|
m_authService = std::make_unique<Wt::Auth::AuthService>();
|
|
m_authService->setEmailVerificationEnabled(true);
|
|
m_authService->setEmailVerificationRequired(true);
|
|
m_authService->setAuthTokensEnabled(true);
|
|
m_passwordService = std::make_unique<Wt::Auth::PasswordService>(*m_authService);
|
|
|
|
auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
|
|
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(7));
|
|
m_passwordService->setVerifier(std::move(verifier));
|
|
m_passwordService->setPasswordThrottle(std::make_unique<Wt::Auth::AuthThrottle>());
|
|
m_passwordService->setStrengthValidator(std::make_unique<Wt::Auth::PasswordStrengthValidator>());
|
|
}
|
|
|
|
Wt::Auth::AuthService &Server::authService() {
|
|
return *m_authService;
|
|
}
|
|
|
|
const Wt::Auth::PasswordService &Server::passwordService() {
|
|
return *m_passwordService;
|
|
}
|
|
|
|
void Server::insertCookie(const std::string &cookie) {
|
|
if (!m_cookies.contains(cookie)) {
|
|
m_cookies.insert(cookie);
|
|
}
|
|
}
|
|
|
|
Wt::Http::Cookie Server::updateCookie(const std::string &oldCookie, const Wt::Auth::AuthTokenResult &result, bool secure) {
|
|
Wt::Http::Cookie cookie(m_authService->authTokenCookieName());
|
|
cookie.setPath(AuthModel::CookiePath);
|
|
cookie.setSecure(secure);
|
|
if (result.state() == Wt::Auth::AuthTokenState::Invalid) {
|
|
if (m_cookies.contains(oldCookie)) {
|
|
m_cookies.erase(oldCookie);
|
|
}
|
|
cookie.setMaxAge(std::chrono::seconds(0));
|
|
cookie.setValue("");
|
|
} else {
|
|
auto newToken = result.newToken();
|
|
if (!newToken.empty()) {
|
|
if (m_cookies.contains(oldCookie)) { // 勾选了记住我
|
|
m_cookies.erase(oldCookie);
|
|
cookie.setMaxAge(std::chrono::seconds(result.newTokenValidity()));
|
|
m_cookies.insert(newToken);
|
|
} else { // 只在会话期间有效
|
|
cookie.setExpires(Wt::WDateTime());
|
|
}
|
|
cookie.setValue(newToken);
|
|
}
|
|
}
|
|
return cookie;
|
|
}
|
|
|
|
} // namespace WebToolkit
|