-
Notifications
You must be signed in to change notification settings - Fork 23
Added support for the importExportRateLimiting setting, and Access-Control-Allow-Origin header added. Fixed the export of readonly pads. #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const Changeset = require('ep_etherpad-lite/static/js/Changeset'); | ||||||||||||||||||||||||||
| const padManager = require('ep_etherpad-lite/node/db/PadManager'); | ||||||||||||||||||||||||||
| const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager'); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const getMarkdownFromAtext = (pad, atext) => { | ||||||||||||||||||||||||||
| const apool = pad.apool(); | ||||||||||||||||||||||||||
|
|
@@ -311,12 +312,23 @@ const _analyzeLine = (text, aline, apool) => { | |||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const getPadMarkdown = async (pad, revNum) => { | ||||||||||||||||||||||||||
| const atext = revNum == null ? pad.atext : await pad.getInternalRevisionAText(revNum); | ||||||||||||||||||||||||||
| const atext = revNum == null|undefined ? pad.atext : await pad.getInternalRevisionAText(revNum); | ||||||||||||||||||||||||||
| return getMarkdownFromAtext(pad, atext); | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const getPadIdIfReadOnly = async (padId) => { | ||||||||||||||||||||||||||
| if(padId.startsWith("r.")) { | ||||||||||||||||||||||||||
| return await readOnlyManager.getPadId(padId); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| return padId | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+320
to
+325
|
||||||||||||||||||||||||||
| if(padId.startsWith("r.")) { | |
| return await readOnlyManager.getPadId(padId); | |
| } else { | |
| return padId | |
| } | |
| } | |
| if (padId.startsWith('r.')) { | |
| return await readOnlyManager.getPadId(padId); | |
| } else { | |
| return padId; | |
| } | |
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,27 @@ | ||||||||||||||||||||||||||||||||||
| 'use strict'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const exportMarkdown = require('./exportMarkdown'); | ||||||||||||||||||||||||||||||||||
| const settings = require('ep_etherpad-lite/node/utils/Settings'); | ||||||||||||||||||||||||||||||||||
| const rateLimit = require('express-rate-limit'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| exports.expressCreateServer = (hookName, {app}) => { | ||||||||||||||||||||||||||||||||||
| const limiter = rateLimit({ | ||||||||||||||||||||||||||||||||||
| ...settings.importExportRateLimiting, | ||||||||||||||||||||||||||||||||||
| handler: (request) => { | ||||||||||||||||||||||||||||||||||
| if (request.rateLimit.current === request.rateLimit.limit + 1) { | ||||||||||||||||||||||||||||||||||
| // when the rate limiter triggers, write a warning in the logs | ||||||||||||||||||||||||||||||||||
| console.warn('Import/Export rate limiter triggered on ' + | ||||||||||||||||||||||||||||||||||
| `"${request.originalUrl}" for IP address ${request.ip}`); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
+15
|
||||||||||||||||||||||||||||||||||
| handler: (request) => { | |
| if (request.rateLimit.current === request.rateLimit.limit + 1) { | |
| // when the rate limiter triggers, write a warning in the logs | |
| console.warn('Import/Export rate limiter triggered on ' + | |
| `"${request.originalUrl}" for IP address ${request.ip}`); | |
| } | |
| handler: (request, response, next, options) => { | |
| if (request.rateLimit.current === request.rateLimit.limit + 1) { | |
| // when the rate limiter triggers, write a warning in the logs | |
| console.warn('Import/Export rate limiter triggered on ' + | |
| `"${request.originalUrl}" for IP address ${request.ip}`); | |
| } | |
| options.handler(request, response, next, options); |
Copilot
AI
Apr 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Access-Control-Allow-Origin is set only on the successful export response. If the rate limiter triggers (or an error is thrown before line 24), the response will not include the CORS header and browsers will still report a CORS failure. Consider setting the CORS header in a middleware that runs before the limiter/handler for this route (or ensure the limiter's handler also sets it).
| app.use('/p/:padId/:revNum?/export/markdown', limiter); | |
| app.get('/p/:padId/:revNum?/export/markdown', (req, res, next) => { | |
| (async () => { | |
| const {padId, revNum} = req.params; | |
| res.attachment(`${padId}.md`); | |
| res.header('Access-Control-Allow-Origin', '*'); | |
| app.use('/p/:padId/:revNum?/export/markdown', (req, res, next) => { | |
| res.header('Access-Control-Allow-Origin', '*'); | |
| next(); | |
| }, limiter); | |
| app.get('/p/:padId/:revNum?/export/markdown', (req, res, next) => { | |
| (async () => { | |
| const {padId, revNum} = req.params; | |
| res.attachment(`${padId}.md`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revNum == null|undefineduses the bitwise OR operator, which changes the condition to effectivelyrevNum == 0and will cause the wrong revision text to be exported. Use a proper null/undefined check (for examplerevNum == null) before deciding whether to callgetInternalRevisionAText().