diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.js b/src/public/app/widgets/type_widgets/options/other/share_settings.js index bee1b56fc..c0727a852 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.js +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.js @@ -6,31 +6,19 @@ const TPL = `

${t('share.settings_title')}

- - -

${t('share.redirect_url_description')}

+
+ + +

${t('share.redirect_bare_domain_description')}

+
- - -

${t('share.login_redirect_url_description')}

-
- -
- -

${t('share.show_login_description')}

+
+ + +

${t('share.show_login_in_share_description')}

+
`; @@ -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); } } diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 76236412c..26a8f533d 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -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" } } diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 52faa1b57..1e11f65cb 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -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);