#include "Application.h" #include "BoostLog.h" #include "BulmaTheme.h" #include "Database/Session.h" #include "HomePage.h" #include "LoginPage.h" #include "NavigationBar.h" #include "RedirectPage.h" #include "Restful.h" #include "TaskPage.h" #include "VisitorRecordsPage.h" #include "WebRTCClientPage.h" #include "model/AuthModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace WebToolkit { Application::Application(const Wt::WEnvironment &env, bool embedded) : Wt::WApplication(env), m_startup(this, "startup"), m_logout(this, "logout") { messageResourceBundle().use(appRoot() + "wt"); messageResourceBundle().use(appRoot() + "auth_strings"); messageResourceBundle().use(appRoot() + "auth_css_theme"); messageResourceBundle().use(appRoot() + "webrtc"); 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("bulma", !embedded)); std::string externalPath; if (!embedded) { root()->addStyleClass("bulma-is-flex bulma-is-flex-direction-column"); m_navigationBar = root()->addNew(); m_navigationBar->registerClicked.connect([this]() { if (m_loginPage) { m_loginPage->registerNewUser(); } else if (m_loginPageRef) { m_loginPageRef->registerNewUser(); } }); m_root = root()->addNew(); m_root->addStyleClass("bulma-container bulma-is-flex-grow-1 bulma-is-flex"); } else { m_extern = true; std::unique_ptr topPtr = std::make_unique(); 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 path = env.getParameter("path"); if (path != nullptr) { externalPath = *path; LOG(info) << "external path: " << externalPath; } else { auto parameters = env.getParameterMap(); for (auto &p : parameters) { LOG(info) << p.first; } } } auto app = Amass::Singleton::instance(); bool authTokensEnabled = app->authService().authTokensEnabled(); std::string authTokenCookieName = app->authService().authTokenCookieName(); std::string authTokenCookieDomain = app->authService().authTokenCookieDomain(); if (env.hostName().find("amass.fun") != std::string::npos) { if (authTokenCookieDomain != AuthModel::CookieDomain) { app->authService().setAuthTokensEnabled(authTokensEnabled, authTokenCookieName, AuthModel::CookieDomain); } } else { if (!authTokenCookieDomain.empty()) { app->authService().setAuthTokensEnabled(authTokensEnabled, authTokenCookieName, ""); } } auto next = env.getParameter("redirect"); if (next != nullptr) { m_loginedRedirectUrl = *next; } LOG(info) << "url: " << url() << ", host name: " << env.hostName(); LOG(info) << "resources url: " << resourcesUrl() << ", relative resources url: " << relativeResourcesUrl(); LOG(info) << "internal path: " << internalPath() << ", bookmark url: " << bookmarkUrl() << ", next: " << m_loginedRedirectUrl; m_loginPage = std::make_unique(app->authService(), m_session->users(), m_session->login()); if (externalPath.empty()) { m_loginPage->processEnvironment(); } else { m_loginPage->processExternalEnvironment(externalPath, app->authService()); } m_logout.connect([this]() { LOG(info) << "logout from external callbak."; m_session->login().logout(); }); m_startup.connect(this, [externalPath]() { LOG(info) << "wtapp started."; auto app = Wt::WApplication::instance(); auto path = externalPath.empty() ? app->internalPath() : externalPath; if (path != app->internalPath()) { app->setInternalPath(externalPath.empty() ? app->internalPath() : externalPath, true); } else { dynamic_cast(app)->handlePathChange(path); } }); internalPathChanged().connect(this, &Application::handlePathChange); doJavaScript(m_startup.createCall({})); } Application::~Application() { } void Application::authEvent() { auto app = Amass::Singleton::instance(); auto &service = app->authService(); auto token = environment().getCookie(service.authTokenCookieName()); 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 (token == nullptr) { Wt::Http::Cookie cookie(service.authTokenCookieName(), service.createAuthToken(u)); cookie.setDomain(service.authTokenCookieDomain()); cookie.setPath(AuthModel::CookiePath); cookie.setExpires(Wt::WDateTime()); setCookie(cookie); app->insertCookie(cookie.value(), u); } 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"); } } if (m_loginedRedirectUrl.empty()) { setInternalPath("/", true); } else { redirect(m_loginedRedirectUrl); } } else { if (m_navigationBar != nullptr) { m_loginPage = m_navigationBar->removeLoginItem(); } if (token != nullptr) { app->removeCookie(*token); } LOG(info) << "user logged out, internal path: " << internalPath(); if (internalPath() == "/wt/login") { handlePathChange(internalPath()); } } doJavaScript("if (window.updateAuthStatus) window.updateAuthStatus();"); } 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."; m_root->clear(); auto p = m_root->addNew(); p->setMessage("您已经登录..."); } 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(); auto p = m_root->addNew(*m_session); p->addStyleClass("bulma-is-flex-grow-1"); } else if (path.starts_with("/wt/redirect")) { m_root->clear(); auto p = m_root->addNew(); p->setMessage("这是一个跳转测试...."); p->setRedirect("https://amass.fun"); } else if (path.starts_with("/wt/webrtc")) { m_root->clear(); auto p = m_root->addNew(); } else if (path.starts_with("/wt/task")) { m_root->clear(); m_root->addNew(); } else { m_root->clear(); m_root->addNew(); } } } bool Application::inDocusaurus() const { return m_extern; } Server::Server(uint16_t port, const std::string &applicationRoot, const std::string &documentRoot) { try { std::vector 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(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(), "/api/v1/auth/${tag}"); m_server->addResource(std::make_shared(), "/plaintext"); m_server->start(); } catch (const std::exception &e) { LOG(error) << e.what(); } } std::unique_ptr Server::createApplication(const Wt::WEnvironment &env, bool embedded) { return std::make_unique(env, embedded); } Server::~Server() { } void Server::initializeAuthenticationService() { m_authService = std::make_unique(); m_authService->setEmailVerificationEnabled(true); m_authService->setEmailVerificationRequired(true); m_authService->setAuthTokensEnabled(true); m_passwordService = std::make_unique(*m_authService); auto verifier = std::make_unique(); verifier->addHashFunction(std::make_unique(7)); m_passwordService->setVerifier(std::move(verifier)); m_passwordService->setPasswordThrottle(std::make_unique()); m_passwordService->setStrengthValidator(std::make_unique()); } Wt::Auth::AuthService &Server::authService() { return *m_authService; } const Wt::Auth::PasswordService &Server::passwordService() { return *m_passwordService; } std::optional Server::user(const std::string &cookie) { if (m_cookies.contains(cookie)) { return m_cookies.at(cookie); } else { return std::nullopt; } } void Server::insertCookie(const std::string &cookie, const Wt::Auth::User &user) { if (!m_cookies.contains(cookie)) { m_cookies.insert_or_assign(cookie, user); } } void Server::removeCookie(const std::string &cookie) { if (m_cookies.contains(cookie)) { m_cookies.erase(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.setDomain(m_authService->authTokenCookieDomain()); 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_or_assign(newToken, result.user()); } else { // 只在会话期间有效 cookie.setExpires(Wt::WDateTime()); } cookie.setValue(newToken); } } return cookie; } } // namespace WebToolkit