mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-31 19:02:59 +01:00
Merge branch 'develop'
This commit is contained in:
commit
c45b7a361e
50 changed files with 2035 additions and 1850 deletions
10
.github/workflows/backend-tests.yml
vendored
10
.github/workflows/backend-tests.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -90,7 +90,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -133,7 +133,6 @@ jobs:
|
|||
ep_font_size
|
||||
ep_hash_auth
|
||||
ep_headings2
|
||||
ep_image_upload
|
||||
ep_markdown
|
||||
ep_readonly_guest
|
||||
ep_set_title_on_pad
|
||||
|
@ -160,7 +159,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -213,7 +212,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -249,7 +248,6 @@ jobs:
|
|||
ep_font_size
|
||||
ep_hash_auth
|
||||
ep_headings2
|
||||
ep_image_upload
|
||||
ep_markdown
|
||||
ep_readonly_guest
|
||||
ep_set_title_on_pad
|
||||
|
|
2
.github/workflows/build-and-deploy-docs.yml
vendored
2
.github/workflows/build-and-deploy-docs.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
@ -44,7 +44,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
4
.github/workflows/frontend-admin-tests.yml
vendored
4
.github/workflows/frontend-admin-tests.yml
vendored
|
@ -34,7 +34,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -85,7 +85,7 @@ jobs:
|
|||
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme1\",\"is_admin\":true}}/' settings.json"
|
||||
-
|
||||
name: increase maxHttpBufferSize
|
||||
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
||||
run: "sed -i 's/\"maxHttpBufferSize\": 50000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
||||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
|
|
6
.github/workflows/frontend-tests.yml
vendored
6
.github/workflows/frontend-tests.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -93,7 +93,7 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -160,7 +160,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
6
.github/workflows/load-test.yml
vendored
6
.github/workflows/load-test.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -73,7 +73,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -144,7 +144,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
2
.github/workflows/perform-type-check.yml
vendored
2
.github/workflows/perform-type-check.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
2
.github/workflows/rate-limit.yml
vendored
2
.github/workflows/rate-limit.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -85,7 +85,6 @@ jobs:
|
|||
ep_font_size
|
||||
ep_hash_auth
|
||||
ep_headings2
|
||||
ep_image_upload
|
||||
ep_markdown
|
||||
ep_readonly_guest
|
||||
ep_set_title_on_pad
|
||||
|
|
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
@ -129,7 +129,7 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
|||
# 2.1.0
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
||||
- Added PWA support. You can now add your Etherpad instance to your home screen on your mobile device or desktop.
|
||||
- Fixed live plugin manager versions clashing. Thanks to @yacchin1205
|
||||
- Fixed a bug in the pad panel where pagination was not working correctly when sorting by pad name
|
||||
|
||||
### Compatibility changes
|
||||
|
||||
- Reintroduced APIKey.txt support. You can now switch between APIKey and OAuth2.0 authentication. This can be toggled with the setting authenticationMethod. The default is OAuth2. If you want to use the APIKey method you can set that to `apikey`.
|
||||
|
||||
|
||||
# 2.0.3
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
@ -47,8 +60,8 @@
|
|||
- Bin folder: The bin folder has been moved from the src folder to the root folder. This change was necessary as the contained scripts do not represent core functionality of the user.
|
||||
- Starting Etherpad: Etherpad can now be started with a single command: `pnpm run prod` in the root directory.
|
||||
- Installing Etherpad: Etherpad no longer symlinks itself in the root directory. This is now also taken care by pnpm, and it just creates a node_modules folder with the src directory`s ep_etherpad-lite folder
|
||||
- Plugins can now be installed simply via the command: `pnpm run install-plugins first-plugin second-plugin` or if you want to install from path you can do:
|
||||
`pnpm run install-plugins --path ../path-to-plugin`
|
||||
- Plugins can now be installed simply via the command: `pnpm run plugins i first-plugin second-plugin` or if you want to install from path you can do:
|
||||
`pnpm run plugins i --path ../path-to-plugin`
|
||||
|
||||
|
||||
# 1.9.7
|
||||
|
|
|
@ -120,7 +120,7 @@ COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./sr
|
|||
|
||||
RUN bin/installDeps.sh && \
|
||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||
pnpm run install-plugins ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||
fi
|
||||
|
||||
|
||||
|
@ -135,7 +135,7 @@ COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./sr
|
|||
|
||||
RUN bin/installDeps.sh && rm -rf ~/.npm && rm -rf ~/.local && rm -rf ~/.cache && \
|
||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||
pnpm run install-plugins ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ Alternatively, you can install plugins from the command line:
|
|||
|
||||
```sh
|
||||
cd /path/to/etherpad-lite
|
||||
pnpm run install-plugins ep_${plugin_name}
|
||||
pnpm run plugins i ep_${plugin_name}
|
||||
```
|
||||
|
||||
Also see [the plugin wiki
|
||||
|
@ -207,7 +207,7 @@ Run the following command in your Etherpad folder to get all of the features
|
|||
visible in the above demo gif:
|
||||
|
||||
```sh
|
||||
pnpm run install-plugins \
|
||||
pnpm run plugins i \
|
||||
ep_align \
|
||||
ep_comments_page \
|
||||
ep_embedded_hyperlinks2 \
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"name": "admin",
|
||||
"private": true,
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -15,25 +16,25 @@
|
|||
"devDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@types/react": "^18.2.79",
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||
"@typescript-eslint/parser": "^7.11.0",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint": "^9.2.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"i18next": "^23.11.2",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"lucide-react": "^0.372.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"i18next": "^23.11.5",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"lucide-react": "^0.381.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.51.3",
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.9",
|
||||
"vite": "^5.2.12",
|
||||
"vite-plugin-static-copy": "^1.0.3",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"zustand": "^4.5.2"
|
||||
|
|
|
@ -167,10 +167,11 @@ export const PadPage = ()=>{
|
|||
}}><ChevronLeft/><span>Previous Page</span></button>
|
||||
<span>{currentPage+1} out of {pages}</span>
|
||||
<button disabled={pages == currentPage+1} onClick={()=>{
|
||||
setCurrentPage(currentPage+1)
|
||||
const newCurrentPage = currentPage+1
|
||||
setCurrentPage(newCurrentPage)
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
offset: (Number(currentPage)-1)*searchParams.limit
|
||||
offset: (Number(newCurrentPage))*searchParams.limit
|
||||
})
|
||||
}}><span>Next Page</span><ChevronRight/></button>
|
||||
</div>
|
||||
|
|
17
bin/commonPlugins.ts
Normal file
17
bin/commonPlugins.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import {PackageData} from "ep_etherpad-lite/node/types/PackageInfo";
|
||||
import {writeFileSync} from "fs";
|
||||
import {installedPluginsPath} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
||||
const pluginsModule = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
||||
|
||||
export const persistInstalledPlugins = async () => {
|
||||
const plugins:PackageData[] = []
|
||||
const installedPlugins = {plugins: plugins};
|
||||
for (const pkg of Object.values(await pluginsModule.getPackages()) as PackageData[]) {
|
||||
installedPlugins.plugins.push({
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
});
|
||||
}
|
||||
installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
|
||||
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins));
|
||||
};
|
|
@ -1,59 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import {writeFileSync} from 'fs'
|
||||
import {linkInstaller, installedPluginsPath} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
||||
import {PackageData} from "ep_etherpad-lite/node/types/PackageInfo";
|
||||
|
||||
const pluginsModule = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
||||
if (process.argv.length === 2) {
|
||||
console.error('Expected at least one argument!');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let args = process.argv.slice(2)
|
||||
|
||||
let registryPlugins: string[] = [];
|
||||
let localPlugins: string[] = [];
|
||||
|
||||
if (args.indexOf('--path') !== -1) {
|
||||
const indexToSplit = args.indexOf('--path');
|
||||
registryPlugins = args.slice(0, indexToSplit);
|
||||
localPlugins = args.slice(indexToSplit + 1);
|
||||
} else {
|
||||
registryPlugins = args;
|
||||
}
|
||||
|
||||
const persistInstalledPlugins = async () => {
|
||||
const plugins:PackageData[] = []
|
||||
const installedPlugins = {plugins: plugins};
|
||||
for (const pkg of Object.values(await pluginsModule.getPackages()) as PackageData[]) {
|
||||
installedPlugins.plugins.push({
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
});
|
||||
}
|
||||
installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
|
||||
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins));
|
||||
};
|
||||
|
||||
async function run() {
|
||||
for (const plugin of registryPlugins) {
|
||||
console.log(`Installing plugin from registry: ${plugin}`)
|
||||
if (plugin.includes('@')) {
|
||||
const [name, version] = plugin.split('@');
|
||||
await linkInstaller.installPlugin(name, version);
|
||||
continue;
|
||||
}
|
||||
await linkInstaller.installPlugin(plugin);
|
||||
}
|
||||
|
||||
for (const plugin of localPlugins) {
|
||||
console.log(`Installing plugin from path: ${plugin}`);
|
||||
await linkInstaller.installFromPath(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await run();
|
||||
await persistInstalledPlugins();
|
||||
})();
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"name": "bin",
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.0",
|
||||
"description": "",
|
||||
"main": "checkAllPads.js",
|
||||
"directories": {
|
||||
"doc": "doc"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"ep_etherpad-lite": "workspace:../src",
|
||||
"log4js": "^6.9.1",
|
||||
"semver": "^7.6.0",
|
||||
"tsx": "^4.7.2",
|
||||
"semver": "^7.6.2",
|
||||
"tsx": "^4.10.5",
|
||||
"ueberdb2": "^4.2.63"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/node": "^20.12.13",
|
||||
"@types/semver": "^7.5.8",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
|
@ -32,7 +32,7 @@
|
|||
"rebuildPad": "node --import tsx rebuildPad.ts",
|
||||
"stalePlugins": "node --import tsx ./plugins/stalePlugins.ts",
|
||||
"checkPlugin": "node --import tsx ./plugins/checkPlugin.ts",
|
||||
"install-plugins": "node --import tsx ./installPlugins.ts"
|
||||
"plugins": "node --import tsx ./plugins.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
|
|
116
bin/plugins.ts
Normal file
116
bin/plugins.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
'use strict';
|
||||
|
||||
import {linkInstaller, checkForMigration} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
||||
import {persistInstalledPlugins} from "./commonPlugins";
|
||||
import fs from "node:fs";
|
||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||
|
||||
if (process.argv.length === 2) {
|
||||
console.error('Expected at least one argument!');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let args = process.argv.slice(2)
|
||||
|
||||
|
||||
const possibleActions = [
|
||||
"i",
|
||||
"install",
|
||||
"rm",
|
||||
"remove",
|
||||
"ls",
|
||||
"list"
|
||||
]
|
||||
|
||||
const install = ()=> {
|
||||
|
||||
let registryPlugins: string[] = [];
|
||||
let localPlugins: string[] = [];
|
||||
|
||||
if (args.indexOf('--path') !== -1) {
|
||||
const indexToSplit = args.indexOf('--path');
|
||||
registryPlugins = args.slice(1, indexToSplit);
|
||||
localPlugins = args.slice(indexToSplit + 1);
|
||||
} else {
|
||||
registryPlugins = args;
|
||||
}
|
||||
|
||||
async function run() {
|
||||
for (const plugin of registryPlugins) {
|
||||
if (possibleActions.includes(plugin)){
|
||||
continue
|
||||
}
|
||||
console.log(`Installing plugin from registry: ${plugin}`)
|
||||
if (plugin.includes('@')) {
|
||||
const [name, version] = plugin.split('@');
|
||||
await linkInstaller.installPlugin(name, version);
|
||||
continue;
|
||||
}
|
||||
await linkInstaller.installPlugin(plugin);
|
||||
}
|
||||
|
||||
for (const plugin of localPlugins) {
|
||||
console.log(`Installing plugin from path: ${plugin}`);
|
||||
await linkInstaller.installFromPath(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await checkForMigration();
|
||||
await run();
|
||||
await persistInstalledPlugins();
|
||||
})();
|
||||
}
|
||||
|
||||
const list = ()=>{
|
||||
const walk = async () => {
|
||||
const plugins = fs.readFileSync(settings.root+"/var/installed_plugins.json", "utf-8")
|
||||
const pluginNames = JSON.parse(plugins).plugins.map((plugin: any) => plugin.name).join(", ")
|
||||
|
||||
console.log("Installed plugins are:", pluginNames)
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await walk();
|
||||
})();
|
||||
}
|
||||
|
||||
const remove = (plugins: string[])=>{
|
||||
const walk = async () => {
|
||||
for (const plugin of plugins) {
|
||||
console.log(`Uninstalling plugin: ${plugin}`)
|
||||
await linkInstaller.uninstallPlugin(plugin);
|
||||
}
|
||||
await persistInstalledPlugins();
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await checkForMigration();
|
||||
await walk();
|
||||
})();
|
||||
}
|
||||
|
||||
let action = args[0];
|
||||
|
||||
switch (action) {
|
||||
case "install":
|
||||
install();
|
||||
break;
|
||||
case "i":
|
||||
install();
|
||||
break;
|
||||
case "ls":
|
||||
list();
|
||||
break;
|
||||
case "list":
|
||||
list();
|
||||
break;
|
||||
case "rm":
|
||||
remove(args.slice(1));
|
||||
break;
|
||||
default:
|
||||
console.error('Expected at least one argument!');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
working-directory: ./etherpad-lite
|
||||
run: |
|
||||
pnpm link --global $PLUGIN_NAME
|
||||
pnpm run install-plugins --path ../../plugin
|
||||
pnpm run plugins i --path ../../plugin
|
||||
env:
|
||||
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
||||
- name: Link ep_etherpad-lite
|
||||
|
|
|
@ -510,7 +510,7 @@ For the editor container, you can also make it full width by adding `full-width-
|
|||
|
||||
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE`
|
||||
| The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks.
|
||||
| `10000`
|
||||
| `50000`
|
||||
|
||||
| `LOAD_TEST`
|
||||
| Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance.
|
||||
|
|
|
@ -213,7 +213,7 @@ For the editor container, you can also make it full width by adding `full-width-
|
|||
| `FOCUS_LINE_PERCENTAGE_ARROW_UP` | Percentage of viewport height to be additionally scrolled when user presses arrow up in the line of the top of the viewport. Set to 0 to let the scroll to be handled as default by Etherpad | `0` |
|
||||
| `FOCUS_LINE_DURATION` | Time (in milliseconds) used to animate the scroll transition. Set to 0 to disable animation | `0` |
|
||||
| `FOCUS_LINE_CARET_SCROLL` | Flag to control if it should scroll when user places the caret in the last line of the viewport | `false` |
|
||||
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. | `10000` |
|
||||
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. | `50000` |
|
||||
| `LOAD_TEST` | Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance. | `false` |
|
||||
| `DUMP_ON_UNCLEAN_EXIT` | Enable dumping objects preventing a clean exit of Node.js. WARNING: this has a significant performance impact. | `false` |
|
||||
| `EXPOSE_VERSION` | Expose Etherpad version in the web interface and in the Server http header. Do not enable on production machines. | `false` |
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.1.3"
|
||||
"vitepress": "^1.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev",
|
||||
|
|
|
@ -24,7 +24,10 @@
|
|||
"test-ui:ui": "pnpm --filter ep_etherpad-lite run test-ui:ui",
|
||||
"test-admin": "pnpm --filter ep_etherpad-lite run test-admin",
|
||||
"test-admin:ui": "pnpm --filter ep_etherpad-lite run test-admin:ui",
|
||||
"install-plugins": "pnpm --filter bin run install-plugins"
|
||||
"plugins": "pnpm --filter bin run plugins",
|
||||
"install-plugins": "pnpm --filter bin run plugins i",
|
||||
"remove-plugins": "pnpm --filter bin run remove-plugins",
|
||||
"list-plugins": "pnpm --filter bin run list-plugins"
|
||||
},
|
||||
"dependencies": {
|
||||
"ep_etherpad-lite": "workspace:./src"
|
||||
|
@ -43,6 +46,6 @@
|
|||
"type": "git",
|
||||
"url": "https://github.com/ether/etherpad-lite.git"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
|
3264
pnpm-lock.yaml
3264
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -171,6 +171,13 @@
|
|||
*/
|
||||
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
||||
|
||||
/*
|
||||
The authentication method used by the server.
|
||||
The default value is sso
|
||||
If you want to use the old authentication system, change this to apikey
|
||||
*/
|
||||
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
||||
|
||||
/*
|
||||
* Node native SSL support
|
||||
*
|
||||
|
@ -537,7 +544,7 @@
|
|||
* value to work properly, but increasing the value increases susceptibility
|
||||
* to denial of service attacks (malicious clients can exhaust memory).
|
||||
*/
|
||||
"maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:10000}"
|
||||
"maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:50000}"
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -537,7 +537,7 @@
|
|||
* value to work properly, but increasing the value increases susceptibility
|
||||
* to denial of service attacks (malicious clients can exhaust memory).
|
||||
*/
|
||||
"maxHttpBufferSize": 10000
|
||||
"maxHttpBufferSize": 50000
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -586,6 +586,13 @@
|
|||
*/
|
||||
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
||||
|
||||
/*
|
||||
The authentication method used by the server.
|
||||
The default value is sso
|
||||
If you want to use the old authentication system, change this to apikey
|
||||
*/
|
||||
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
||||
|
||||
/*
|
||||
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
||||
*
|
||||
|
|
12
src/ep.json
12
src/ep.json
|
@ -57,6 +57,12 @@
|
|||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pwa",
|
||||
"hooks": {
|
||||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/pwa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "apicalls",
|
||||
"hooks": {
|
||||
|
@ -112,12 +118,6 @@
|
|||
"hooks": {
|
||||
"expressPreSession": "ep_etherpad-lite/node/hooks/express/openapi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ep_message_all",
|
||||
"client_hooks": {
|
||||
"handleClientMessage_shoutMessage": "ep_etherpad-lite/static/js/messageHandler"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"Bellayet",
|
||||
"Greatder",
|
||||
"Nasir8891",
|
||||
"RiazACU",
|
||||
"Sankarshan",
|
||||
"Sibabrata Banerjee",
|
||||
"আজিজ",
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"admin_settings.current_example-devel": "Юлгю хазырлау джарашдырыуланы шаблону",
|
||||
"admin_settings.current_example-prod": "Юлгю чыгъарыу джарашдырыуланы шаблону",
|
||||
"admin_settings.current_restart.value": "Etherpad-ны джангыдан башлат",
|
||||
"admin_settings.current_save.value": "Джарашдырыуланы Сакъла",
|
||||
"admin_settings.current_save.value": "Джарашдырыуланы Сакъландыр",
|
||||
"admin_settings.page-title": "Джарашдырыула — Etherpad",
|
||||
"index.newPad": "Джангы Блокнот",
|
||||
"index.createOpenPad": "неда бу ат бла Блокнот болдур/ач:",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"authors": [
|
||||
"Aalam",
|
||||
"Babanwalia",
|
||||
"Cabal",
|
||||
"Tow",
|
||||
"ਗੁਰਪ੍ਰੀਤ ਹੁੰਦਲ",
|
||||
"ਪ੍ਰਚਾਰਕ"
|
||||
|
@ -37,7 +38,7 @@
|
|||
"pad.settings.stickychat": "ਹਮੇਸ਼ਾ ਸਕਰੀਨ ਉੱਤੇ ਗੱਲ ਕਰੋ",
|
||||
"pad.settings.chatandusers": "ਗੱਲ-ਬਾਤ ਅਤੇ ਵਰਤੋਂਕਾਰ ਦਿਖਾਵੋ",
|
||||
"pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ",
|
||||
"pad.settings.linenocheck": "ਲਾਈਨ ਨੰਬਰ",
|
||||
"pad.settings.linenocheck": "ਲਕੀਰ ਨੰਬਰ",
|
||||
"pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?",
|
||||
"pad.settings.fontType": "ਫੋਂਟ ਕਿਸਮ:",
|
||||
"pad.settings.fontType.normal": "ਸਧਾਰਨ",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"Hzy980512",
|
||||
"JuneAugust",
|
||||
"Lakejason0",
|
||||
"LittlePaw365",
|
||||
"Liuxinyu970226",
|
||||
"Qiyue2001",
|
||||
"Shangkuanlc",
|
||||
|
@ -57,7 +58,7 @@
|
|||
"admin_settings.current_save.value": "保存设置",
|
||||
"admin_settings.page-title": "设置 - Etherpad",
|
||||
"index.newPad": "新记事本",
|
||||
"index.createOpenPad": "或创建/打开一个名为以下的记事本:",
|
||||
"index.createOpenPad": "或创建/打开以下名称的记事本:",
|
||||
"index.openPad": "打开一个现有的记事本,名称为:",
|
||||
"pad.toolbar.bold.title": "粗体(Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "斜体(Ctrl-I)",
|
||||
|
|
|
@ -27,7 +27,7 @@ import createHTTPError from 'http-errors';
|
|||
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||
import {publicKeyExported} from "../security/OAuth2Provider";
|
||||
import {jwtVerify} from "jose";
|
||||
|
||||
import {apikey} from './APIKeyHandler'
|
||||
// a list of all functions
|
||||
const version:MapArrayType<any> = {};
|
||||
|
||||
|
@ -149,9 +149,11 @@ exports.version = version;
|
|||
|
||||
|
||||
type APIFields = {
|
||||
apikey: string;
|
||||
api_key: string;
|
||||
padID: string;
|
||||
padName: string;
|
||||
authorization: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,9 +162,9 @@ type APIFields = {
|
|||
* @param {String} functionName the name of the called function
|
||||
* @param fields the params of the called function
|
||||
* @param req express request object
|
||||
* @param res express response object
|
||||
*/
|
||||
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields, req: Http2ServerRequest, res: Http2ServerResponse) {
|
||||
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields,
|
||||
req: Http2ServerRequest) {
|
||||
// say goodbye if this is an unknown API version
|
||||
if (!(apiVersion in version)) {
|
||||
throw new createHTTPError.NotFound('no such api version');
|
||||
|
@ -173,19 +175,25 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
|||
throw new createHTTPError.NotFound('no such function');
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (apikey !== null && apikey.trim().length > 0) {
|
||||
fields.apikey = fields.apikey || fields.api_key || fields.authorization;
|
||||
// API key is configured, check if it is valid
|
||||
if (fields.apikey !== apikey!.trim()) {
|
||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||
}
|
||||
} else {
|
||||
if(!req.headers.authorization) {
|
||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||
}
|
||||
|
||||
try {
|
||||
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
||||
requiredClaims: ["admin"]})
|
||||
|
||||
} catch (e) {
|
||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||
throw new createHTTPError.Unauthorized('no or wrong OAuth token');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// sanitize any padIDs before continuing
|
||||
if (fields.padID) {
|
||||
|
|
25
src/node/handler/APIKeyHandler.ts
Normal file
25
src/node/handler/APIKeyHandler.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
const absolutePaths = require('../utils/AbsolutePaths');
|
||||
import fs from 'fs';
|
||||
import log4js from 'log4js';
|
||||
const randomString = require('../utils/randomstring');
|
||||
const argv = require('../utils/Cli').argv;
|
||||
const settings = require('../utils/Settings');
|
||||
|
||||
const apiHandlerLogger = log4js.getLogger('APIHandler');
|
||||
|
||||
// ensure we have an apikey
|
||||
export let apikey:string|null = null;
|
||||
const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt');
|
||||
|
||||
|
||||
if(settings.authenticationMethod === 'apikey') {
|
||||
try {
|
||||
apikey = fs.readFileSync(apikeyFilename, 'utf8');
|
||||
apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`);
|
||||
} catch (e) {
|
||||
apiHandlerLogger.info(
|
||||
`Api key file "${apikeyFilename}" not found. Creating with random contents.`);
|
||||
apikey = randomString(32);
|
||||
fs.writeFileSync(apikeyFilename, apikey!, 'utf8');
|
||||
}
|
||||
}
|
|
@ -2,11 +2,13 @@
|
|||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import express from "express";
|
||||
import * as url from "node:url";
|
||||
import {MapArrayType} from "../../types/MapType";
|
||||
|
||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||
|
||||
const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin');
|
||||
|
||||
const ADMIN_PATH = path.join(settings.root, 'src', 'templates');
|
||||
const PROXY_HEADER = "x-proxy-path"
|
||||
/**
|
||||
* Add the admin navigation link
|
||||
* @param hookName {String} the name of the hook
|
||||
|
@ -14,12 +16,71 @@ const ADMIN_PATH = path.join(settings.root, 'src', 'templates', 'admin');
|
|||
* @param {Function} cb the callback function
|
||||
* @return {*}
|
||||
*/
|
||||
exports.expressCreateServer = (hookName:string, args: ArgsExpressType, cb:Function): any => {
|
||||
args.app.use('/admin/', express.static(path.join(__dirname, '../../../templates/admin'), {maxAge: 1000 * 60 * 60 * 24}));
|
||||
args.app.get('/admin/*', (_request:any, response:any)=>{
|
||||
response.sendFile(path.resolve(__dirname,'../../../templates/admin', 'index.html'));
|
||||
} )
|
||||
args.app.get('/admin', (req:any, res:any, next:Function) => {
|
||||
exports.expressCreateServer = (hookName: string, args: ArgsExpressType, cb: Function): any => {
|
||||
|
||||
if (!fs.existsSync(ADMIN_PATH)) {
|
||||
console.error('admin template not found, skipping admin interface. You need to rebuild it in /admin with pnpm run build-copy')
|
||||
return cb();
|
||||
}
|
||||
args.app.get('/admin/*', (req: any, res: any) => {
|
||||
// parse URL
|
||||
const parsedUrl = url.parse(req.url);
|
||||
// extract URL path
|
||||
let pathname = ADMIN_PATH + `${parsedUrl.pathname}`;
|
||||
// based on the URL path, extract the file extension. e.g. .js, .doc, ...
|
||||
let ext = path.parse(pathname).ext;
|
||||
// maps file extension to MIME typere
|
||||
const map: MapArrayType<string> = {
|
||||
'.ico': 'image/x-icon',
|
||||
'.html': 'text/html',
|
||||
'.js': 'text/javascript',
|
||||
'.json': 'application/json',
|
||||
'.css': 'text/css',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.wav': 'audio/wav',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.pdf': 'application/pdf',
|
||||
'.doc': 'application/msword'
|
||||
};
|
||||
|
||||
fs.exists(pathname, function (exist) {
|
||||
if (!exist) {
|
||||
// if the file is not found, return 404
|
||||
res.statusCode = 200;
|
||||
pathname = ADMIN_PATH + "/admin/index.html"
|
||||
ext = path.parse(pathname).ext;
|
||||
}
|
||||
|
||||
// if is a directory search for index file matching the extension
|
||||
if (fs.statSync(pathname).isDirectory()) {
|
||||
pathname = pathname + '/index.html';
|
||||
ext = path.parse(pathname).ext;
|
||||
}
|
||||
|
||||
// read file from file system
|
||||
fs.readFile(pathname, function (err, data) {
|
||||
if (err) {
|
||||
res.statusCode = 500;
|
||||
res.end(`Error getting the file: ${err}.`);
|
||||
} else {
|
||||
let dataToSend:Buffer|string = data
|
||||
// if the file is found, set Content-type and send data
|
||||
res.setHeader('Content-type', map[ext] || 'text/plain');
|
||||
if (ext === ".html" || ext === ".js" || ext === ".css") {
|
||||
if (req.header(PROXY_HEADER)) {
|
||||
let string = data.toString()
|
||||
dataToSend = string.replaceAll("/admin", req.header(PROXY_HEADER) + "/admin")
|
||||
dataToSend = dataToSend.replaceAll("/socket.io", req.header(PROXY_HEADER) + "/socket.io")
|
||||
}
|
||||
}
|
||||
res.end(dataToSend);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
args.app.get('/admin', (req: any, res: any, next: Function) => {
|
||||
if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/');
|
||||
})
|
||||
return cb();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {PadQueryResult, PadSearchQuery} from "../../types/PadSearchQuery";
|
||||
import {PadType} from "../../types/PadType";
|
||||
import log4js from 'log4js';
|
||||
|
||||
const eejs = require('../../eejs');
|
||||
const fsp = require('fs').promises;
|
||||
|
@ -15,6 +16,7 @@ const api = require('../../db/API');
|
|||
|
||||
|
||||
const queryPadLimit = 12;
|
||||
const logger = log4js.getLogger('adminSettings');
|
||||
|
||||
|
||||
exports.socketio = (hookName: string, {io}: any) => {
|
||||
|
@ -28,7 +30,7 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
try {
|
||||
data = await fsp.readFile(settings.settingsFilename, 'utf8');
|
||||
} catch (err) {
|
||||
return console.log(err);
|
||||
return logger.error(`Error loading settings: ${err}`);
|
||||
}
|
||||
// if showSettingsInAdminPage is set to false, then return NOT_ALLOWED in the result
|
||||
if (settings.showSettingsInAdminPage === false) {
|
||||
|
@ -39,8 +41,12 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
});
|
||||
|
||||
socket.on('saveSettings', async (newSettings: string) => {
|
||||
console.log('Admin request to save settings through a socket on /admin/settings');
|
||||
logger.info('Admin request to save settings through a socket on /admin/settings');
|
||||
try {
|
||||
await fsp.writeFile(settings.settingsFilename, newSettings);
|
||||
} catch (err) {
|
||||
logger.error(`Error saving settings: ${err}`);
|
||||
}
|
||||
socket.emit('saveprogress', 'saved');
|
||||
});
|
||||
|
||||
|
@ -210,6 +216,7 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
socket.on('deletePad', async (padId: string) => {
|
||||
const padExists = await padManager.doesPadExists(padId);
|
||||
if (padExists) {
|
||||
logger.info(`Deleting pad: ${padId}`);
|
||||
const pad = await padManager.getPad(padId);
|
||||
await pad.remove();
|
||||
socket.emit('results:deletePad', padId);
|
||||
|
@ -217,7 +224,7 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
})
|
||||
|
||||
socket.on('restartServer', async () => {
|
||||
console.log('Admin request to restart server through a socket on /admin/settings');
|
||||
logger.info('Admin request to restart server through a socket on /admin/settings');
|
||||
settings.reloadSettings();
|
||||
await plugins.update();
|
||||
await hooks.aCallAll('loadSettings', {settings});
|
||||
|
@ -230,4 +237,3 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
const searchPad = async (query: PadSearchQuery) => {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -608,7 +608,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => {
|
|||
for (const funcName of Object.keys(apiHandler.version[version])) {
|
||||
const handler = async (c: any, req:any, res:any) => {
|
||||
// parse fields from request
|
||||
const {header, params, query} = c.request;
|
||||
const {headers, params, query} = c.request;
|
||||
|
||||
// read form data if method was POST
|
||||
let formData:MapArrayType<any> = {};
|
||||
|
@ -622,8 +622,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => {
|
|||
}
|
||||
}
|
||||
|
||||
const fields = Object.assign({}, header, params, query, formData);
|
||||
|
||||
const fields = Object.assign({}, headers, params, query, formData);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`);
|
||||
}
|
||||
|
|
32
src/node/hooks/express/pwa.ts
Normal file
32
src/node/hooks/express/pwa.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
const settings = require('../../utils/Settings');
|
||||
|
||||
const pwa = {
|
||||
name: settings.title || "Etherpad",
|
||||
short_name: settings.title,
|
||||
description: "A collaborative online editor",
|
||||
icons: [
|
||||
{
|
||||
"src": "/static/skins/colibris/images/fond.jpg",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
type: "image/png"
|
||||
}
|
||||
],
|
||||
start_url: "/",
|
||||
display: "fullscreen",
|
||||
theme_color: "#0f775b",
|
||||
background_color: "#0f775b"
|
||||
}
|
||||
|
||||
exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
args.app.get('/manifest.json', (req:any, res:any) => {
|
||||
res.json(pwa);
|
||||
});
|
||||
|
||||
return cb();
|
||||
}
|
|
@ -45,5 +45,10 @@ for (let i = 0; i < argv.length; i++) {
|
|||
exports.argv.sessionkey = arg;
|
||||
}
|
||||
|
||||
// Override location of APIKEY.txt file
|
||||
if (prevArg === '--apikey') {
|
||||
exports.argv.apikey = arg;
|
||||
}
|
||||
|
||||
prevArg = arg;
|
||||
}
|
||||
|
|
|
@ -153,9 +153,18 @@ exports.socketIo = {
|
|||
* properly, but increasing the value increases susceptibility to denial of service attacks
|
||||
* (malicious clients can exhaust memory).
|
||||
*/
|
||||
maxHttpBufferSize: 10000,
|
||||
maxHttpBufferSize: 50000,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
The authentication method used by the server.
|
||||
The default value is sso
|
||||
If you want to use the old authentication system, change this to apikey
|
||||
*/
|
||||
exports.authenticationMethod = 'sso'
|
||||
|
||||
|
||||
/*
|
||||
* The Type of the database
|
||||
*/
|
||||
|
@ -519,6 +528,8 @@ exports.getGitCommit = () => {
|
|||
// Return etherpad version from package.json
|
||||
exports.getEpVersion = () => require('../../package.json').version;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Receives a settingsObj and, if the property name is a valid configuration
|
||||
* item, stores it in the module's exported properties via a side effect.
|
||||
|
@ -745,7 +756,6 @@ const lookupEnvironmentVariables = (obj: MapArrayType<any>) => {
|
|||
|
||||
//console.log(root.collectFromLeafsUpwards())
|
||||
const rooting = root.collectFromLeafsUpwards()
|
||||
console.log("Rooting is", rooting.ADMIN)
|
||||
obj = Object.assign(obj, rooting)
|
||||
return obj;
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"async": "^3.2.5",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"clean-css": "^5.3.3",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cross-spawn": "^7.0.3",
|
||||
|
@ -45,13 +45,13 @@
|
|||
"find-root": "1.1.0",
|
||||
"formidable": "^3.5.1",
|
||||
"http-errors": "^2.0.0",
|
||||
"jose": "^5.2.4",
|
||||
"jose": "^5.3.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsdom": "^24.0.0",
|
||||
"jsdom": "^24.1.0",
|
||||
"jsonminify": "0.4.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"languages4translatewiki": "0.1.3",
|
||||
"live-plugin-manager": "^0.20.0",
|
||||
"live-plugin-manager": "^1.0.0",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"log4js": "^6.9.1",
|
||||
"measured-core": "^2.0.0",
|
||||
|
@ -64,14 +64,14 @@
|
|||
"rehype-minify-whitespace": "^6.0.0",
|
||||
"resolve": "1.22.8",
|
||||
"security": "1.0.0",
|
||||
"semver": "^7.6.0",
|
||||
"semver": "^7.6.2",
|
||||
"socket.io": "^4.7.5",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"superagent": "^8.1.2",
|
||||
"superagent": "^9.0.2",
|
||||
"terser": "^5.30.3",
|
||||
"threads": "^1.7.0",
|
||||
"tinycon": "0.6.8",
|
||||
"tsx": "^4.7.2",
|
||||
"tsx": "^4.10.5",
|
||||
"ueberdb2": "^4.2.63",
|
||||
"underscore": "1.13.6",
|
||||
"unorm": "1.6.0",
|
||||
|
@ -83,21 +83,21 @@
|
|||
"etherpad-lite": "node/server.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.43.1",
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@types/async": "^3.2.24",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/http-errors": "^2.0.4",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/node": "^20.12.13",
|
||||
"@types/oidc-provider": "^8.4.4",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@types/underscore": "^1.11.15",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint": "^9.2.0",
|
||||
"eslint-config-etherpad": "^4.0.4",
|
||||
"etherpad-cli-client": "^3.0.2",
|
||||
"mocha": "^10.4.0",
|
||||
|
@ -105,9 +105,9 @@
|
|||
"nodeify": "^1.0.1",
|
||||
"openapi-schema-validation": "^0.4.2",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sinon": "^17.0.1",
|
||||
"sinon": "^18.0.0",
|
||||
"split-grid": "^1.0.11",
|
||||
"supertest": "^6.3.4",
|
||||
"supertest": "^7.0.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -123,8 +123,8 @@
|
|||
"lint": "eslint .",
|
||||
"test": "mocha --import=tsx --timeout 120000 --recursive tests/backend/specs/**.ts ../node_modules/ep_*/static/tests/backend/specs/**",
|
||||
"test-container": "mocha --import=tsx --timeout 5000 tests/container/specs/api",
|
||||
"dev": "node --import tsx node/server.ts",
|
||||
"prod": "node --import tsx node/server.ts",
|
||||
"dev": "node --require tsx/cjs node/server.ts",
|
||||
"prod": "node --require tsx/cjs node/server.ts",
|
||||
"ts-check": "tsc --noEmit",
|
||||
"ts-check:watch": "tsc --noEmit --watch",
|
||||
"test-ui": "npx playwright test tests/frontend-new/specs",
|
||||
|
@ -132,6 +132,6 @@
|
|||
"test-admin": "npx playwright test tests/frontend-new/admin-spec --workers 1",
|
||||
"test-admin:ui": "npx playwright test tests/frontend-new/admin-spec --ui --workers 1"
|
||||
},
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
|
|
@ -100,6 +100,10 @@ export const checkForMigration = async () => {
|
|||
|
||||
for (let file of files){
|
||||
const moduleName = path.basename(file);
|
||||
if (moduleName === '.versions') {
|
||||
// Skip the directory using live-plugin-manager
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
await fs.access(path.join(node_modules, moduleName), fs.constants.F_OK);
|
||||
logger.debug(`plugin ${moduleName} already exists in node_modules`);
|
||||
|
|
|
@ -129,8 +129,7 @@ exports.getPackages = async () => {
|
|||
if (!plugin.name.startsWith(exports.prefix)) {
|
||||
continue;
|
||||
}
|
||||
plugin.realPath = await fs.realpath(plugin.location);
|
||||
plugin.path = plugin.realPath;
|
||||
plugin.path = plugin.realPath = plugin.location;
|
||||
newDependencies[plugin.name] = plugin;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ const tsortTest = () => {
|
|||
];
|
||||
|
||||
let sorted = tsort(edges);
|
||||
console.log(sorted);
|
||||
|
||||
// example 2: failure ( A > B > C > A )
|
||||
edges = [
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title><%- padId %></title>
|
||||
<meta name="generator" content="Etherpad"/>
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="generator" content="Etherpad"/>
|
||||
<meta name="author" content="Etherpad"/>
|
||||
<meta name="changedby" content="Etherpad"/>
|
||||
<meta charset="utf-8"/>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<title><%=settings.title%></title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>JavaScript license information</title>
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<% e.begin_block("htmlHead"); %>
|
||||
<% e.end_block(); %>
|
||||
<title><%=settings.title%></title>
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<script>
|
||||
/*
|
||||
|@licstart The following is the entire license notice for the
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
</script>
|
||||
<script src="../../static/js/basic_error_handler.js?v=<%=settings.randomVersionString%>"></script>
|
||||
<meta charset="utf-8">
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.9"
|
||||
"vite": "^5.2.12"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue