mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-01-23 02:35:34 +00:00
chore: added esm
This commit is contained in:
parent
aa766825cb
commit
7339c5a1b2
37 changed files with 2793 additions and 1155 deletions
1504
pnpm-lock.yaml
generated
1504
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,3 +8,4 @@ onlyBuiltDependencies:
|
|||
- '@scarf/scarf'
|
||||
- '@swc/core'
|
||||
- esbuild
|
||||
- protobufjs
|
||||
|
|
|
|||
1728
src/node/db/API.ts
1728
src/node/db/API.ts
File diff suppressed because it is too large
Load diff
|
|
@ -19,12 +19,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const db = require('./DB');
|
||||
const CustomError = require('../utils/customError');
|
||||
import db from './DB';
|
||||
import CustomError from '../utils/customError';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
import padutils, {randomString} from "../../static/js/pad_utils";
|
||||
import {randomString} from "../../static/js/pad_utils";
|
||||
|
||||
exports.getColorPalette = () => [
|
||||
const getColorPalette = () => [
|
||||
'#ffc7c7',
|
||||
'#fff1c7',
|
||||
'#e3ffc7',
|
||||
|
|
@ -95,7 +95,7 @@ exports.getColorPalette = () => [
|
|||
* Checks if the author exists
|
||||
* @param {String} authorID The id of the author
|
||||
*/
|
||||
exports.doesAuthorExist = async (authorID: string) => {
|
||||
const doesAuthorExist = async (authorID: string) => {
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
return author != null;
|
||||
|
|
@ -105,7 +105,7 @@ exports.doesAuthorExist = async (authorID: string) => {
|
|||
exported for backwards compatibility
|
||||
@param {String} authorID The id of the author
|
||||
*/
|
||||
exports.doesAuthorExists = exports.doesAuthorExist;
|
||||
const doesAuthorExists = doesAuthorExist;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -120,7 +120,7 @@ const mapAuthorWithDBKey = async (mapperkey: string, mapper:string) => {
|
|||
|
||||
if (author == null) {
|
||||
// there is no author with this mapper, so create one
|
||||
const author = await exports.createAuthor(null);
|
||||
const author = await createAuthor(null);
|
||||
|
||||
// create the token2author relation
|
||||
await db.set(`${mapperkey}:${mapper}`, author.authorID);
|
||||
|
|
@ -155,36 +155,24 @@ const getAuthor4Token = async (token: string) => {
|
|||
* @param {Object} user
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
exports.getAuthorId = async (token: string, user: object) => {
|
||||
const getAuthorId = async (token: string, user: object | undefined) => {
|
||||
const context = {dbKey: token, token, user};
|
||||
let [authorId] = await hooks.aCallFirst('getAuthorId', context);
|
||||
if (!authorId) authorId = await getAuthor4Token(context.dbKey);
|
||||
return authorId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a token.
|
||||
*
|
||||
* @deprecated Use `getAuthorId` instead.
|
||||
* @param {String} token The token
|
||||
*/
|
||||
exports.getAuthor4Token = async (token: string) => {
|
||||
padutils.warnDeprecated(
|
||||
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
|
||||
return await getAuthor4Token(token);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a mapper.
|
||||
* @param {String} authorMapper The mapper
|
||||
* @param {String} name The name of the author (optional)
|
||||
*/
|
||||
exports.createAuthorIfNotExistsFor = async (authorMapper: string, name: string) => {
|
||||
const createAuthorIfNotExistsFor = async (authorMapper: string, name: string) => {
|
||||
const author = await mapAuthorWithDBKey('mapper2author', authorMapper);
|
||||
|
||||
if (name) {
|
||||
// set the name of this author
|
||||
await exports.setAuthorName(author.authorID, name);
|
||||
await setAuthorName(author.authorID, name);
|
||||
}
|
||||
|
||||
return author;
|
||||
|
|
@ -195,13 +183,13 @@ exports.createAuthorIfNotExistsFor = async (authorMapper: string, name: string)
|
|||
* Internal function that creates the database entry for an author
|
||||
* @param {String} name The name of the author
|
||||
*/
|
||||
exports.createAuthor = async (name: string) => {
|
||||
const createAuthor = async (name: string| null) => {
|
||||
// create the new author name
|
||||
const author = `a.${randomString(16)}`;
|
||||
|
||||
// create the globalAuthors db entry
|
||||
const authorObj = {
|
||||
colorId: Math.floor(Math.random() * (exports.getColorPalette().length)),
|
||||
colorId: Math.floor(Math.random() * (getColorPalette().length)),
|
||||
name,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
|
@ -216,41 +204,41 @@ exports.createAuthor = async (name: string) => {
|
|||
* Returns the Author Obj of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthor = async (author: string) => await db.get(`globalAuthor:${author}`);
|
||||
const getAuthor = async (author: string) => await db.get(`globalAuthor:${author}`);
|
||||
|
||||
/**
|
||||
* Returns the color Id of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthorColorId = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['colorId']);
|
||||
const getAuthorColorId = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['colorId']);
|
||||
|
||||
/**
|
||||
* Sets the color Id of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} colorId The color id of the author
|
||||
*/
|
||||
exports.setAuthorColorId = async (author: string, colorId: string) => await db.setSub(
|
||||
const setAuthorColorId = async (author: string, colorId: string) => await db.setSub(
|
||||
`globalAuthor:${author}`, ['colorId'], colorId);
|
||||
|
||||
/**
|
||||
* Returns the name of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthorName = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['name']);
|
||||
const getAuthorName = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['name']);
|
||||
|
||||
/**
|
||||
* Sets the name of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} name The name of the author
|
||||
*/
|
||||
exports.setAuthorName = async (author: string, name: string) => await db.setSub(
|
||||
const setAuthorName = async (author: string, name: string|null) => await db.setSub(
|
||||
`globalAuthor:${author}`, ['name'], name);
|
||||
|
||||
/**
|
||||
* Returns an array of all pads this author contributed to
|
||||
* @param {String} authorID The id of the author
|
||||
*/
|
||||
exports.listPadsOfAuthor = async (authorID: string) => {
|
||||
const listPadsOfAuthor = async (authorID: string) => {
|
||||
/* There are two other places where this array is manipulated:
|
||||
* (1) When the author is added to a pad, the author object is also updated
|
||||
* (2) When a pad is deleted, each author of that pad is also updated
|
||||
|
|
@ -275,7 +263,7 @@ exports.listPadsOfAuthor = async (authorID: string) => {
|
|||
* @param {String} authorID The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.addPad = async (authorID: string, padID: string) => {
|
||||
const addPad = async (authorID: string, padID: string) => {
|
||||
// get the entry
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
|
|
@ -302,7 +290,7 @@ exports.addPad = async (authorID: string, padID: string) => {
|
|||
* @param {String} authorID The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.removePad = async (authorID: string, padID: string) => {
|
||||
const removePad = async (authorID: string, padID: string) => {
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
if (author == null) return;
|
||||
|
|
@ -313,3 +301,21 @@ exports.removePad = async (authorID: string, padID: string) => {
|
|||
await db.set(`globalAuthor:${authorID}`, author);
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
doesAuthorExist,
|
||||
doesAuthorExists,
|
||||
getAuthor4Token,
|
||||
getAuthorId,
|
||||
createAuthorIfNotExistsFor,
|
||||
createAuthor,
|
||||
getAuthor,
|
||||
getAuthorColorId,
|
||||
setAuthorColorId,
|
||||
getAuthorName,
|
||||
setAuthorName,
|
||||
listPadsOfAuthor,
|
||||
addPad,
|
||||
removePad,
|
||||
getColorPalette,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,37 +24,56 @@
|
|||
import {Database, DatabaseType} from 'ueberdb2';
|
||||
import settings from '../utils/Settings';
|
||||
import log4js from 'log4js';
|
||||
const stats = require('../stats')
|
||||
|
||||
import stats from '../stats'
|
||||
const logger = log4js.getLogger('ueberDB');
|
||||
|
||||
/**
|
||||
* The UeberDB Object that provides the database functions
|
||||
*/
|
||||
exports.db = null;
|
||||
export let db: Database | null = null;
|
||||
|
||||
|
||||
export type DBFunctionsPromisified = {
|
||||
get: (key: string, cb?: Function) => Promise<any>,
|
||||
set: (key:string, value:object|string, callback?:Function) => Promise<void>,
|
||||
findKeys: (key:string, notKey:string|null, callback?:Function) => Promise<string[]>,
|
||||
getSub: (key: string, sub: string[]) => Promise<any>,
|
||||
setSub: (key: string, sub: string[], value: any) => Promise<void>,
|
||||
remove: (key:string, cb?: Function| null) => Promise<void>,
|
||||
}
|
||||
|
||||
export const asyncFunctions: DBFunctionsPromisified = {
|
||||
|
||||
} as DBFunctionsPromisified
|
||||
|
||||
export default asyncFunctions
|
||||
|
||||
/**
|
||||
* Initializes the database with the settings provided by the settings module
|
||||
*/
|
||||
exports.init = async () => {
|
||||
exports.db = new Database(settings.dbType as DatabaseType, settings.dbSettings, null, logger);
|
||||
await exports.db.init();
|
||||
if (exports.db.metrics != null) {
|
||||
for (const [metric, value] of Object.entries(exports.db.metrics)) {
|
||||
export const init = async () => {
|
||||
db = new Database(settings.dbType as DatabaseType, settings.dbSettings, null, logger);
|
||||
await db.init();
|
||||
if (db.metrics != null) {
|
||||
for (const [metric, value] of Object.entries(db.metrics)) {
|
||||
if (typeof value !== 'number') continue;
|
||||
stats.gauge(`ueberdb_${metric}`, () => exports.db.metrics[metric]);
|
||||
stats.gauge(`ueberdb_${metric}`, () => db!.metrics[metric]);
|
||||
}
|
||||
}
|
||||
for (const fn of ['get', 'set', 'findKeys', 'getSub', 'setSub', 'remove']) {
|
||||
const f = exports.db[fn];
|
||||
exports[fn] = async (...args:string[]) => await f.call(exports.db, ...args);
|
||||
Object.setPrototypeOf(exports[fn], Object.getPrototypeOf(f));
|
||||
Object.defineProperties(exports[fn], Object.getOwnPropertyDescriptors(f));
|
||||
// @ts-ignore
|
||||
const f = db[fn];
|
||||
// @ts-ignore
|
||||
asyncFunctions[fn] = async (...args:string[]) => await f.call(db, ...args);
|
||||
// @ts-ignore
|
||||
Object.setPrototypeOf(asyncFunctions[fn], Object.getPrototypeOf(f));
|
||||
// @ts-ignore
|
||||
Object.defineProperties(asyncFunctions[fn], Object.getOwnPropertyDescriptors(f));
|
||||
}
|
||||
};
|
||||
|
||||
exports.shutdown = async (hookName: string, context:any) => {
|
||||
if (exports.db != null) await exports.db.close();
|
||||
exports.db = null;
|
||||
export const shutdown = async (hookName: string, context:any) => {
|
||||
if (db != null) await db.close();
|
||||
db = null;
|
||||
logger.log('Database closed');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const CustomError = require('../utils/customError');
|
||||
import CustomError from '../utils/customError';
|
||||
import {randomString} from "../../static/js/pad_utils";
|
||||
const db = require('./DB');
|
||||
const padManager = require('./PadManager');
|
||||
const sessionManager = require('./SessionManager');
|
||||
import db from './DB';
|
||||
import padManager from './PadManager';
|
||||
import sessionManager from './SessionManager';
|
||||
|
||||
/**
|
||||
* Lists all groups
|
||||
* @return {Promise<{groupIDs: string[]}>} The ids of all groups
|
||||
*/
|
||||
exports.listAllGroups = async () => {
|
||||
export const listAllGroups = async () => {
|
||||
let groups = await db.get('groups');
|
||||
groups = groups || {};
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ exports.listAllGroups = async () => {
|
|||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<void>} Resolves when the group is deleted
|
||||
*/
|
||||
exports.deleteGroup = async (groupID: string): Promise<void> => {
|
||||
export const deleteGroup = async (groupID: string): Promise<void> => {
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
||||
// ensure group exists
|
||||
|
|
@ -82,7 +82,7 @@ exports.deleteGroup = async (groupID: string): Promise<void> => {
|
|||
* @param {String} groupID the id of the group to delete
|
||||
* @return {Promise<boolean>} Resolves to true if the group exists
|
||||
*/
|
||||
exports.doesGroupExist = async (groupID: string) => {
|
||||
const doesGroupExist = async (groupID: string) => {
|
||||
// try to get the group entry
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ exports.doesGroupExist = async (groupID: string) => {
|
|||
* Creates a new group
|
||||
* @return {Promise<{groupID: string}>} the id of the new group
|
||||
*/
|
||||
exports.createGroup = async () => {
|
||||
const createGroup = async () => {
|
||||
const groupID = `g.${randomString(16)}`;
|
||||
await db.set(`group:${groupID}`, {pads: {}, mappings: {}});
|
||||
// Add the group to the `groups` record after the group's individual record is created so that
|
||||
|
|
@ -108,13 +108,13 @@ exports.createGroup = async () => {
|
|||
* @param groupMapper the mapper of the group
|
||||
* @return {Promise<{groupID: string}|{groupID: *}>} a promise that resolves to the group ID
|
||||
*/
|
||||
exports.createGroupIfNotExistsFor = async (groupMapper: string|object) => {
|
||||
const createGroupIfNotExistsFor = async (groupMapper: string|object) => {
|
||||
if (typeof groupMapper !== 'string') {
|
||||
throw new CustomError('groupMapper is not a string', 'apierror');
|
||||
}
|
||||
const groupID = await db.get(`mapper2group:${groupMapper}`);
|
||||
if (groupID && await exports.doesGroupExist(groupID)) return {groupID};
|
||||
const result = await exports.createGroup();
|
||||
if (groupID && await doesGroupExist(groupID)) return {groupID};
|
||||
const result = await createGroup();
|
||||
await Promise.all([
|
||||
db.set(`mapper2group:${groupMapper}`, result.groupID),
|
||||
// Remember the mapping in the group record so that it can be cleaned up when the group is
|
||||
|
|
@ -134,12 +134,12 @@ exports.createGroupIfNotExistsFor = async (groupMapper: string|object) => {
|
|||
* @param {String} authorId The id of the author
|
||||
* @return {Promise<{padID: string}>} a promise that resolves to the id of the new pad
|
||||
*/
|
||||
exports.createGroupPad = async (groupID: string, padName: string, text: string, authorId: string = ''): Promise<{ padID: string; }> => {
|
||||
const createGroupPad = async (groupID: string, padName: string, text: string, authorId: string = ''): Promise<{ padID: string; }> => {
|
||||
// create the padID
|
||||
const padID = `${groupID}$${padName}`;
|
||||
|
||||
// ensure group exists
|
||||
const groupExists = await exports.doesGroupExist(groupID);
|
||||
const groupExists = await doesGroupExist(groupID);
|
||||
|
||||
if (!groupExists) {
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
|
|
@ -167,8 +167,8 @@ exports.createGroupPad = async (groupID: string, padName: string, text: string,
|
|||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<{padIDs: string[]}>} a promise that resolves to the ids of all pads of the group
|
||||
*/
|
||||
exports.listPads = async (groupID: string): Promise<{ padIDs: string[]; }> => {
|
||||
const exists = await exports.doesGroupExist(groupID);
|
||||
const listPads = async (groupID: string): Promise<{ padIDs: string[]; }> => {
|
||||
const exists = await doesGroupExist(groupID);
|
||||
|
||||
// ensure the group exists
|
||||
if (!exists) {
|
||||
|
|
@ -181,3 +181,13 @@ exports.listPads = async (groupID: string): Promise<{ padIDs: string[]; }> => {
|
|||
|
||||
return {padIDs};
|
||||
};
|
||||
|
||||
export default {
|
||||
listAllGroups,
|
||||
deleteGroup,
|
||||
doesGroupExist,
|
||||
createGroup,
|
||||
createGroupIfNotExistsFor,
|
||||
createGroupPad,
|
||||
listPads,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ import AttributeMap from '../../static/js/AttributeMap';
|
|||
import {applyToAText, checkRep, copyAText, deserializeOps, makeAText, makeSplice, opsFromAText, pack, unpack} from '../../static/js/Changeset';
|
||||
import ChatMessage from '../../static/js/ChatMessage';
|
||||
import AttributePool from '../../static/js/AttributePool';
|
||||
const Stream = require('../utils/Stream');
|
||||
const assert = require('assert').strict;
|
||||
const db = require('./DB');
|
||||
import Stream from '../utils/Stream';
|
||||
import {strict as assert} from 'node:assert'
|
||||
import db, {DBFunctionsPromisified} from './DB';
|
||||
import settings from '../utils/Settings';
|
||||
const authorManager = require('./AuthorManager');
|
||||
const padManager = require('./PadManager');
|
||||
const padMessageHandler = require('../handler/PadMessageHandler');
|
||||
const groupManager = require('./GroupManager');
|
||||
const CustomError = require('../utils/customError');
|
||||
import authorManager from "./AuthorManager";
|
||||
import padManager from './PadManager'
|
||||
import padMessageHandler from '../handler/PadMessageHandler';
|
||||
import groupManager from './GroupManager';
|
||||
import CustomError from '../utils/customError';
|
||||
import readOnlyManager from './ReadOnlyManager';
|
||||
import randomString from '../utils/randomstring';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
|
@ -33,19 +33,19 @@ import {timesLimit} from "async";
|
|||
* @param {String} txt The text to clean
|
||||
* @returns {String} The cleaned text
|
||||
*/
|
||||
exports.cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n')
|
||||
export const cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/\xa0/g, ' ');
|
||||
|
||||
class Pad {
|
||||
private db: Database;
|
||||
private atext: AText;
|
||||
private pool: AttributePool;
|
||||
private head: number;
|
||||
private chatHead: number;
|
||||
private db: DBFunctionsPromisified;
|
||||
atext: AText;
|
||||
pool: AttributePool;
|
||||
head: number;
|
||||
chatHead: number;
|
||||
private publicStatus: boolean;
|
||||
private id: string;
|
||||
id: string;
|
||||
private savedRevisions: any[];
|
||||
/**
|
||||
* @param id
|
||||
|
|
@ -230,9 +230,9 @@ class Pad {
|
|||
const colorPalette = authorManager.getColorPalette();
|
||||
|
||||
await Promise.all(
|
||||
authorIds.map((authorId) => authorManager.getAuthorColorId(authorId).then((colorId:string) => {
|
||||
authorIds.map((authorId) => authorManager.getAuthorColorId(authorId).then((colorId:number) => {
|
||||
// colorId might be a hex color or an number out of the palette
|
||||
returnTable[authorId] = colorPalette[colorId] || colorId;
|
||||
returnTable[authorId] = colorPalette[colorId] || colorId.toString();
|
||||
})));
|
||||
|
||||
return returnTable;
|
||||
|
|
@ -287,7 +287,7 @@ class Pad {
|
|||
const orig = this.text();
|
||||
assert(orig.endsWith('\n'));
|
||||
if (start + ndel > orig.length) throw new RangeError('start/delete past the end of the text');
|
||||
ins = exports.cleanText(ins);
|
||||
ins = cleanText(ins);
|
||||
const willEndWithNewline =
|
||||
start + ndel < orig.length || // Keeping last char (which is guaranteed to be a newline).
|
||||
ins.endsWith('\n') ||
|
||||
|
|
@ -352,6 +352,9 @@ class Pad {
|
|||
const entry = await this.db.get(`pad:${this.id}:chat:${entryNum}`);
|
||||
if (entry == null) return null;
|
||||
const message = ChatMessage.fromObject(entry);
|
||||
if (message.authorId == null) {
|
||||
return null
|
||||
}
|
||||
message.displayName = await authorManager.getAuthorName(message.authorId);
|
||||
return message;
|
||||
}
|
||||
|
|
@ -363,7 +366,7 @@ class Pad {
|
|||
* (inclusive), in order. Note: `start` and `end` form a closed interval, not a half-open
|
||||
* interval as is typical in code.
|
||||
*/
|
||||
async getChatMessages(start: string, end: number) {
|
||||
async getChatMessages(start: number, end: number) {
|
||||
const entries =
|
||||
await Promise.all(Stream.range(start, end + 1).map(this.getChatMessage.bind(this)));
|
||||
|
||||
|
|
@ -392,7 +395,7 @@ class Pad {
|
|||
const context = {pad: this, authorId, type: 'text', content: settings.defaultPadText};
|
||||
await hooks.aCallAll('padDefaultContent', context);
|
||||
if (context.type !== 'text') throw new Error(`unsupported content type: ${context.type}`);
|
||||
text = exports.cleanText(context.content);
|
||||
text = cleanText(context.content);
|
||||
}
|
||||
const firstChangeset = makeSplice('\n', 0, 0, text);
|
||||
await this.appendRevision(firstChangeset, authorId);
|
||||
|
|
@ -620,7 +623,7 @@ class Pad {
|
|||
await this.saveToDatabase();
|
||||
}
|
||||
|
||||
async addSavedRevision(revNum: string, savedById: string, label: string) {
|
||||
async addSavedRevision(revNum: number, savedById: string, label?: string) {
|
||||
// if this revision is already saved, return silently
|
||||
for (const i in this.savedRevisions) {
|
||||
if (this.savedRevisions[i] && this.savedRevisions[i].revNum === revNum) {
|
||||
|
|
@ -765,4 +768,4 @@ class Pad {
|
|||
await hooks.aCallAll('padCheck', {pad: this});
|
||||
}
|
||||
}
|
||||
exports.Pad = Pad;
|
||||
export default Pad
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
import {MapArrayType} from "../types/MapType";
|
||||
import {PadType} from "../types/PadType";
|
||||
|
||||
const CustomError = require('../utils/customError');
|
||||
const Pad = require('../db/Pad');
|
||||
const db = require('./DB');
|
||||
import CustomError from '../utils/customError';
|
||||
import Pad from '../db/Pad';
|
||||
import db from './DB';
|
||||
import settings from '../utils/Settings';
|
||||
|
||||
/**
|
||||
|
|
@ -97,7 +97,7 @@ const padList = new class {
|
|||
}
|
||||
}();
|
||||
|
||||
// initialises the all-knowing data structure
|
||||
// initializes the all-knowing data structure
|
||||
|
||||
/**
|
||||
* Returns a Pad Object with the callback
|
||||
|
|
@ -106,9 +106,9 @@ const padList = new class {
|
|||
* @param {string} [authorId] - Optional author ID of the user that initiated the pad creation (if
|
||||
* applicable).
|
||||
*/
|
||||
exports.getPad = async (id: string, text?: string|null, authorId:string|null = ''):Promise<PadType> => {
|
||||
export const getPad = async (id: string, text?: string|null, authorId:string|null = ''):Promise<Pad> => {
|
||||
// check if this is a valid padId
|
||||
if (!exports.isValidPadId(id)) {
|
||||
if (!isValidPadId(id)) {
|
||||
throw new CustomError(`${id} is not a valid padId`, 'apierror');
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ exports.getPad = async (id: string, text?: string|null, authorId:string|null = '
|
|||
}
|
||||
|
||||
// try to load pad
|
||||
pad = new Pad.Pad(id);
|
||||
pad = new Pad(id);
|
||||
|
||||
// initialize the pad
|
||||
await pad.init(text, authorId);
|
||||
|
|
@ -143,7 +143,7 @@ exports.getPad = async (id: string, text?: string|null, authorId:string|null = '
|
|||
return pad;
|
||||
};
|
||||
|
||||
exports.listAllPads = async () => {
|
||||
export const listAllPads = async () => {
|
||||
const padIDs = await padList.getPads();
|
||||
|
||||
return {padIDs};
|
||||
|
|
@ -153,14 +153,14 @@ exports.listAllPads = async () => {
|
|||
|
||||
|
||||
// checks if a pad exists
|
||||
exports.doesPadExist = async (padId: string) => {
|
||||
export const doesPadExist = async (padId: string) => {
|
||||
const value = await db.get(`pad:${padId}`);
|
||||
|
||||
return (value != null && value.atext);
|
||||
};
|
||||
|
||||
// alias for backwards compatibility
|
||||
exports.doesPadExists = exports.doesPadExist;
|
||||
export const doesPadExists = doesPadExist;
|
||||
|
||||
/**
|
||||
* An array of padId transformations. These represent changes in pad name policy over
|
||||
|
|
@ -172,9 +172,9 @@ const padIdTransforms = [
|
|||
];
|
||||
|
||||
// returns a sanitized padId, respecting legacy pad id formats
|
||||
exports.sanitizePadId = async (padId: string) => {
|
||||
export const sanitizePadId = async (padId: string) => {
|
||||
for (let i = 0, n = padIdTransforms.length; i < n; ++i) {
|
||||
const exists = await exports.doesPadExist(padId);
|
||||
const exists = await doesPadExist(padId);
|
||||
|
||||
if (exists) {
|
||||
return padId;
|
||||
|
|
@ -192,19 +192,33 @@ exports.sanitizePadId = async (padId: string) => {
|
|||
return padId;
|
||||
};
|
||||
|
||||
exports.isValidPadId = (padId: string) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
||||
export const isValidPadId = (padId: string) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
||||
|
||||
/**
|
||||
* Removes the pad from database and unloads it.
|
||||
*/
|
||||
exports.removePad = async (padId: string) => {
|
||||
export const removePad = async (padId: string) => {
|
||||
const p = db.remove(`pad:${padId}`);
|
||||
exports.unloadPad(padId);
|
||||
unloadPad(padId);
|
||||
padList.removePad(padId);
|
||||
await p;
|
||||
};
|
||||
|
||||
// removes a pad from the cache
|
||||
exports.unloadPad = (padId: string) => {
|
||||
export const unloadPad = (padId: string) => {
|
||||
globalPads.remove(padId);
|
||||
};
|
||||
|
||||
export default {
|
||||
getPad,
|
||||
listAllPads,
|
||||
doesPadExist,
|
||||
doesPadExists: doesPadExist, // alias for backwards compatibility
|
||||
sanitizePadId,
|
||||
isValidPadId,
|
||||
removePad,
|
||||
unloadPad,
|
||||
globalPads,
|
||||
padList,
|
||||
padIdTransforms,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The ReadOnlyManager manages the database and rendering releated to read only pads
|
||||
*/
|
||||
|
|
@ -20,7 +19,7 @@
|
|||
*/
|
||||
|
||||
|
||||
const db = require('./DB');
|
||||
import db from './DB'
|
||||
import randomString from '../utils/randomstring';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@
|
|||
|
||||
import {UserSettingsObject} from "../types/UserSettingsObject";
|
||||
|
||||
const authorManager = require('./AuthorManager');
|
||||
import authorManager from "./AuthorManager";
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const padManager = require('./PadManager');
|
||||
import padManager from './PadManager'
|
||||
import readOnlyManager from './ReadOnlyManager';
|
||||
const sessionManager = require('./SessionManager');
|
||||
import sessionManager from './SessionManager';
|
||||
import settings from '../utils/Settings';
|
||||
const webaccess = require('../hooks/express/webaccess');
|
||||
const log4js = require('log4js');
|
||||
import webaccess from '../hooks/express/webaccess';
|
||||
import log4js from 'log4js';
|
||||
const authLogger = log4js.getLogger('auth');
|
||||
import padutils from '../../static/js/pad_utils'
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ const DENY = Object.freeze({accessStatus: 'deny'});
|
|||
* @param {Object} userSettings
|
||||
* @return {DENY|{accessStatus: String, authorID: String}}
|
||||
*/
|
||||
exports.checkAccess = async (padID:string, sessionCookie:string, token:string, userSettings:UserSettingsObject) => {
|
||||
export const checkAccess = async (padID:string, sessionCookie:string, token:string, userSettings:UserSettingsObject | undefined) => {
|
||||
if (!padID) {
|
||||
authLogger.debug('access denied: missing padID');
|
||||
return DENY;
|
||||
|
|
@ -148,3 +148,7 @@ exports.checkAccess = async (padID:string, sessionCookie:string, token:string, u
|
|||
|
||||
return grant;
|
||||
};
|
||||
|
||||
export default {
|
||||
checkAccess
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const CustomError = require('../utils/customError');
|
||||
import CustomError from '../utils/customError';
|
||||
import {firstSatisfies} from '../utils/promises';
|
||||
import randomString from '../utils/randomstring';
|
||||
const db = require('./DB');
|
||||
const groupManager = require('./GroupManager');
|
||||
const authorManager = require('./AuthorManager');
|
||||
import db from './DB';
|
||||
import groupManager from './GroupManager';
|
||||
import authorManager from './AuthorManager';
|
||||
|
||||
/**
|
||||
* Finds the author ID for a session with matching ID and group.
|
||||
|
|
@ -36,7 +36,7 @@ const authorManager = require('./AuthorManager');
|
|||
* sessionCookie, and is bound to a group with the given ID, then this returns the author ID
|
||||
* bound to the session. Otherwise, returns undefined.
|
||||
*/
|
||||
exports.findAuthorID = async (groupID:string, sessionCookie: string) => {
|
||||
const findAuthorID = async (groupID:string, sessionCookie: string) => {
|
||||
if (!sessionCookie) return undefined;
|
||||
/*
|
||||
* Sometimes, RFC 6265-compliant web servers may send back a cookie whose
|
||||
|
|
@ -64,7 +64,7 @@ exports.findAuthorID = async (groupID:string, sessionCookie: string) => {
|
|||
const sessionIDs = sessionCookie.replace(/^"|"$/g, '').split(',');
|
||||
const sessionInfoPromises = sessionIDs.map(async (id) => {
|
||||
try {
|
||||
return await exports.getSessionInfo(id);
|
||||
return await getSessionInfo(id);
|
||||
} catch (err:any) {
|
||||
if (err.message === 'sessionID does not exist') {
|
||||
console.debug(`SessionManager getAuthorID: no session exists with ID ${id}`);
|
||||
|
|
@ -89,7 +89,7 @@ exports.findAuthorID = async (groupID:string, sessionCookie: string) => {
|
|||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<boolean>} Resolves to true if the session exists
|
||||
*/
|
||||
exports.doesSessionExist = async (sessionID: string) => {
|
||||
const doesSessionExist = async (sessionID: string) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
return (session != null);
|
||||
|
|
@ -102,7 +102,7 @@ exports.doesSessionExist = async (sessionID: string) => {
|
|||
* @param {Number} validUntil The unix timestamp when the session should expire
|
||||
* @return {Promise<{sessionID: string}>} the id of the new session
|
||||
*/
|
||||
exports.createSession = async (groupID: string, authorID: string, validUntil: number) => {
|
||||
const createSession = async (groupID: string, authorID: string, validUntil: number|string) => {
|
||||
// check if the group exists
|
||||
const groupExists = await groupManager.doesGroupExist(groupID);
|
||||
if (!groupExists) {
|
||||
|
|
@ -163,7 +163,7 @@ exports.createSession = async (groupID: string, authorID: string, validUntil: nu
|
|||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<Object>} the sessioninfos
|
||||
*/
|
||||
exports.getSessionInfo = async (sessionID:string) => {
|
||||
const getSessionInfo = async (sessionID:string) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ exports.getSessionInfo = async (sessionID:string) => {
|
|||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<void>} Resolves when the session is deleted
|
||||
*/
|
||||
exports.deleteSession = async (sessionID:string) => {
|
||||
const deleteSession = async (sessionID:string) => {
|
||||
// ensure that the session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
if (session == null) {
|
||||
|
|
@ -210,7 +210,7 @@ exports.deleteSession = async (sessionID:string) => {
|
|||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<Object>} The sessioninfos of all sessions of this group
|
||||
*/
|
||||
exports.listSessionsOfGroup = async (groupID: string) => {
|
||||
const listSessionsOfGroup = async (groupID: string) => {
|
||||
// check that the group exists
|
||||
const exists = await groupManager.doesGroupExist(groupID);
|
||||
if (!exists) {
|
||||
|
|
@ -226,7 +226,7 @@ exports.listSessionsOfGroup = async (groupID: string) => {
|
|||
* @param {String} authorID The id of the author
|
||||
* @return {Promise<Object>} The sessioninfos of all sessions of this author
|
||||
*/
|
||||
exports.listSessionsOfAuthor = async (authorID: string) => {
|
||||
const listSessionsOfAuthor = async (authorID: string) => {
|
||||
// check that the author exists
|
||||
const exists = await authorManager.doesAuthorExist(authorID);
|
||||
if (!exists) {
|
||||
|
|
@ -251,7 +251,7 @@ const listSessionsWithDBKey = async (dbkey: string) => {
|
|||
// iterate through the sessions and get the sessioninfos
|
||||
for (const sessionID of Object.keys(sessions || {})) {
|
||||
try {
|
||||
sessions[sessionID] = await exports.getSessionInfo(sessionID);
|
||||
sessions[sessionID] = await getSessionInfo(sessionID);
|
||||
} catch (err:any) {
|
||||
if (err.name === 'apierror') {
|
||||
console.warn(`Found bad session ${sessionID} in ${dbkey}`);
|
||||
|
|
@ -271,5 +271,15 @@ const listSessionsWithDBKey = async (dbkey: string) => {
|
|||
* @param {number|string} value
|
||||
* @return {boolean} If the value is an integer
|
||||
*/
|
||||
// @ts-ignore
|
||||
const isInt = (value:number|string): boolean => (parseFloat(value) === parseInt(value)) && !isNaN(value);
|
||||
const isInt = (value:any): boolean => (parseFloat(value) === parseInt(value)) && !isNaN(value);
|
||||
|
||||
export default {
|
||||
findAuthorID,
|
||||
doesSessionExist,
|
||||
createSession,
|
||||
getSessionInfo,
|
||||
deleteSession,
|
||||
listSessionsOfGroup,
|
||||
listSessionsOfAuthor,
|
||||
isInt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,4 +113,4 @@ for (const m of ['get', 'set', 'destroy', 'touch']) {
|
|||
SessionStore.prototype[m] = util.callbackify(SessionStore.prototype[`_${m}`]);
|
||||
}
|
||||
|
||||
module.exports = SessionStore;
|
||||
export default SessionStore
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
'use strict';
|
||||
/*
|
||||
* Copyright (c) 2011 RedHog (Egil Möller) <egil.moller@freecode.no>
|
||||
*
|
||||
|
|
@ -31,44 +30,41 @@ import {pluginInstallPath} from '../../static/js/pluginfw/installer'
|
|||
|
||||
const templateCache = new Map();
|
||||
|
||||
exports.info = {
|
||||
export const info: any = {
|
||||
__output_stack: [],
|
||||
block_stack: [],
|
||||
file_stack: [],
|
||||
args: [],
|
||||
};
|
||||
|
||||
const getCurrentFile = () => exports.info.file_stack[exports.info.file_stack.length - 1];
|
||||
const getCurrentFile = () => info.file_stack[info.file_stack.length - 1];
|
||||
|
||||
exports._init = (b: any, recursive: boolean) => {
|
||||
exports.info.__output_stack.push(exports.info.__output);
|
||||
exports.info.__output = b;
|
||||
export const _init = (b: any, recursive: boolean) => {
|
||||
info.__output_stack.push(info.__output);
|
||||
info.__output = b;
|
||||
};
|
||||
|
||||
exports._exit = (b:any, recursive:boolean) => {
|
||||
exports.info.__output = exports.info.__output_stack.pop();
|
||||
export const _exit = (b:any, recursive:boolean) => {
|
||||
info.__output = info.__output_stack.pop();
|
||||
};
|
||||
|
||||
exports.begin_block = (name:string) => {
|
||||
exports.info.block_stack.push(name);
|
||||
exports.info.__output_stack.push(exports.info.__output.get());
|
||||
exports.info.__output.set('');
|
||||
export const begin_block = (name:string) => {
|
||||
info.block_stack.push(name);
|
||||
info.__output_stack.push(info.__output.get());
|
||||
info.__output.set('');
|
||||
};
|
||||
|
||||
exports.end_block = () => {
|
||||
const name = exports.info.block_stack.pop();
|
||||
const renderContext = exports.info.args[exports.info.args.length - 1];
|
||||
const content = exports.info.__output.get();
|
||||
exports.info.__output.set(exports.info.__output_stack.pop());
|
||||
export const end_block = () => {
|
||||
const name = info.block_stack.pop();
|
||||
const renderContext = info.args[info.args.length - 1];
|
||||
const content = info.__output.get();
|
||||
info.__output.set(info.__output_stack.pop());
|
||||
const args = {content, renderContext};
|
||||
hooks.callAll(`eejsBlock_${name}`, args);
|
||||
exports.info.__output.set(exports.info.__output.get().concat(args.content));
|
||||
info.__output.set(info.__output.get().concat(args.content));
|
||||
};
|
||||
|
||||
exports.require = (name:string, args:{
|
||||
e?: Function,
|
||||
require?: Function,
|
||||
}, mod:{
|
||||
export const require2 = (name:string, args: any, mod?:{
|
||||
filename:string,
|
||||
paths:string[],
|
||||
}) => {
|
||||
|
|
@ -77,7 +73,7 @@ exports.require = (name:string, args:{
|
|||
let basedir = __dirname;
|
||||
let paths:string[] = [];
|
||||
|
||||
if (exports.info.file_stack.length) {
|
||||
if (info.file_stack.length) {
|
||||
basedir = path.dirname(getCurrentFile().path);
|
||||
}
|
||||
if (mod) {
|
||||
|
|
@ -94,7 +90,14 @@ exports.require = (name:string, args:{
|
|||
|
||||
const ejspath = resolve.sync(name, {paths, basedir, extensions: ['.html', '.ejs']});
|
||||
|
||||
args.e = exports;
|
||||
args.e = {
|
||||
_init: (b:any, recursive:boolean) => _init(b, recursive),
|
||||
_exit: (b:any, recursive:boolean) => _exit(b, recursive),
|
||||
begin_block: (name:string) => begin_block(name),
|
||||
end_block: () => end_block(),
|
||||
info,
|
||||
getCurrentFile,
|
||||
};
|
||||
args.require = require;
|
||||
|
||||
const cache = settings.maxAge !== 0;
|
||||
|
|
@ -104,11 +107,25 @@ exports.require = (name:string, args:{
|
|||
{filename: ejspath});
|
||||
if (cache) templateCache.set(ejspath, template);
|
||||
|
||||
exports.info.args.push(args);
|
||||
exports.info.file_stack.push({path: ejspath});
|
||||
info.args.push(args);
|
||||
info.file_stack.push({path: ejspath});
|
||||
const res = template(args);
|
||||
exports.info.file_stack.pop();
|
||||
exports.info.args.pop();
|
||||
info.file_stack.pop();
|
||||
info.args.pop();
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export default {
|
||||
require: require2,
|
||||
_init,
|
||||
_exit,
|
||||
begin_block,
|
||||
end_block,
|
||||
info,
|
||||
getCurrentFile,
|
||||
templateCache,
|
||||
infoStack: info.__output_stack,
|
||||
blockStack: info.block_stack,
|
||||
fileStack: info.file_stack,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import settings from '../utils/Settings';
|
|||
import os from 'os';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
import util from 'util';
|
||||
const { checkValidRev } = require('../utils/checkValidRev');
|
||||
import {checkValidRev} from '../utils/checkValidRev';
|
||||
|
||||
const fsp_writeFile = util.promisify(fs.writeFile);
|
||||
const fsp_unlink = util.promisify(fs.unlink);
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@
|
|||
import {MapArrayType} from "../types/MapType";
|
||||
|
||||
import AttributeMap from '../../static/js/AttributeMap';
|
||||
const padManager = require('../db/PadManager');
|
||||
import padManager from '../db/PadManager';
|
||||
import {checkRep, cloneAText, compose, deserializeOps, follow, identity, inverse, makeAText, makeSplice, moveOpsToNewPool, mutateAttributionLines, mutateTextLines, oldLen, prepareForWire, splitAttributionLines, splitTextLines, unpack} from '../../static/js/Changeset';
|
||||
import ChatMessage from '../../static/js/ChatMessage';
|
||||
import AttributePool from '../../static/js/AttributePool';
|
||||
const AttributeManager = require('../../static/js/AttributeManager');
|
||||
const authorManager = require('../db/AuthorManager');
|
||||
import authorManager from '../db/AuthorManager';
|
||||
import padutils from '../../static/js/pad_utils';
|
||||
import readOnlyManager from '../db/ReadOnlyManager';
|
||||
import settings, {
|
||||
|
|
@ -35,13 +35,13 @@ import settings, {
|
|||
abiwordAvailable,
|
||||
sofficeAvailable
|
||||
} from '../utils/Settings';
|
||||
const securityManager = require('../db/SecurityManager');
|
||||
import securityManager from '../db/SecurityManager';
|
||||
const plugins = require('../../static/js/pluginfw/plugin_defs');
|
||||
import log4js from 'log4js';
|
||||
const messageLogger = log4js.getLogger('message');
|
||||
const accessLogger = log4js.getLogger('access');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const stats = require('../stats')
|
||||
import stats from '../stats';
|
||||
const assert = require('assert').strict;
|
||||
import {RateLimiterMemory} from 'rate-limiter-flexible';
|
||||
import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest";
|
||||
|
|
@ -49,11 +49,12 @@ import {APool, AText, PadAuthor, PadType} from "../types/PadType";
|
|||
import {ChangeSet} from "../types/ChangeSet";
|
||||
import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, PadDeleteMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage";
|
||||
import {Builder} from "../../static/js/Builder";
|
||||
const webaccess = require('../hooks/express/webaccess');
|
||||
const { checkValidRev } = require('../utils/checkValidRev');
|
||||
import webaccess from '../hooks/express/webaccess';
|
||||
import {checkValidRev} from '../utils/checkValidRev';
|
||||
import Pad from "../db/Pad";
|
||||
|
||||
let rateLimiter:any;
|
||||
let socketio: any = null;
|
||||
let _socketio: any = null;
|
||||
|
||||
hooks.deprecationNotices.clientReady = 'use the userJoin hook instead';
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ const addContextToError = (err:any, pfx:string) => {
|
|||
return err;
|
||||
};
|
||||
|
||||
exports.socketio = () => {
|
||||
export const socketio = () => {
|
||||
// The rate limiter is created in this hook so that restarting the server resets the limiter. The
|
||||
// settings.commitRateLimiting object is passed directly to the rate limiter so that the limits
|
||||
// can be dynamically changed during runtime by modifying its properties.
|
||||
|
|
@ -91,10 +92,9 @@ exports.socketio = () => {
|
|||
* - readonly: Whether the client has read-only access (true) or read/write access (false).
|
||||
* - rev: The last revision that was sent to the client.
|
||||
*/
|
||||
const sessioninfos:MapArrayType<any> = {};
|
||||
exports.sessioninfos = sessioninfos;
|
||||
export const sessioninfos:MapArrayType<any> = {};
|
||||
|
||||
stats.gauge('totalUsers', () => socketio ? socketio.engine.clientsCount : 0);
|
||||
stats.gauge('totalUsers', () => _socketio ? _socketio.engine.clientsCount : 0);
|
||||
stats.gauge('activePads', () => {
|
||||
const padIds = new Set();
|
||||
for (const {padId} of Object.values(sessioninfos)) {
|
||||
|
|
@ -149,15 +149,15 @@ const padChannels = new Channels((ch, {socket, message}) => handleUserChanges(so
|
|||
* This Method is called by server.ts to tell the message handler on which socket it should send
|
||||
* @param socket_io The Socket
|
||||
*/
|
||||
exports.setSocketIO = (socket_io:any) => {
|
||||
socketio = socket_io;
|
||||
export const setSocketIO = (socket_io:any) => {
|
||||
_socketio = socket_io;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the connection of a new user
|
||||
* @param socket the socket.io Socket object for the new connection from the client
|
||||
*/
|
||||
exports.handleConnect = (socket:any) => {
|
||||
export const handleConnect = (socket:any) => {
|
||||
stats.meter('connects').mark();
|
||||
|
||||
// Initialize sessioninfos for this new session
|
||||
|
|
@ -167,22 +167,22 @@ exports.handleConnect = (socket:any) => {
|
|||
/**
|
||||
* Kicks all sessions from a pad
|
||||
*/
|
||||
exports.kickSessionsFromPad = (padID: string) => {
|
||||
export const kickSessionsFromPad = (padID: string) => {
|
||||
|
||||
if(socketio.sockets == null) return;
|
||||
if(_socketio.sockets == null) return;
|
||||
|
||||
// skip if there is nobody on this pad
|
||||
if (_getRoomSockets(padID).length === 0) return;
|
||||
|
||||
// disconnect everyone from this pad
|
||||
socketio.in(padID).emit('message', {disconnect: 'deleted'});
|
||||
_socketio.in(padID).emit('message', {disconnect: 'deleted'});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the disconnection of a user
|
||||
* @param socket the socket.io Socket object for the client
|
||||
*/
|
||||
exports.handleDisconnect = async (socket:any) => {
|
||||
export const handleDisconnect = async (socket:any) => {
|
||||
stats.meter('disconnects').mark();
|
||||
const session = sessioninfos[socket.id];
|
||||
delete sessioninfos[socket.id];
|
||||
|
|
@ -259,7 +259,7 @@ const handlePadDelete = async (socket: any, padDeleteMessage: PadDeleteMessage)
|
|||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
||||
export const handleMessage = async (socket:any, message: ClientVarMessage) => {
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
|
||||
if (env === 'production') {
|
||||
|
|
@ -315,14 +315,14 @@ exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
|||
const msg = JSON.stringify(message, null, 2);
|
||||
throw new Error(`pre-CLIENT_READY message from IP ${ip}: ${msg}`);
|
||||
}
|
||||
|
||||
const {session: {user} = {}} = socket.client.request as SocketClientRequest;
|
||||
const {accessStatus, authorID} =
|
||||
await securityManager.checkAccess(auth.padID, auth.sessionID, auth.token, user);
|
||||
if (accessStatus !== 'grant') {
|
||||
socket.emit('message', {accessStatus});
|
||||
const result = await securityManager.checkAccess(auth.padID, auth.sessionID, auth.token, user);
|
||||
|
||||
if (result.accessStatus !== 'grant') {
|
||||
socket.emit('message', {accessStatus: result.accessStatus});
|
||||
throw new Error('access denied');
|
||||
}
|
||||
const { authorID } = result
|
||||
if (thisSession.author != null && thisSession.author !== authorID) {
|
||||
socket.emit('message', {disconnect: 'rejected'});
|
||||
throw new Error([
|
||||
|
|
@ -443,14 +443,14 @@ const handleSaveRevisionMessage = async (socket:any, message: ClientSaveRevision
|
|||
* @param msg {Object} the message we're sending
|
||||
* @param sessionID {string} the socketIO session to which we're sending this message
|
||||
*/
|
||||
exports.handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => {
|
||||
export const handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => {
|
||||
if (msg.data.type === 'CUSTOM') {
|
||||
if (sessionID) {
|
||||
// a sessionID is targeted: directly to this sessionID
|
||||
socketio.sockets.socket(sessionID).emit('message', msg);
|
||||
_socketio.sockets.socket(sessionID).emit('message', msg);
|
||||
} else {
|
||||
// broadcast to all clients on this pad
|
||||
socketio.sockets.in(msg.data.payload.padId).emit('message', msg);
|
||||
_socketio.sockets.in(msg.data.payload.padId).emit('message', msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -461,7 +461,7 @@ exports.handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => {
|
|||
* @param padID {Pad} the pad to which we're sending this message
|
||||
* @param msgString {String} the message we're sending
|
||||
*/
|
||||
exports.handleCustomMessage = (padID: string, msgString:string) => {
|
||||
export const handleCustomMessage = (padID: string, msgString:string) => {
|
||||
const time = Date.now();
|
||||
const msg = {
|
||||
type: 'COLLABROOM',
|
||||
|
|
@ -470,7 +470,7 @@ exports.handleCustomMessage = (padID: string, msgString:string) => {
|
|||
time,
|
||||
},
|
||||
};
|
||||
socketio.sockets.in(padID).emit('message', msg);
|
||||
_socketio.sockets.in(padID).emit('message', msg);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -484,7 +484,7 @@ const handleChatMessage = async (socket:any, message: ChatMessageMessage) => {
|
|||
// Don't trust the user-supplied values.
|
||||
chatMessage.time = Date.now();
|
||||
chatMessage.authorId = authorId;
|
||||
await exports.sendChatMessageToPadClients(chatMessage, padId);
|
||||
await sendChatMessageToPadClients(chatMessage, padId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -498,16 +498,16 @@ const handleChatMessage = async (socket:any, message: ChatMessageMessage) => {
|
|||
* @param {string} [padId] - The destination pad ID. Deprecated; pass a chat message
|
||||
* object as the first argument and the destination pad ID as the second argument instead.
|
||||
*/
|
||||
exports.sendChatMessageToPadClients = async (mt: ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => {
|
||||
const sendChatMessageToPadClients = async (mt: ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => {
|
||||
const message = mt instanceof ChatMessage ? mt : new ChatMessage(text, puId, mt);
|
||||
padId = mt instanceof ChatMessage ? puId : padId;
|
||||
const pad = await padManager.getPad(padId, null, message.authorId);
|
||||
const pad = await padManager.getPad(padId!, null, message.authorId);
|
||||
await hooks.aCallAll('chatNewMessage', {message, pad, padId});
|
||||
// pad.appendChatMessage() ignores the displayName property so we don't need to wait for
|
||||
// authorManager.getAuthorName() to resolve before saving the message to the database.
|
||||
const promise = pad.appendChatMessage(message);
|
||||
message.displayName = await authorManager.getAuthorName(message.authorId);
|
||||
socketio.sockets.in(padId).emit('message', {
|
||||
message.displayName = await authorManager.getAuthorName(message.authorId!);
|
||||
_socketio.sockets.in(padId).emit('message', {
|
||||
type: 'COLLABROOM',
|
||||
data: {type: 'CHAT_MESSAGE', message},
|
||||
});
|
||||
|
|
@ -713,7 +713,7 @@ const handleUserChanges = async (socket:any, message: {
|
|||
socket.emit('message', {type: 'COLLABROOM', data: {type: 'ACCEPT_COMMIT', newRev}});
|
||||
thisSession.rev = newRev;
|
||||
if (newRev !== r) thisSession.time = await pad.getRevisionDate(newRev);
|
||||
await exports.updatePadClients(pad);
|
||||
await updatePadClients(pad);
|
||||
} catch (err:any) {
|
||||
socket.emit('message', {disconnect: 'badChangeset'});
|
||||
stats.meter('failedChangesets').mark();
|
||||
|
|
@ -724,7 +724,7 @@ const handleUserChanges = async (socket:any, message: {
|
|||
}
|
||||
};
|
||||
|
||||
exports.updatePadClients = async (pad: PadType) => {
|
||||
const updatePadClients = async (pad: Pad) => {
|
||||
// skip this if no-one is on this pad
|
||||
const roomSockets = _getRoomSockets(pad.id);
|
||||
if (roomSockets.length === 0) return;
|
||||
|
|
@ -1161,7 +1161,7 @@ const handleChangesetRequest = async (socket:any, {data: {granularity, start, re
|
|||
* Tries to rebuild the getChangestInfo function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L144
|
||||
*/
|
||||
const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, granularity: number) => {
|
||||
const getChangesetInfo = async (pad: Pad, startNum: number, endNum:number, granularity: number) => {
|
||||
const headRevision = pad.getHeadRevisionNumber();
|
||||
|
||||
// calculate the last full endnum
|
||||
|
|
@ -1192,7 +1192,7 @@ const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, g
|
|||
getPadLines(pad, startNum - 1),
|
||||
// Get all needed composite Changesets.
|
||||
...compositesChangesetNeeded.map(async (item) => {
|
||||
const changeset = await exports.composePadChangesets(pad, item.start, item.end);
|
||||
const changeset = await composePadChangesets(pad, item.start, item.end);
|
||||
composedChangesets[`${item.start}/${item.end}`] = changeset;
|
||||
}),
|
||||
// Get all needed revision Dates.
|
||||
|
|
@ -1238,7 +1238,7 @@ const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, g
|
|||
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||
*/
|
||||
const getPadLines = async (pad: PadType, revNum: number) => {
|
||||
const getPadLines = async (pad: Pad, revNum: number) => {
|
||||
// get the atext
|
||||
let atext;
|
||||
|
||||
|
|
@ -1258,7 +1258,7 @@ const getPadLines = async (pad: PadType, revNum: number) => {
|
|||
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
||||
*/
|
||||
exports.composePadChangesets = async (pad: PadType, startNum: number, endNum: number) => {
|
||||
export const composePadChangesets = async (pad: Pad, startNum: number, endNum: number) => {
|
||||
// fetch all changesets we need
|
||||
const headNum = pad.getHeadRevisionNumber();
|
||||
endNum = Math.min(endNum, headNum + 1);
|
||||
|
|
@ -1297,7 +1297,7 @@ exports.composePadChangesets = async (pad: PadType, startNum: number, endNum: nu
|
|||
};
|
||||
|
||||
const _getRoomSockets = (padID: string) => {
|
||||
const ns = socketio.sockets; // Default namespace.
|
||||
const ns = _socketio.sockets; // Default namespace.
|
||||
// We could call adapter.clients(), but that method is unnecessarily asynchronous. Replicate what
|
||||
// it does here, but synchronously to avoid a race condition. This code will have to change when
|
||||
// we update to socket.io v3.
|
||||
|
|
@ -1313,14 +1313,14 @@ const _getRoomSockets = (padID: string) => {
|
|||
/**
|
||||
* Get the number of users in a pad
|
||||
*/
|
||||
exports.padUsersCount = (padID:string) => ({
|
||||
export const padUsersCount = (padID:string) => ({
|
||||
padUsersCount: _getRoomSockets(padID).length,
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the list of users in a pad
|
||||
*/
|
||||
exports.padUsers = async (padID: string) => {
|
||||
export const padUsers = async (padID: string) => {
|
||||
const padUsers:PadAuthor[] = [];
|
||||
|
||||
// iterate over all clients (in parallel)
|
||||
|
|
@ -1340,4 +1340,21 @@ exports.padUsers = async (padID: string) => {
|
|||
return {padUsers};
|
||||
};
|
||||
|
||||
exports.sessioninfos = sessioninfos;
|
||||
export default {
|
||||
sessioninfos,
|
||||
handleGetChatMessages,
|
||||
sendChatMessageToPadClients,
|
||||
handleSuggestUserName,
|
||||
updatePadClients,
|
||||
handleUserInfoUpdate,
|
||||
handleUserChanges,
|
||||
handleCustomMessage,
|
||||
handleClientReady,
|
||||
handleChangesetRequest,
|
||||
getChangesetInfo,
|
||||
composePadChangesets,
|
||||
getPadLines,
|
||||
padUsersCount,
|
||||
padUsers,
|
||||
kickSessionsFromPad
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {MapArrayType} from "../types/MapType";
|
|||
import {SocketModule} from "../types/SocketModule";
|
||||
import log4js from 'log4js';
|
||||
import settings from '../utils/Settings';
|
||||
const stats = require('../../node/stats')
|
||||
import stats from '../../node/stats';
|
||||
|
||||
const logger = log4js.getLogger('socket.io');
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import expressSession, {Store} from 'express-session';
|
|||
import fs from 'fs';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
import log4js from 'log4js';
|
||||
const SessionStore = require('../db/SessionStore');
|
||||
import SessionStore from '../db/SessionStore';
|
||||
import settings, {getEpVersion, getGitCommit} from '../utils/Settings';
|
||||
const stats = require('../stats')
|
||||
import stats from '../stats';
|
||||
import util from 'util';
|
||||
const webaccess = require('./express/webaccess');
|
||||
import webaccess from './express/webaccess';
|
||||
|
||||
import SecretRotator from '../security/SecretRotator';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
import path from 'node:path';
|
||||
const eejs = require('../../eejs')
|
||||
import eejs from '../../eejs';
|
||||
import fs from 'node:fs';
|
||||
const fsp = fs.promises;
|
||||
const toolbar = require('../../utils/toolbar');
|
||||
const hooks = require('../../../static/js/pluginfw/hooks');
|
||||
import settings, {getEpVersion} from '../../utils/Settings';
|
||||
import util from 'node:util';
|
||||
const webaccess = require('./webaccess');
|
||||
import webaccess from './webaccess';
|
||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||
|
||||
import {build, buildSync} from 'esbuild'
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const aCallFirst0 =
|
|||
// @ts-ignore
|
||||
async (hookName: string, context:any, pred = null) => (await aCallFirst(hookName, context, pred))[0];
|
||||
|
||||
exports.normalizeAuthzLevel = (level: string|boolean) => {
|
||||
export const normalizeAuthzLevel = (level: string|boolean) => {
|
||||
if (!level) return false;
|
||||
switch (level) {
|
||||
case true:
|
||||
|
|
@ -36,18 +36,18 @@ exports.normalizeAuthzLevel = (level: string|boolean) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
exports.userCanModify = (padId: string, req: SocketClientRequest) => {
|
||||
export const userCanModify = (padId: string, req: SocketClientRequest) => {
|
||||
if (readOnlyManager.isReadOnlyId(padId)) return false;
|
||||
if (!settings.requireAuthentication) return true;
|
||||
const {session: {user} = {}} = req;
|
||||
if (!user || user.readOnly) return false;
|
||||
assert(user.padAuthorizations); // This is populated even if !settings.requireAuthorization.
|
||||
const level = exports.normalizeAuthzLevel(user.padAuthorizations[padId]);
|
||||
const level = normalizeAuthzLevel(user.padAuthorizations[padId]);
|
||||
return level && level !== 'readOnly';
|
||||
};
|
||||
|
||||
// Exported so that tests can set this to 0 to avoid unnecessary test slowness.
|
||||
exports.authnFailureDelayMs = 1000;
|
||||
const authnFailureDelayMs = 1000;
|
||||
|
||||
const staticResources = [
|
||||
/^\/padbootstrap-[a-zA-Z0-9]+\.min\.js$/,
|
||||
|
|
@ -55,7 +55,7 @@ const staticResources = [
|
|||
/^\/manifest.json$/
|
||||
]
|
||||
|
||||
const checkAccess = async (req:any, res:any, next: Function) => {
|
||||
const _checkAccess = async (req:any, res:any, next: Function) => {
|
||||
const requireAdmin = req.path.toLowerCase().startsWith('/admin-auth');
|
||||
for (const staticResource of staticResources) {
|
||||
if (req.path.match(staticResource)) {
|
||||
|
|
@ -106,7 +106,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
// authentication is checked and once after (if settings.requireAuthorization is true).
|
||||
const authorize = async () => {
|
||||
const grant = async (level: string|false) => {
|
||||
level = exports.normalizeAuthzLevel(level);
|
||||
level = normalizeAuthzLevel(level);
|
||||
if (!level) return false;
|
||||
const user = req.session.user;
|
||||
if (user == null) return true; // This will happen if authentication is not required.
|
||||
|
|
@ -186,7 +186,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
||||
}
|
||||
// Delay the error response for 1s to slow down brute force attacks.
|
||||
await new Promise((resolve) => setTimeout(resolve, exports.authnFailureDelayMs));
|
||||
await new Promise((resolve) => setTimeout(resolve, authnFailureDelayMs));
|
||||
res.status(401).send('Authentication Required');
|
||||
return;
|
||||
}
|
||||
|
|
@ -230,6 +230,12 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
* Express middleware to authenticate the user and check authorization. Must be installed after the
|
||||
* express-session middleware.
|
||||
*/
|
||||
exports.checkAccess = (req:any, res:any, next:Function) => {
|
||||
checkAccess(req, res, next).catch((err) => next(err || new Error(err)));
|
||||
const checkAccess = (req:any, res:any, next:Function) => {
|
||||
_checkAccess(req, res, next).catch((err) => next(err || new Error(err)));
|
||||
};
|
||||
|
||||
export default {
|
||||
checkAccess,
|
||||
normalizeAuthzLevel,
|
||||
userCanModify
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
import {DeriveModel} from "../types/DeriveModel";
|
||||
import {LegacyParams} from "../types/LegacyParams";
|
||||
import {Buffer} from 'node:buffer'
|
||||
import crypto from './crypto'
|
||||
|
||||
const {Buffer} = require('buffer');
|
||||
const crypto = require('./crypto');
|
||||
const db = require('../db/DB');
|
||||
const log4js = require('log4js');
|
||||
import db from '../db/DB';
|
||||
import log4js from "log4js";
|
||||
|
||||
class Kdf {
|
||||
async generateParams(): Promise<{ salt: string; digest: string; keyLen: number; secret: string }> { throw new Error('not implemented'); }
|
||||
async derive(params: DeriveModel, info: any) { throw new Error('not implemented'); }
|
||||
async derive(params: DeriveModel, info: any): Promise<string> { throw new Error('not implemented'); }
|
||||
}
|
||||
|
||||
class LegacyStaticSecret extends Kdf {
|
||||
async derive(params:any, info:any) { return params; }
|
||||
async derive(params:any, info:any) : Promise<string> { return params; }
|
||||
}
|
||||
|
||||
class Hkdf extends Kdf {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const util = require('util');
|
||||
import crypto from 'node:crypto'
|
||||
import util from 'node:util'
|
||||
|
||||
|
||||
/**
|
||||
* Promisified version of Node.js's crypto.hkdf.
|
||||
*/
|
||||
exports.hkdf = util.promisify(crypto.hkdf);
|
||||
const hkdf = util.promisify(crypto.hkdf);
|
||||
|
||||
/**
|
||||
* Promisified version of Node.js's crypto.randomBytes
|
||||
*/
|
||||
exports.randomBytes = util.promisify(crypto.randomBytes);
|
||||
const randomBytes = util.promisify(crypto.randomBytes);
|
||||
|
||||
export default {
|
||||
hkdf,
|
||||
randomBytes
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ const hooks = require('../static/js/pluginfw/hooks');
|
|||
const pluginDefs = require('../static/js/pluginfw/plugin_defs');
|
||||
const plugins = require('../static/js/pluginfw/plugins');
|
||||
import {Gate} from './utils/promises';
|
||||
const stats = require('./stats')
|
||||
import stats from './stats'
|
||||
|
||||
const logger = log4js.getLogger('server');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
const measured = require('measured-core');
|
||||
|
||||
module.exports = measured.createCollection();
|
||||
export default measured.createCollection();
|
||||
|
||||
// @ts-ignore
|
||||
module.exports.shutdown = async (hookName, context) => {
|
||||
export const shutdown = async (hookName, context) => {
|
||||
module.exports.end();
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
export type UserSettingsObject = {
|
||||
canCreate: boolean,
|
||||
canCreate?: boolean,
|
||||
readOnly: boolean,
|
||||
padAuthorizations: any
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import {AChangeSet} from "../types/PadType";
|
|||
import {Revision} from "../types/Revision";
|
||||
|
||||
import {timesLimit, firstSatisfies} from './promises';
|
||||
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||
const db = require('ep_etherpad-lite/node/db/DB');
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler');
|
||||
import padManager from 'ep_etherpad-lite/node/db/PadManager';
|
||||
import db from 'ep_etherpad-lite/node/db/DB';
|
||||
import {applyToAText, makeAText} from 'ep_etherpad-lite/static/js/Changeset';
|
||||
import padMessageHandler from 'ep_etherpad-lite/node/handler/PadMessageHandler';
|
||||
import log4js from 'log4js';
|
||||
const logger = log4js.getLogger('cleanup');
|
||||
|
||||
|
|
@ -88,10 +88,10 @@ export const deleteRevisions = async (padId: string, keepRevisions: number): Pro
|
|||
}
|
||||
await db.set(`pad:${padId}`, padContent);
|
||||
|
||||
let newAText = Changeset.makeAText('\n');
|
||||
let newAText = makeAText('\n');
|
||||
let pool = pad.apool()
|
||||
|
||||
newAText = Changeset.applyToAText(changeset, newAText, pool);
|
||||
newAText = applyToAText(changeset as string, newAText, pool);
|
||||
|
||||
const revision = await createRevision(
|
||||
changeset,
|
||||
|
|
@ -110,7 +110,7 @@ export const deleteRevisions = async (padId: string, keepRevisions: number): Pro
|
|||
const rev = i + cleanupUntilRevision + 1
|
||||
const newRev = rev - cleanupUntilRevision;
|
||||
|
||||
newAText = Changeset.applyToAText(revisions[rev].changeset, newAText, pool);
|
||||
newAText = applyToAText(revisions[rev].changeset as string, newAText, pool);
|
||||
|
||||
const revision = await createRevision(
|
||||
revisions[rev].changeset,
|
||||
|
|
@ -152,7 +152,7 @@ export const checkTodos = async () => {
|
|||
|
||||
const revisionDate = await pad.getRevisionDate(pad.getHeadRevisionNumber())
|
||||
|
||||
if (pad.head < settings.minHead || padMessageHandler.padUsersCount(padId) > 0 || Date.now() < revisionDate + settings.minAge) {
|
||||
if (pad.head < settings.minHead || padMessageHandler.padUsersCount(padId).padUsersCount > 0 || Date.now() < revisionDate + settings.minAge) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Stream = require('./Stream');
|
||||
import Stream from './Stream';
|
||||
const assert = require('assert').strict;
|
||||
const authorManager = require('../db/AuthorManager');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
|
|
|||
|
|
@ -22,13 +22,12 @@
|
|||
import AttributeMap from '../../static/js/AttributeMap';
|
||||
import AttributePool from "../../static/js/AttributePool";
|
||||
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
|
||||
const { checkValidRev } = require('./checkValidRev');
|
||||
import {checkValidRev} from './checkValidRev';
|
||||
|
||||
/*
|
||||
* This method seems unused in core and no plugins depend on it
|
||||
*/
|
||||
exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any; atext: any; pool: any; }, revNum: undefined) => {
|
||||
const _analyzeLine = exports._analyzeLine;
|
||||
export const getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any; atext: any; pool: any; }, revNum: undefined) => {
|
||||
const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(checkValidRev(revNum)) : pad.atext);
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
const attribLines = splitAttributionLines(atext.attribs, atext.text);
|
||||
|
|
@ -38,7 +37,7 @@ exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any;
|
|||
for (let i = 0; i < textLines.length; i++) {
|
||||
const line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
if (line.listLevel) {
|
||||
const numSpaces = line.listLevel * 2 - 1;
|
||||
const numSpaces = (line.listLevel as number) * 2 - 1;
|
||||
const bullet = '*';
|
||||
pieces.push(new Array(numSpaces + 1).join(' '), bullet, ' ', line.text, '\n');
|
||||
} else {
|
||||
|
|
@ -49,10 +48,14 @@ exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any;
|
|||
return pieces.join('');
|
||||
};
|
||||
type LineModel = {
|
||||
[id:string]:string|number|LineModel
|
||||
listLevel?: number;
|
||||
text?: string
|
||||
listTypeName?: string;
|
||||
start?: string;
|
||||
aline?: string;
|
||||
}
|
||||
|
||||
exports._analyzeLine = (text:string, aline: string, apool: AttributePool) => {
|
||||
export const _analyzeLine = (text:string, aline: string, apool: AttributePool) => {
|
||||
const line: LineModel = {};
|
||||
|
||||
// identify list
|
||||
|
|
@ -88,5 +91,5 @@ exports._analyzeLine = (text:string, aline: string, apool: AttributePool) => {
|
|||
};
|
||||
|
||||
|
||||
exports._encodeWhitespace =
|
||||
export const _encodeWhitespace =
|
||||
(s:string) => s.replace(/[^\x21-\x7E\s\t\n\r]/gu, (c) => `&#${c.codePointAt(0)};`);
|
||||
|
|
|
|||
|
|
@ -21,17 +21,18 @@ import {MapArrayType} from "../types/MapType";
|
|||
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
|
||||
const attributes = require('../../static/js/attributes');
|
||||
const padManager = require('../db/PadManager');
|
||||
const _ = require('underscore');
|
||||
import _ from 'underscore';
|
||||
const Security = require('../../static/js/security');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const eejs = require('../eejs');
|
||||
import eejs from '../eejs';
|
||||
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
|
||||
import padutils from "../../static/js/pad_utils";
|
||||
import {StringIterator} from "../../static/js/StringIterator";
|
||||
import {StringAssembler} from "../../static/js/StringAssembler";
|
||||
import Pad from "../db/Pad";
|
||||
|
||||
const getPadHTML = async (pad: PadType, revNum: string) => {
|
||||
export const getPadHTML = async (pad: Pad, revNum: number) => {
|
||||
let atext = pad.atext;
|
||||
|
||||
// fetch revision atext
|
||||
|
|
@ -43,7 +44,7 @@ const getPadHTML = async (pad: PadType, revNum: string) => {
|
|||
return await getHTMLFromAtext(pad, atext);
|
||||
};
|
||||
|
||||
const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string[]) => {
|
||||
export const getHTMLFromAtext = async (pad: Pad, atext: AText, authorColors?: MapArrayType<string>) => {
|
||||
const apool = pad.apool();
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
const attribLines = splitAttributionLines(atext.attribs, atext.text);
|
||||
|
|
@ -476,7 +477,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
|
|||
return pieces.join('');
|
||||
};
|
||||
|
||||
exports.getPadHTMLDocument = async (padId: string, revNum: string, readOnlyId: number) => {
|
||||
export const getPadHTMLDocument = async (padId: string, revNum: number, readOnlyId: number) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
// Include some Styles into the Head for Export
|
||||
|
|
@ -548,5 +549,11 @@ const _processSpaces = (s: string) => {
|
|||
return parts.join('');
|
||||
};
|
||||
|
||||
exports.getPadHTML = getPadHTML;
|
||||
exports.getHTMLFromAtext = getHTMLFromAtext;
|
||||
export default {
|
||||
getPadHTML,
|
||||
getHTMLFromAtext,
|
||||
getPadHTMLDocument,
|
||||
_processSpaces,
|
||||
_analyzeLine,
|
||||
_encodeWhitespace,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@
|
|||
*/
|
||||
|
||||
import {AText, PadType} from "../types/PadType";
|
||||
import {MapType} from "../types/MapType";
|
||||
import {MapArrayType, MapType} from "../types/MapType";
|
||||
|
||||
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
|
||||
import {StringIterator} from "../../static/js/StringIterator";
|
||||
import {StringAssembler} from "../../static/js/StringAssembler";
|
||||
const attributes = require('../../static/js/attributes');
|
||||
const padManager = require('../db/PadManager');
|
||||
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
import padManager from '../db/PadManager';
|
||||
import {_analyzeLine} from "./ExportHelper";
|
||||
import Pad from "../db/Pad";
|
||||
|
||||
// This is slightly different than the HTML method as it passes the output to getTXTFromAText
|
||||
const getPadTXT = async (pad: PadType, revNum: string) => {
|
||||
const getPadTXT = async (pad: Pad, revNum: number) => {
|
||||
let atext = pad.atext;
|
||||
|
||||
if (revNum !== undefined) {
|
||||
|
|
@ -44,7 +45,7 @@ const getPadTXT = async (pad: PadType, revNum: string) => {
|
|||
|
||||
// This is different than the functionality provided in ExportHtml as it provides formatting
|
||||
// functionality that is designed specifically for TXT exports
|
||||
const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
|
||||
const getTXTFromAtext = (pad: Pad, atext: AText, authorColors?:string) => {
|
||||
const apool = pad.apool();
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
const attribLines = splitAttributionLines(atext.attribs, atext.text);
|
||||
|
|
@ -195,12 +196,12 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
|
|||
// want to deal gracefully with blank lines.
|
||||
// => keeps track of the parents level of indentation
|
||||
|
||||
const listNumbers:MapType = {};
|
||||
const listNumbers:MapArrayType<number> = {};
|
||||
let prevListLevel;
|
||||
|
||||
for (let i = 0; i < textLines.length; i++) {
|
||||
const line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
let lineContent = getLineTXT(line.text, line.aline);
|
||||
let lineContent = getLineTXT(line.text!, line.aline);
|
||||
|
||||
if (line.listTypeName === 'bullet') {
|
||||
lineContent = `* ${lineContent}`; // add a bullet
|
||||
|
|
@ -213,7 +214,7 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (line.listLevel > 0) {
|
||||
if (line.listLevel && line.listLevel > 0) {
|
||||
for (let j = line.listLevel - 1; j >= 0; j--) {
|
||||
pieces.push('\t'); // tab indent list numbers..
|
||||
if (!listNumbers[line.listLevel]) {
|
||||
|
|
@ -235,12 +236,11 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
|
|||
* To handle going back to 2.1 when prevListLevel is lower number
|
||||
* than current line.listLevel then reset the object value
|
||||
*/
|
||||
if (line.listLevel < prevListLevel) {
|
||||
if (prevListLevel && line.listLevel < prevListLevel) {
|
||||
delete listNumbers[prevListLevel];
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
listNumbers[line.listLevel]++;
|
||||
listNumbers[line.listLevel.toString()]++;
|
||||
if (line.listLevel > 1) {
|
||||
let x = 1;
|
||||
while (x <= line.listLevel - 1) {
|
||||
|
|
@ -263,9 +263,13 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
|
|||
return pieces.join('');
|
||||
};
|
||||
|
||||
exports.getTXTFromAtext = getTXTFromAtext;
|
||||
|
||||
exports.getPadTXTDocument = async (padId:string, revNum:string) => {
|
||||
export const getPadTXTDocument = async (padId:string, revNum: number) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
return getPadTXT(pad, revNum);
|
||||
};
|
||||
|
||||
export default {
|
||||
getPadTXT,
|
||||
getTXTFromAtext,
|
||||
getPadTXTDocument,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {APool} from "../types/PadType";
|
|||
|
||||
import AttributePool from '../../static/js/AttributePool';
|
||||
const {Pad} = require('../db/Pad');
|
||||
const Stream = require('./Stream');
|
||||
import Stream from './Stream';
|
||||
const authorManager = require('../db/AuthorManager');
|
||||
const db = require('../db/DB');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import log4js from 'log4js';
|
|||
import {deserializeOps} from '../../static/js/Changeset';
|
||||
const contentcollector = require('../../static/js/contentcollector');
|
||||
import jsdom from 'jsdom';
|
||||
import {PadType} from "../types/PadType";
|
||||
import {Builder} from "../../static/js/Builder";
|
||||
import Pad from "../db/Pad";
|
||||
|
||||
const apiLogger = log4js.getLogger('ImportHtml');
|
||||
let processor:any;
|
||||
|
||||
exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => {
|
||||
export const setPadHTML = async (pad: Pad, html:string, authorId = '') => {
|
||||
if (processor == null) {
|
||||
const [{rehype}, {default: minifyWhitespace}] =
|
||||
await Promise.all([import('rehype'), import('rehype-minify-whitespace')]);
|
||||
|
|
@ -93,3 +93,7 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => {
|
|||
await pad.setText('\n', authorId);
|
||||
await pad.appendRevision(theChangeset, authorId);
|
||||
};
|
||||
|
||||
export default {
|
||||
setPadHTML,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,4 +136,4 @@ class Stream {
|
|||
[Symbol.iterator]() { return this._iter; }
|
||||
}
|
||||
|
||||
module.exports = Stream;
|
||||
export default Stream;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const CustomError = require('../utils/customError');
|
||||
import CustomError from '../utils/customError';
|
||||
|
||||
// checks if a rev is a legal number
|
||||
// pre-condition is that `rev` is not undefined
|
||||
const checkValidRev = (rev: number|string) => {
|
||||
export const checkValidRev = (rev: number|string) => {
|
||||
if (typeof rev !== 'number') {
|
||||
rev = parseInt(rev, 10);
|
||||
}
|
||||
|
|
@ -28,7 +28,4 @@ const checkValidRev = (rev: number|string) => {
|
|||
};
|
||||
|
||||
// checks if a number is an int
|
||||
const isInt = (value:number) => (parseFloat(String(value)) === parseInt(String(value), 10)) && !isNaN(value);
|
||||
|
||||
exports.isInt = isInt;
|
||||
exports.checkValidRev = checkValidRev;
|
||||
export const isInt = (value:number) => (parseFloat(String(value)) === parseInt(String(value), 10)) && !isNaN(value);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
'use strict';
|
||||
/**
|
||||
* CustomError
|
||||
*
|
||||
|
|
@ -21,4 +20,4 @@ class CustomError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = CustomError;
|
||||
export default CustomError
|
||||
|
|
|
|||
|
|
@ -11,17 +11,18 @@ import {numToString} from "../../static/js/ChangesetUtils";
|
|||
import Op from "../../static/js/Op";
|
||||
import {StringAssembler} from "../../static/js/StringAssembler";
|
||||
const attributes = require('../../static/js/attributes');
|
||||
const exportHtml = require('./ExportHtml');
|
||||
import exportHtml from './ExportHtml';
|
||||
import Pad from "../db/Pad";
|
||||
|
||||
|
||||
class PadDiff {
|
||||
private readonly _pad: PadType;
|
||||
private readonly _pad: Pad;
|
||||
private readonly _fromRev: string;
|
||||
private readonly _toRev: string;
|
||||
private _html: any;
|
||||
public _authors: any[];
|
||||
private self: PadDiff | undefined
|
||||
constructor(pad: PadType, fromRev:string, toRev:string) {
|
||||
constructor(pad: Pad, fromRev:number, toRev:number) {
|
||||
// check parameters
|
||||
if (!pad || !pad.id || !pad.atext || !pad.pool) {
|
||||
throw new Error('Invalid pad');
|
||||
|
|
@ -466,4 +467,4 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
};
|
||||
|
||||
// export the constructor
|
||||
module.exports = PadDiff;
|
||||
export default PadDiff
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@
|
|||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.62.0",
|
||||
"@opentelemetry/resources": "^2.0.1",
|
||||
"@opentelemetry/sdk-metrics": "^2.0.1",
|
||||
"@opentelemetry/sdk-node": "^0.203.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.0.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.36.0",
|
||||
"async": "^3.2.6",
|
||||
"axios": "^1.11.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
const Stream = require('../../../node/utils/Stream');
|
||||
import Stream from '../../../node/utils/Stream';
|
||||
import {strict} from "assert";
|
||||
import {it,describe,} from 'vitest'
|
||||
|
||||
class DemoIterable {
|
||||
private value: number;
|
||||
|
|
@ -24,7 +25,7 @@ class DemoIterable {
|
|||
const alreadyCompleted = this.completed();
|
||||
this.errs.push(err);
|
||||
if (alreadyCompleted) throw err; // Mimic standard generator objects.
|
||||
throw err;
|
||||
return err
|
||||
}
|
||||
|
||||
return(ret: number) {
|
||||
|
|
@ -112,11 +113,11 @@ describe(__filename, function () {
|
|||
|
||||
it('throw is propagated', async function () {
|
||||
const underlying = new DemoIterable();
|
||||
const s = new Stream(underlying);
|
||||
const s = new Stream(underlying satisfies Iterable<any, any, any>);
|
||||
const iter = s[Symbol.iterator]();
|
||||
strict.deepEqual(iter.next(), {value: 0, done: false});
|
||||
const err = new Error('injected');
|
||||
strict.throws(() => iter.throw(err), err);
|
||||
strict.throws(() => iter!.throw!(err), err);
|
||||
strict.equal(underlying.errs[0], err);
|
||||
});
|
||||
|
||||
|
|
@ -125,7 +126,7 @@ describe(__filename, function () {
|
|||
const s = new Stream(underlying);
|
||||
const iter = s[Symbol.iterator]();
|
||||
strict.deepEqual(iter.next(), {value: 0, done: false});
|
||||
strict.deepEqual(iter.return(42), {value: 42, done: true});
|
||||
strict.deepEqual(iter.return!(42), {value: 42, done: true});
|
||||
strict.equal(underlying.rets[0], 42);
|
||||
});
|
||||
});
|
||||
|
|
@ -225,7 +226,7 @@ describe(__filename, function () {
|
|||
strict.equal(lastYield, 'promise of 2');
|
||||
strict.equal(await nextp, 0);
|
||||
await strict.rejects(iter.next().value, err);
|
||||
iter.return();
|
||||
iter.return!();
|
||||
});
|
||||
|
||||
it('batched Promise rejections are unsuppressed when iteration completes', async function () {
|
||||
|
|
@ -243,7 +244,7 @@ describe(__filename, function () {
|
|||
const iter = s[Symbol.iterator]();
|
||||
strict.equal(await iter.next().value, 0);
|
||||
strict.equal(lastYield, 'promise of 2');
|
||||
await assertUnhandledRejection(() => iter.return(), err);
|
||||
await assertUnhandledRejection(() => iter.return!(), err);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -319,7 +320,7 @@ describe(__filename, function () {
|
|||
strict.equal(lastYield, 'promise of 2');
|
||||
strict.equal(await nextp, 0);
|
||||
await strict.rejects(iter.next().value, err);
|
||||
iter.return();
|
||||
iter.return!();
|
||||
});
|
||||
|
||||
it('buffered Promise rejections are unsuppressed when iteration completes', async function () {
|
||||
|
|
@ -337,7 +338,7 @@ describe(__filename, function () {
|
|||
const iter = s[Symbol.iterator]();
|
||||
strict.equal(await iter.next().value, 0);
|
||||
strict.equal(lastYield, 'promise of 2');
|
||||
await assertUnhandledRejection(() => iter.return(), err);
|
||||
await assertUnhandledRejection(() => iter!.return!(), err);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue