diff --git a/WebApplication/CMakeLists.txt b/WebApplication/CMakeLists.txt index 1299693..cd530a1 100644 --- a/WebApplication/CMakeLists.txt +++ b/WebApplication/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(WebApplication WebApplication.h WebApplication.cpp + LoginWidget.h LoginWidget.cpp User.h User.cpp Hello.h Hello.cpp Restful.h Restful.cpp diff --git a/WebApplication/Dialog.h b/WebApplication/Dialog.h index 505b378..b612b19 100644 --- a/WebApplication/Dialog.h +++ b/WebApplication/Dialog.h @@ -2,6 +2,7 @@ #define __DIALOG_H__ #include +#include class Dialog : public Wt::WContainerWidget { public: diff --git a/WebApplication/Hello.cpp b/WebApplication/Hello.cpp index e478ce8..3227376 100644 --- a/WebApplication/Hello.cpp +++ b/WebApplication/Hello.cpp @@ -1,12 +1,10 @@ #include "Hello.h" #include "BoostLog.h" #include "Dialog.h" +#include "LoginWidget.h" #include "Session.h" #include "WebApplication.h" -#include -#include #include -#include #include #include #include @@ -14,18 +12,22 @@ #include #include -Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connectionPool, bool embedded) : Wt::WApplication(env) { +Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connectionPool, 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 = std::make_unique(connectionPool); m_session->login().changed().connect(this, &Hello::authEvent); setTitle("Hello world"); - Wt::WContainerWidget *top = nullptr; if (!embedded) { setTheme(std::make_shared()); - top = root(); + m_root = root(); } else { std::unique_ptr topPtr = std::make_unique(); - top = topPtr.get(); + m_root = topPtr.get(); const std::string *div = env.getParameter("div"); if (div) { setJavaScriptClass(*div); @@ -37,7 +39,7 @@ Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connection 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) { @@ -54,29 +56,27 @@ Hello::Hello(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &connection "from within a web page.

")); } - top->addWidget(std::make_unique("Your name, please ? ")); - m_nameEdit = top->addWidget(std::make_unique()); + m_root->addWidget(std::make_unique("Your name, please ? ")); + m_nameEdit = m_root->addWidget(std::make_unique()); m_nameEdit->setFocus(); - auto b = top->addWidget(std::make_unique("点击我!")); + auto b = m_root->addWidget(std::make_unique("点击我!")); b->setMargin(5, Wt::Side::Left); - top->addWidget(std::make_unique()); + m_root->addWidget(std::make_unique()); - m_greeting = top->addWidget(std::make_unique()); + m_greeting = m_root->addWidget(std::make_unique()); b->clicked().connect(this, &Hello::greet); m_nameEdit->enterPressed().connect(this, &Hello::greet); auto app = Amass::Singleton::instance(); - auto authWidget = std::make_unique(app->authService(), m_session->users(), m_session->login()); - authWidget->setInternalBasePath("/"); - authWidget->model()->addPasswordAuth(&app->passwordService()); - authWidget->setRegistrationEnabled(true); - authWidget->processEnvironment(); - top->addWidget(std::move(authWidget)); - top->addWidget(std::make_unique()); + m_root->addWidget(std::make_unique()); + + if (!m_externalPath.empty()) { + handlePathChange(m_externalPath); + } } Hello::~Hello() { @@ -99,4 +99,9 @@ void Hello::authEvent() { void Hello::handlePathChange(const std::string &path) { LOG(info) << "handlePathChange: " << path; + if (path.starts_with("/wt/login") || path.starts_with("/wt/register")) { + m_root->clear(); + m_root->setStyleClass("WtCenterContainer"); + m_root->addNew(m_session->users(), m_session->login()); + } } diff --git a/WebApplication/Hello.h b/WebApplication/Hello.h index 40c5936..0570ce6 100644 --- a/WebApplication/Hello.h +++ b/WebApplication/Hello.h @@ -20,6 +20,8 @@ private: Wt::WText *m_greeting = nullptr; std::unique_ptr m_session; std::string m_externalPath; + + Wt::WContainerWidget *m_root = nullptr; }; #endif // __HELLO_H__ \ No newline at end of file diff --git a/WebApplication/LoginWidget.cpp b/WebApplication/LoginWidget.cpp new file mode 100644 index 0000000..80b45c2 --- /dev/null +++ b/WebApplication/LoginWidget.cpp @@ -0,0 +1,17 @@ +#include "LoginWidget.h" +#include "WebApplication.h" +#include +#include +#include +#include + +LoginWidget::LoginWidget(Wt::Auth::AbstractUserDatabase &users, Wt::Auth::Login &login) { + auto app = Amass::Singleton::instance(); + auto authWidget = std::make_unique(app->authService(), users, login); + authWidget->setInternalBasePath("/wt"); + authWidget->model()->addPasswordAuth(&app->passwordService()); + authWidget->setRegistrationEnabled(true); + authWidget->processEnvironment(); + setAttributeValue("style", "transform: translateY(-100px);"); + addWidget(std::move(authWidget)); +} diff --git a/WebApplication/LoginWidget.h b/WebApplication/LoginWidget.h new file mode 100644 index 0000000..60e4407 --- /dev/null +++ b/WebApplication/LoginWidget.h @@ -0,0 +1,11 @@ +#ifndef __LOGINWIDGET_H__ +#define __LOGINWIDGET_H__ + +#include + +class LoginWidget : public Wt::WContainerWidget { +public: + LoginWidget(Wt::Auth::AbstractUserDatabase &users, Wt::Auth::Login &login); +}; + +#endif // __LOGINWIDGET_H__ \ No newline at end of file diff --git a/WebApplication/WebApplication.cpp b/WebApplication/WebApplication.cpp index e0bf55c..0144249 100644 --- a/WebApplication/WebApplication.cpp +++ b/WebApplication/WebApplication.cpp @@ -18,6 +18,7 @@ WebApplication::WebApplication() { std::vector args; args.push_back("--approot=./build"); args.push_back("--docroot=./build"); + args.push_back("--config=resources/wt_config.xml"); args.push_back("--http-listen=127.0.0.1:8855"); // --docroot=. --no-compression --http-listen 127.0.0.1:8855 initializeAuthenticationService(); @@ -30,7 +31,7 @@ WebApplication::WebApplication() { "/hello"); m_server->addEntryPoint(Wt::EntryPointType::WidgetSet, std::bind(&WebApplication::createApplication, this, std::placeholders::_1, true), - "/wt.js"); + "/wt/app.js"); m_server->addResource(std::make_shared(*m_sqlConnectionPool), "/auth"); m_server->addResource(std::make_shared(), "/plaintext"); m_server->addResource(std::make_shared("database.sqlite"), "/db"); diff --git a/resources/app.css b/resources/app.css new file mode 100644 index 0000000..7960c94 --- /dev/null +++ b/resources/app.css @@ -0,0 +1,7 @@ + +.WtCenterContainer { + display: flex; + flex: 1; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/resources/auth_css_theme.xml b/resources/auth_css_theme.xml new file mode 100644 index 0000000..42132a9 --- /dev/null +++ b/resources/auth_css_theme.xml @@ -0,0 +1,175 @@ + + + + } + + ${{1}} + ${} + ]]> + + + + + + + +
+

${tr:Wt.Auth.registration-form-title}

+

${tr:Wt.Auth.registration-form-info}

+ + ${password-description} + +
+ ${} + + ${user-name} + ${} + + ${block:Wt.Auth.field choose-password} + ${block:Wt.Auth.field repeat-password} + ${block:Wt.Auth.field email} +
+ + ${} +
+ ${oauth-description} + + ${icons} + ${
} + +
+ ${ok-button} ${cancel-button} +
+
+
+ + +
+

${tr:Wt.Auth.update-password-form-title}

+

${tr:Wt.Auth.update-password-form-info}

+ +
+ + ${user-name} + + ${block:Wt.Auth.field password} + ${block:Wt.Auth.field choose-password} + ${block:Wt.Auth.field repeat-password} +
+ +
+ ${ok-button} ${cancel-button} +
+
+
+ + +
+

${tr:Wt.Auth.lost-password-form-title}

+

${tr:Wt.Auth.lost-password-form-info}

+ +
+ + ${email} +
+ +
+ ${send-button} ${cancel-button} +
+
+
+ + +
+

${tr:Wt.Auth.password-prompt-form-title}

+

${tr:Wt.Auth.password-prompt-form-info}

+ +
+ + ${user-name} + + ${block:Wt.Auth.field password} +
+ +
+ ${ok-button} ${cancel-button} +
+
+
+ + +
+ ${user-name} ${logout} +
+
+ + + + +
diff --git a/resources/auth_strings_zh.xml b/resources/auth_strings_zh.xml new file mode 100644 index 0000000..1b1e21a --- /dev/null +++ b/resources/auth_strings_zh.xml @@ -0,0 +1,284 @@ + + + + + + The operation could not be completed: invalid token. + The operation could not be completed: the token has expired. + An email has been sent. Follow the instructions to set a new password. + Your email address is now confirmed. + + + Check failed + + + The same as the old one + + + Based on the old one + + + 长度太短 + + + 超出长度 + + + Not enough different characters or classes for this length + + + Not enough different characters or classes + + + Based on personal information + + + Based on a dictionary word and not a passphrase + + + Based on a common sequence of characters + + + Valid + + + +

Sorry, could not login using your Google account.

+ +

Google's identification server generated an unexpected + response.

+
+
+ + +

Sorry, could not login using your Facebook account.

+ +

Facebook's identification server generated an unexpected + response.

+
+
+ + + + 登录 + + 用户名 + 请输入您的用户名 + Invalid + Email address + Enter your email address + + Enter your email address (optional) + + 密码 + 请输入您的密码 + 记住我 + + Keeps login for one day + Keeps login for {1} days + + + 保持登录状态1周 + 保持登录状态{1}周 + + Keeps login for {1} + Lost password + 登录 + 退出 + {1}s后重试 + + Resend? + Resend Email Verification + Please enter your email address again below to resend the verification email. Make sure to check your spam folder in case the email does not show up in your inbox. + Email address does not match this user + Email verification + + + + + 注册 + 请填写以下信息进行注册 + Register using a user name and password: + Register with an identity provider: + Or, register with an identity provider: + Valid + Choose Password + Choose a password + Repeat password + Re-enter your password + Invalid email address + Email already registered + Your external account + Choose one of your accounts + User already exists + User exists, is this you? + Min. size {1} characters + Min. size {1} characters + Passwords don't match + 注册 + 注册 + 注册失败 + Please confirm your email first. + +
Welcome!
+ + You will be able to login after confirming your email address using the + email we've just sent you. +
+ + + + Enter your password + Enter your password + + Invalid password + + + + Update password + Update password + Enter a new password below + + + + Forgot your password? No sweat! + Recover your password + Please enter below the + email address which you used during registration. A mail will be + sent with instructions to enter a new password. + Send + + + + Error + Notice + + + + User account activation Link + + +Hello {1}, + +Thank you for joining! + +To complete your registration, please finally confirm your account by +clicking on the following link or copying the URL into your browser. + +Please click here to confirm your registration or copy and paste the +following URL into your browser: (Note: be sure to copy the entire +URL, including any part of it which goes onto a second line.) + +{3} + +This text is resolved from the "Wt.Auth.confirmmail.subject", +"Wt.Auth.confirmmail.body", and "Wt.Auth.confirmmail.htmlbody" +resource keys. + +Good luck with your Wt application, + +The Wt team. + + + +

Hello {1},

+ +

Thank you for joining!

+ +

To complete your registration, please finally confirm your + account by clicking on the following link or copying the URL into + your browser.

+ +

Please click here to confirm your + registration or copy and paste the following URL into your + browser: (Note: be sure to copy the entire URL, including any + part of it which goes onto a second line.)

+ + {3} + +

+ Note to developer: +

+

+ This text is resolved from the "Wt.Auth.confirmmail.subject", + "Wt.Auth.confirmmail.body", and "Wt.Auth.confirmmail.htmlbody" + resource keys. +

+ +

Good luck with your Wt application,

+ +

The Wt team.

+
+ + Lost password instructions + + +Hello {1}, + +This mail has been sent to you, because someone (presumably you?) +indicated that he wishes to choose a new password, because the current +password escapes his mind. + +If you requested this, then choose a new password by clicking on the +following link or copying the URL into your browser. If you didn't +request this, you can safely ignore and discard this email. + +Please copy and paste the following URL into your browser: (Note: be +sure to copy the entire URL, including any part of it which goes onto +a second line.) + +{3} + +This text is resolved from the "Wt.Auth.lostpasswordmail.subject", +"Wt.Auth.lostpasswordmail.body", and "Wt.Auth.lostpasswordmail.htmlbody" +resource keys. + +Good luck with your Wt application, + +The Wt team. + + + +

Hello {1},

+ +

This mail has been sent to you, because someone (presumably + you?) indicated that he wishes to choose a new password, because + the current password escapes his mind.

+ +

If you requested this, then choose a new password by clicking + on the following link or copying the URL into your browser. If you + didn't request this, you can safely ignore and discard this + email.

+ +

Please click here to choose a new password or + copy and paste the following URL into your browser: (Note: be + sure to copy the entire URL, including any part of it which goes + onto a second line.)

+ + {3} + +

+ Note to developer: +

+

+ This text is resolved from the + "Wt.Auth.lostpasswordmail.subject", + "Wt.Auth.lostpasswordmail.body", and + "Wt.Auth.lostpasswordmail.htmlbody" resource keys. +

+ +

Good luck with your Wt application,

+ +

The Wt team.

+
+ + TOTP Verification + Use your authenticator app/extension to generate a TOTP code. + This QR code can be scanned by an authenticator app. It will allow you to generate TOTP codes. This will serve as an additional layer of security. + Code + Enter the TOTP code + Invalid TOTP code + Throtteling TOTP validation attempts +
diff --git a/resources/build.sh b/resources/build.sh index fc1cc54..d019d4e 100755 --- a/resources/build.sh +++ b/resources/build.sh @@ -24,6 +24,8 @@ function build() { # reset # pkill -9 HttpServer # cp -r /opt/Libraries/wt-4.11.1/share/Wt/* ./build + # cp resources/*.css build/resources/ + # cp resources/*.xml build/ if [ ! -f "${build_path}/CMakeCache.txt" ]; then cmake_scan diff --git a/resources/wt_config.xml b/resources/wt_config.xml new file mode 100644 index 0000000..7c96f6c --- /dev/null +++ b/resources/wt_config.xml @@ -0,0 +1,841 @@ + + + + + + + + + + + + + + + + 1 + + + + URL + + + true + + + 600 + + + + + + 50 + + + + + + + + + /opt/Libraries/wt-4.11.1/var/run/wt + + + + + + + + 128 + + + + false + + + + + + * -debug + + + 128 + + + 5120 + + + 1000 + + + 10 + + + 16 + + + 1 + + + false + + + false + + + false + + + true + + + Load basic HTML + + + + + + + + X-Forwarded-For + + + + + + + + + + + + + + true + + + 500 + + + 200 + + + + + + + + + .*Googlebot.* + .*msnbot.* + .*Slurp.* + .*Crawler.* + .*Bot.* + .*ia_archiver.* + .*Twiceler.* + .*Yandex.* + .*Nutch.* + .*MJ12bot.* + .*Baiduspider.* + .*Ezooms.* + .*Sogou web spider.* + .*AhrefsBot.* + + + + + + + false + + + false + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + /resources/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/wt_zh.xml b/resources/wt_zh.xml new file mode 100644 index 0000000..db14d22 --- /dev/null +++ b/resources/wt_zh.xml @@ -0,0 +1,278 @@ + + + + The application has stopped running, would you like to restart? + + + {1} of {2} + « First + ‹ Previous + Next › + Last » + + « + » + Today + + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday + Mon + Tue + Wed + Thu + Fri + Sat + Sun + January + February + March + April + May + June + July + August + September + October + November + December + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + Close + + less than a second + + one second + {1} seconds + + + one minute + {1} minutes + + + one hour + {1} hours + + + one day + {1} days + + + one week + {1} weeks + + + one month + {1} months + + + one year + {1} years + + + + The date must be after {1} + The date must be before {1} + The date must be between {1} and {2} + Must be a date in the format '{1}' + + Must be a valid email address + Must be a comma-separated list of email addresses + Must be an email address matching the pattern '{1}' + Must be a comma-separated list of email addresses matching the pattern '{1}' + + The time must be after {1} + The time must be before {1} + The time must be between {1} and {2} + Must be a time in the format '{1}' + + Loading... + + + ${center-script} + ${layout} + + + + + /* */ + + + Must be a number + The number must be larger than {1} + The number must be in the range {1} to {2} + The number must be smaller than {1} + + Save + Cancel + + Must be an integer number + The number must be larger than {1} + The number must be in the range {1} to {2} + The number must be smaller than {1} + + The input must be at least {1} characters + The input must have a length between {1} and {2} characters + The input must be no more than {1} characters + + Play + Pause + Stop + Mute + Unmute + Maximum volume + Repeat + Repeat off + Play + Full screen + Restore screen + + Abort + 取消 + Ignore + No + No to All + Ok + Retry + Yes + Yes to All + + Loading... + + Invalid input + + This field cannot be empty + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${hour}${minute}${second}${millisecond}${ampm} + + + + ${cols-row}${expand}${no-expand}${col0} + + + +
+ ${cols-row}${expand}${no-expand}${label-area} +
+
+ ${children} +
+ + +

${title}

+
+