mirror of
https://github.com/TriliumNext/Notes.git
synced 2026-01-08 05:49:49 -06:00
feature complete
Rolled back custom url setting. It was getting too complicated. For example, what if user defined urls that are already used by existing routes? We'd need to add path validation and probably other stuff too. My motivation was to obscure the login link in order to provide a little protection from simple bot attacks. It's likely better to take a considered security look (from folks who know this space!)
This commit is contained in:
@@ -6,31 +6,19 @@ const TPL = `
|
||||
<h4>${t('share.settings_title')}</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t('share.redirect_url_label')}</label>
|
||||
<input type="text"
|
||||
class="share-redirect-url form-control"
|
||||
name="shareRedirectUrl"
|
||||
placeholder="share"/>
|
||||
<p>${t('share.redirect_url_description')}</p>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input redirect-bare-domain" type="checkbox" id="redirect-bare-domain">
|
||||
<label class="custom-control-label" for="redirect-bare-domain">${t('share.redirect_bare_domain')}</label>
|
||||
<p>${t('share.redirect_bare_domain_description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t('share.login_redirect_url_label')}</label>
|
||||
<input type="text"
|
||||
class="login-redirect-url form-control"
|
||||
name="loginRedirectUrl"
|
||||
placeholder="login"/>
|
||||
<p>${t('share.login_redirect_url_description')}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-check">
|
||||
<input type="checkbox"
|
||||
class="show-login-in-share form-check-input"
|
||||
name="showLoginInShareTheme"/>
|
||||
${t('share.show_login_label')}
|
||||
</label>
|
||||
<p>${t('share.show_login_description')}</p>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input show-login-in-share" type="checkbox" id="showLoginInShare">
|
||||
<label class="custom-control-label" for="showLoginInShare">${t('share.show_login_in_share')}</label>
|
||||
<p>${t('share.show_login_in_share_description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -38,24 +26,18 @@ const TPL = `
|
||||
export default class ShareSettingsWidget extends OptionsWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.$shareRedirectUrl = this.$widget.find(".share-redirect-url");
|
||||
this.$loginRedirectUrl = this.$widget.find(".login-redirect-url");
|
||||
this.$redirectBareDomain = this.$widget.find(".redirect-bare-domain");
|
||||
this.$showLoginInShare = this.$widget.find(".show-login-in-share");
|
||||
|
||||
this.$shareRedirectUrl.on('change', () =>
|
||||
this.updateOption('shareRedirectUrl', this.$shareRedirectUrl.val() || 'share'));
|
||||
|
||||
this.$loginRedirectUrl.on('change', () =>
|
||||
this.updateOption('loginRedirectUrl', this.$loginRedirectUrl.val() || 'login'));
|
||||
this.$redirectBareDomain.on('change', () =>
|
||||
this.updateCheckboxOption('redirectBareDomain', this.$redirectBareDomain));
|
||||
|
||||
this.$showLoginInShare.on('change', () =>
|
||||
this.updateCheckboxOption('showLoginInShareTheme', this.$showLoginInShare));
|
||||
}
|
||||
|
||||
optionsLoaded(options) {
|
||||
this.$shareRedirectUrl.val(options.shareRedirectUrl || 'share');
|
||||
this.$loginRedirectUrl.val(options.loginRedirectUrl || 'login');
|
||||
this.setCheckboxState(this.$redirectBareDomain, options.redirectBareDomain);
|
||||
this.setCheckboxState(this.$showLoginInShare, options.showLoginInShareTheme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1562,12 +1562,10 @@
|
||||
"clipped-from": "Clipped from: %s"
|
||||
},
|
||||
"share": {
|
||||
"settings_title": "Share and Login Settings",
|
||||
"redirect_url_label": "Share Redirect URL",
|
||||
"redirect_url_description": "URL to redirect to when user is not logged in (default: 'share')",
|
||||
"login_redirect_url_label": "Login Redirect URL",
|
||||
"login_redirect_url_description": "URL to redirect to for login page (default: '/login')",
|
||||
"show_login_label": "Show login link in share theme",
|
||||
"show_login_description": "When enabled, displays a login link in the share theme interface (default: false)"
|
||||
"settings_title": "Share Settings",
|
||||
"redirect_bare_domain": "Redirect bare domain to share",
|
||||
"redirect_bare_domain_description": "When enabled, accessing the root URL (/) will redirect to the share page (/share)",
|
||||
"show_login_in_share": "Show login in share theme",
|
||||
"show_login_in_share_description": "When enabled, shows a login button in the share theme"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import clipperRoute from "./api/clipper.js";
|
||||
import similarNotesRoute from "./api/similar_notes.js";
|
||||
import keysRoute from "./api/keys.js";
|
||||
import backendLogRoute from "./api/backend_log.js";
|
||||
import statsRoute from "./api/stats.js";
|
||||
import statsRoute from "../stats/stats.js";
|
||||
import fontsRoute from "./api/fonts.js";
|
||||
import etapiTokensApiRoutes from "./api/etapi_tokens.js";
|
||||
import relationMapApiRoute from "./api/relation-map.js";
|
||||
@@ -102,6 +102,15 @@ const uploadMiddlewareWithErrorHandling = function (req: express.Request, res: e
|
||||
};
|
||||
|
||||
function register(app: express.Application) {
|
||||
// Add bare domain redirect handler
|
||||
app.use((req, res, next) => {
|
||||
if (req.path === '/' && optionService.getOption('redirectBareDomain')) {
|
||||
res.redirect('/share');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
|
||||
route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage);
|
||||
route(GET, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPasswordPage);
|
||||
@@ -110,6 +119,9 @@ function register(app: express.Application) {
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 10, // limit each IP to 10 requests per windowMs
|
||||
skipSuccessfulRequests: true // successful auth to rate-limited ETAPI routes isn't counted. However, successful auth to /login is still counted!
|
||||
message: 'Too many login attempts, please try again later.',
|
||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||
legacyHeaders: false // Disable the `X-RateLimit-*` headers
|
||||
});
|
||||
|
||||
route(PST, '/login', [loginRateLimiter], loginRoute.login);
|
||||
|
||||
Reference in New Issue
Block a user