1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-01-14 00:29:38 +02:00

Compare commits

..

23 Commits

Author SHA1 Message Date
Laurent Cozic
593c3a9e3f update 2021-09-17 10:21:13 +01:00
Laurent Cozic
9b143b8da7 tests 2021-09-16 17:33:45 +01:00
Laurent Cozic
5e8b7420ff Server: Added support for app level slow SQL query log 2021-09-15 23:14:14 +01:00
Laurent Cozic
8ae4e30fd2 Server v2.4.7 2021-09-15 16:58:59 +01:00
Laurent Cozic
3ce947e82c Server: Fixed handling of brute force limiter by getting correct user IP 2021-09-15 16:57:18 +01:00
Laurent Cozic
c2298213d7 Server: Improve flag logic 2021-09-15 12:06:01 +01:00
Abdunnasir Saeed
9679f03cfa All: Translation: Update ar.po (#5464)
A complete Arabic translation
2021-09-15 06:39:50 -04:00
Laurent Cozic
3cddac3931 Server v2.4.6 2021-09-14 16:02:51 +01:00
Laurent Cozic
41c1e3bec9 Server: Fix transaction deadlock logging 2021-09-14 15:59:01 +01:00
Laurent Cozic
25c5892e74 Server v2.4.5 2021-09-14 13:02:56 +01:00
Laurent Cozic
a661a73511 Revert "Server: Enable multi platform builds (amd64, armv7 and arm64) (#5338)"
This reverts commit ab134807ea.

Does not build:

https://github.com/laurent22/joplin/runs/3597996286?check_suite_focus=true#step:8:388
2021-09-14 13:01:33 +01:00
Laurent Cozic
b00959e143 Server v2.4.4 2021-09-14 12:16:47 +01:00
Laurent Cozic
f6f5d6808d Merge branch 'dev' into release-2.4 2021-09-14 12:07:04 +01:00
Laurent Cozic
01b653fc34 Server: Add transaction info to debug deadlock issues 2021-09-14 12:05:29 +01:00
Laurent Cozic
4e7fe66883 Server: Add link to Stripe subscription page to manage payment details 2021-09-13 12:30:36 +01:00
mrkaato
cd99e675d9 All: Translation: Update fi_FI.po (#5452) 2021-09-12 13:00:30 -04:00
Laurent Cozic
a7130ce17a Tools: Added script to compile SASS files 2021-09-12 16:35:08 +01:00
Laurent Cozic
20f8743079 Tools: Upgrade back package-lock files to v2 2021-09-12 16:34:03 +01:00
Laurent Cozic
660b53575e Doc: Update sponsors 2021-09-12 13:08:02 +01:00
Kenichi Kobayashi
6c43b78496 All: Fixes #5447: Plugin onNoteSelectionChange() is triggered twice after a search (#5449) 2021-09-12 11:40:14 +01:00
Helmut K. C. Tessarek
9d5d891fe3 Desktop, Mobile: Resolves #5295: Update Mermaid 8.10.2 -> 8.12.1 and fix gitGraph crash (#5448) 2021-09-11 19:47:01 +01:00
Laurent Cozic
96ac12b460 Chore: Converted encryption config screens to React Hooks to share logic between desktop and mobile 2021-09-10 19:05:47 +01:00
Laurent Cozic
4b93664240 Desktop release v2.4.6 2021-09-09 19:25:54 +01:00
63 changed files with 1622 additions and 1440 deletions

View File

@@ -76,6 +76,9 @@ packages/app-cli/app/command-e2ee.js.map
packages/app-cli/app/command-settingschema.d.ts
packages/app-cli/app/command-settingschema.js
packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/command-testing.d.ts
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-testing.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
@@ -109,6 +112,9 @@ packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/testUtils.d.ts
packages/app-cli/tests/testUtils.js
packages/app-cli/tests/testUtils.js.map
packages/app-cli/tools/populateDatabase.d.ts
packages/app-cli/tools/populateDatabase.js
packages/app-cli/tools/populateDatabase.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map

View File

@@ -35,14 +35,6 @@ jobs:
sudo apt-get update || true
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# the next line enables multi-architecture support for docker, it basically makes it use qemu for non native platforms
# See https://hub.docker.com/r/tonistiigi/binfmt for more info
docker run --privileged --rm tonistiigi/binfmt --install all
# this just prints the info about what platforms are supported in the builder (can help debugging if something isn't working right)
# and also proves the above worked properly
sudo docker buildx ls
- uses: actions/checkout@v2
- uses: olegtarasov/get-tag@v2.1
- uses: actions/setup-node@v2

6
.gitignore vendored
View File

@@ -61,6 +61,9 @@ packages/app-cli/app/command-e2ee.js.map
packages/app-cli/app/command-settingschema.d.ts
packages/app-cli/app/command-settingschema.js
packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/command-testing.d.ts
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-testing.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
@@ -94,6 +97,9 @@ packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/testUtils.d.ts
packages/app-cli/tests/testUtils.js
packages/app-cli/tests/testUtils.js.map
packages/app-cli/tools/populateDatabase.d.ts
packages/app-cli/tools/populateDatabase.js
packages/app-cli/tools/populateDatabase.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map

View File

@@ -23,4 +23,6 @@ tests/support/dropbox-auth.txt
tests/support/nextcloud-auth.json
tests/support/onedrive-auth.txt
build/
patches/
patches/
createUsers-*.txt
tools/temp/

View File

@@ -89,7 +89,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
flags = cliUtils.parseFlags(flags);
if (!flags.arg) {
booleanFlags.push(flags.short);
if (flags.short) booleanFlags.push(flags.short);
if (flags.long) booleanFlags.push(flags.long);
}

View File

@@ -0,0 +1,95 @@
const { BaseCommand } = require('./base-command.js');
import { reg } from '@joplin/lib/registry';
import Note from '@joplin/lib/models/Note';
import uuid from '@joplin/lib/uuid';
import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
function randomElement(array: any[]): any {
if (!array.length) return null;
return array[Math.floor(Math.random() * array.length)];
}
function itemCount(args: any) {
const count = Number(args.arg0);
if (!count || isNaN(count)) throw new Error('Note count must be specified');
return count;
}
class Command extends BaseCommand {
usage() {
return 'testing <command> [arg0]';
}
description() {
return 'testing';
}
enabled() {
return false;
}
options(): any[] {
return [
['--folder-count <count>', 'Folders to create'],
['--note-count <count>', 'Notes to create'],
['--tag-count <count>', 'Tags to create'],
['--tags-per-note <count>', 'Tags per note'],
['--silent', 'Silent'],
];
}
async action(args: any) {
const { command, options } = args;
if (command === 'populate') {
await populateDatabase(reg.db(), {
folderCount: options['folder-count'],
noteCount: options['note-count'],
tagCount: options['tag-count'],
tagsPerNote: options['tags-per-note'],
silent: options['silent'],
});
}
const promises: any[] = [];
if (command === 'createRandomNotes') {
const noteCount = itemCount(args);
for (let i = 0; i < noteCount; i++) {
promises.push(Note.save({
title: `Note ${uuid.createNano()}`,
}));
}
}
if (command === 'updateRandomNotes') {
const noteCount = itemCount(args);
const noteIds = await Note.allIds();
for (let i = 0; i < noteCount; i++) {
const noteId = randomElement(noteIds);
promises.push(Note.save({
id: noteId,
title: `Note ${uuid.createNano()}`,
}));
}
}
if (command === 'deleteRandomNotes') {
const noteCount = itemCount(args);
const noteIds = await Note.allIds();
for (let i = 0; i < noteCount; i++) {
const noteId = randomElement(noteIds);
promises.push(Note.delete(noteId));
}
}
await Promise.all(promises);
}
}
module.exports = Command;

52
packages/app-cli/createUsers.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Start the server with:
#
# JOPLIN_IS_TESTING=1 npm run start-dev
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# curl --data '{"action": "clearDatabase"}' -H 'Content-Type: application/json' http://api.joplincloud.local:22300/api/debug
# SMALL
# curl --data '{"action": "createTestUsers", "count": 400, "fromNum": 1}' -H 'Content-Type: application/json' http://api.joplincloud.local:22300/api/debug
NUM=398
while [ "$NUM" -lt 400 ]; do
NUM=$(( NUM + 1 ))
echo "User $NUM"
CMD_FILE="$SCRIPT_DIR/createUsers-$NUM.txt"
PROFILE_DIR=~/.config/joplindev-testing-$NUM
USER_EMAIL="user$NUM@example.com"
rm -rf "$CMD_FILE" "$PROFILE_DIR"
touch "$CMD_FILE"
FLAG_FOLDER_COUNT=100
FLAG_NOTE_COUNT=1000
FLAG_TAG_COUNT=20
if [ "$NUM" -gt 300 ]; then
FLAG_FOLDER_COUNT=2000
FLAG_NOTE_COUNT=10000
FLAG_TAG_COUNT=200
fi
if [ "$NUM" -gt 399 ]; then
FLAG_FOLDER_COUNT=10000
FLAG_NOTE_COUNT=150000
FLAG_TAG_COUNT=2000
fi
echo "testing populate --silent --folder-count $FLAG_FOLDER_COUNT --note-count $FLAG_NOTE_COUNT --tag-count $FLAG_TAG_COUNT" >> "$CMD_FILE"
echo "config keychain.supported 0" >> "$CMD_FILE"
echo "config sync.target 10" >> "$CMD_FILE"
echo "config sync.10.username $USER_EMAIL" >> "$CMD_FILE"
echo "config sync.10.password hunter1hunter2hunter3" >> "$CMD_FILE"
echo "sync" >> "$CMD_FILE"
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
done

View File

@@ -10,6 +10,7 @@
"test-ci": "jest --config=jest.config.js --forceExit",
"build": "gulp build",
"start": "gulp build -L && node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",
"start-no-build": "node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json"
},

View File

@@ -0,0 +1,86 @@
import * as fs from 'fs-extra';
import { homedir } from 'os';
import { execCommand2 } from '@joplin/tools/tool-utils';
import { chdir } from 'process';
const minUserNum = 1;
const maxUserNum = 400;
const cliDir = `${__dirname}/..`;
const tempDir = `${__dirname}/temp`;
function randomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const processing_: Record<number, boolean> = {};
const processUser = async (userNum: number) => {
if (processing_[userNum]) {
console.info(`User already being processed: ${userNum} - skipping`);
return;
}
processing_[userNum] = true;
try {
const userEmail = `user${userNum}@example.com`;
const userPassword = 'hunter1hunter2hunter3';
const commandFile = `${tempDir}/populateDatabase-${userNum}.txt`;
const profileDir = `${homedir()}/.config/joplindev-populate/joplindev-testing-${userNum}`;
const commands: string[] = [];
const jackpot = Math.random() >= 0.95 ? 100 : 1;
commands.push(`testing createRandomNotes ${randomInt(1, 500 * jackpot)}`);
commands.push(`testing updateRandomNotes ${randomInt(1, 1500 * jackpot)}`);
commands.push(`testing deleteRandomNotes ${randomInt(1, 200 * jackpot)}`);
commands.push('config keychain.supported 0');
commands.push('config sync.target 10');
commands.push(`config sync.10.username ${userEmail}`);
commands.push(`config sync.10.password ${userPassword}`);
commands.push('sync');
await fs.writeFile(commandFile, commands.join('\n'), 'utf8');
await chdir(cliDir);
await execCommand2(['npm', 'run', 'start-no-build', '--', '--profile', profileDir, 'batch', commandFile]);
} catch (error) {
console.error(`Could not process user ${userNum}:`, error);
} finally {
delete processing_[userNum];
}
};
const waitForProcessing = (count: number) => {
return new Promise((resolve) => {
const iid = setInterval(() => {
if (Object.keys(processing_).length <= count) {
clearInterval(iid);
resolve(null);
}
}, 100);
});
};
const main = async () => {
await fs.mkdirp(tempDir);
// Build the app once before starting, because we'll use start-no-build to
// run the scripts (faster)
await execCommand2(['npm', 'run', 'build']);
while (true) {
const userNum = randomInt(minUserNum, maxUserNum);
void processUser(userNum);
await waitForProcessing(10);
}
};
main().catch((error) => {
console.error('Fatal error', error);
process.exit(1);
});

View File

@@ -0,0 +1,5 @@
.encryption-config-test {
& > .item {
font-weight: bold;
}
}

View File

@@ -1,5 +1,6 @@
const gulp = require('gulp');
const utils = require('@joplin/tools/gulp/utils');
const compileSass = require('@joplin/tools/compileSass');
const tasks = {
compileScripts: {
@@ -20,6 +21,14 @@ const tasks = {
tsc: require('@joplin/tools/gulp/tasks/tsc'),
updateIgnoredTypeScriptBuild: require('@joplin/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
buildCommandIndex: require('@joplin/tools/gulp/tasks/buildCommandIndex'),
compileSass: {
fn: async () => {
const guiDir = `${__dirname}/gui`;
await compileSass([
`${guiDir}/EncryptionConfigScreen/style.scss`,
], `${__dirname}/style.min.css`);
},
},
};
utils.registerGulpTasks(gulp, tasks);
@@ -31,6 +40,7 @@ const buildParallel = [
'copyTinyMceLangs',
'updateIgnoredTypeScriptBuild',
'buildCommandIndex',
'compileSass',
];
gulp.task('build', gulp.parallel(...buildParallel));

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/app-desktop",
"version": "2.4.5",
"version": "2.4.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/app-desktop",
"version": "2.4.5",
"version": "2.4.6",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.13.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.4.5",
"version": "2.4.6",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,

5
packages/app-desktop/style.min.css vendored Normal file
View File

@@ -0,0 +1,5 @@
.encryption-config-test > .item {
font-weight: bold;
}
/*# sourceMappingURL=style.min.css.map */

View File

@@ -1 +1 @@
module.exports = `LyoKCkF0b20gT25lIERhcmsgV2l0aCBzdXBwb3J0IGZvciBSZWFzb25NTCBieSBHaWRpIE1vcnJpcywgYmFzZWQgb2ZmIHdvcmsgYnkgRGFuaWVsIEdhbWFnZQoKT3JpZ2luYWwgT25lIERhcmsgU3ludGF4IHRoZW1lIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2F0b20vb25lLWRhcmstc3ludGF4CgoqLwouaGxqcyB7CiAgZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsKICBjb2xvcjogI2FiYjJiZjsKICBiYWNrZ3JvdW5kOiAjMjgyYzM0Owp9Ci5obGpzLWtleXdvcmQsIC5obGpzLW9wZXJhdG9yIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIC5obGpzLWNvbnN0cnVjdG9yIHsKICBjb2xvcjogIzYxYWVlZTsKfQouaGxqcy1mdW5jdGlvbiB7CiAgY29sb3I6ICM2MWFlZWU7Cn0KLmhsanMtZnVuY3Rpb24gLmhsanMtcGFyYW1zIHsKICBjb2xvcjogI0E2RTIyRTsKfQouaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5nIHsKICBjb2xvcjogI0ZEOTcxRjsKfQouaGxqcy1tb2R1bGUtYWNjZXNzIC5obGpzLW1vZHVsZSB7CiAgY29sb3I6ICM3ZTU3YzI7Cn0KLmhsanMtY29uc3RydWN0b3IgewogIGNvbG9yOiAjZTJiOTNkOwp9Ci5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZyB7CiAgY29sb3I6ICM5Q0NDNjU7Cn0KLmhsanMtY29tbWVudCwgLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYjE4ZWIxOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQouaGxqcy1kb2N0YWcsIC5obGpzLWZvcm11bGEgewogIGNvbG9yOiAjYzY3OGRkOwp9Ci5obGpzLXNlY3Rpb24sIC5obGpzLW5hbWUsIC5obGpzLXNlbGVjdG9yLXRhZywgLmhsanMtZGVsZXRpb24sIC5obGpzLXN1YnN0IHsKICBjb2xvcjogI2UwNmM3NTsKfQouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzU2YjZjMjsKfQouaGxqcy1zdHJpbmcsIC5obGpzLXJlZ2V4cCwgLmhsanMtYWRkaXRpb24sIC5obGpzLWF0dHJpYnV0ZSwgLmhsanMtbWV0YS1zdHJpbmcgewogIGNvbG9yOiAjOThjMzc5Owp9Ci5obGpzLWJ1aWx0X2luLCAuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSB7CiAgY29sb3I6ICNlNmMwN2I7Cn0KLmhsanMtYXR0ciwgLmhsanMtdmFyaWFibGUsIC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLCAuaGxqcy10eXBlLCAuaGxqcy1zZWxlY3Rvci1jbGFzcywgLmhsanMtc2VsZWN0b3ItYXR0ciwgLmhsanMtc2VsZWN0b3ItcHNldWRvLCAuaGxqcy1udW1iZXIgewogIGNvbG9yOiAjZDE5YTY2Owp9Ci5obGpzLXN5bWJvbCwgLmhsanMtYnVsbGV0LCAuaGxqcy1saW5rLCAuaGxqcy1tZXRhLCAuaGxqcy1zZWxlY3Rvci1pZCwgLmhsanMtdGl0bGUgewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQouaGxqcy1saW5rIHsKICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKfQo=`;
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiNhYmIyYmY7YmFja2dyb3VuZDojMjgyYzM0fS5obGpzLWtleXdvcmQsLmhsanMtb3BlcmF0b3IsLmhsanMtcGF0dGVybi1tYXRjaHtjb2xvcjojZjkyNjcyfS5obGpzLWZ1bmN0aW9uLC5obGpzLXBhdHRlcm4tbWF0Y2ggLmhsanMtY29uc3RydWN0b3J7Y29sb3I6IzYxYWVlZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXN7Y29sb3I6I2E2ZTIyZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5ne2NvbG9yOiNmZDk3MWZ9LmhsanMtbW9kdWxlLWFjY2VzcyAuaGxqcy1tb2R1bGV7Y29sb3I6IzdlNTdjMn0uaGxqcy1jb25zdHJ1Y3Rvcntjb2xvcjojZTJiOTNkfS5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZ3tjb2xvcjojOWNjYzY1fS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2IxOGViMTtmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYXtjb2xvcjojYzY3OGRkfS5obGpzLWRlbGV0aW9uLC5obGpzLW5hbWUsLmhsanMtc2VjdGlvbiwuaGxqcy1zZWxlY3Rvci10YWcsLmhsanMtc3Vic3R7Y29sb3I6I2UwNmM3NX0uaGxqcy1saXRlcmFse2NvbG9yOiM1NmI2YzJ9LmhsanMtYWRkaXRpb24sLmhsanMtYXR0cmlidXRlLC5obGpzLW1ldGEgLmhsanMtc3RyaW5nLC5obGpzLXJlZ2V4cCwuaGxqcy1zdHJpbmd7Y29sb3I6Izk4YzM3OX0uaGxqcy1idWlsdF9pbiwuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSwuaGxqcy10aXRsZS5jbGFzc197Y29sb3I6I2U2YzA3Yn0uaGxqcy1hdHRyLC5obGpzLW51bWJlciwuaGxqcy1zZWxlY3Rvci1hdHRyLC5obGpzLXNlbGVjdG9yLWNsYXNzLC5obGpzLXNlbGVjdG9yLXBzZXVkbywuaGxqcy10ZW1wbGF0ZS12YXJpYWJsZSwuaGxqcy10eXBlLC5obGpzLXZhcmlhYmxle2NvbG9yOiNkMTlhNjZ9LmhsanMtYnVsbGV0LC5obGpzLWxpbmssLmhsanMtbWV0YSwuaGxqcy1zZWxlY3Rvci1pZCwuaGxqcy1zeW1ib2wsLmhsanMtdGl0bGV7Y29sb3I6IzYxYWVlZX0uaGxqcy1lbXBoYXNpc3tmb250LXN0eWxlOml0YWxpY30uaGxqcy1zdHJvbmd7Zm9udC13ZWlnaHQ6NzAwfS5obGpzLWxpbmt7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX0=`;

View File

@@ -1 +1 @@
module.exports = `LyoKCkF0b20gT25lIExpZ2h0IGJ5IERhbmllbCBHYW1hZ2UKT3JpZ2luYWwgT25lIExpZ2h0IFN5bnRheCB0aGVtZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9hdG9tL29uZS1saWdodC1zeW50YXgKCmJhc2U6ICAgICNmYWZhZmEKbW9uby0xOiAgIzM4M2E0Mgptb25vLTI6ICAjNjg2Yjc3Cm1vbm8tMzogICNhMGExYTcKaHVlLTE6ICAgIzAxODRiYgpodWUtMjogICAjNDA3OGYyCmh1ZS0zOiAgICNhNjI2YTQKaHVlLTQ6ICAgIzUwYTE0ZgpodWUtNTogICAjZTQ1NjQ5Cmh1ZS01LTI6ICNjOTEyNDMKaHVlLTY6ICAgIzk4NjgwMQpodWUtNi0yOiAjYzE4NDAxCgoqLwoKLmhsanMgewogIGRpc3BsYXk6IGJsb2NrOwogIG92ZXJmbG93LXg6IGF1dG87CiAgcGFkZGluZzogMC41ZW07CiAgY29sb3I6ICMzODNhNDI7CiAgYmFja2dyb3VuZDogI2ZhZmFmYTsKfQoKLmhsanMtY29tbWVudCwKLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYTBhMWE3OwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtZG9jdGFnLAouaGxqcy1rZXl3b3JkLAouaGxqcy1mb3JtdWxhIHsKICBjb2xvcjogI2E2MjZhNDsKfQoKLmhsanMtc2VjdGlvbiwKLmhsanMtbmFtZSwKLmhsanMtc2VsZWN0b3ItdGFnLAouaGxqcy1kZWxldGlvbiwKLmhsanMtc3Vic3QgewogIGNvbG9yOiAjZTQ1NjQ5Owp9CgouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzAxODRiYjsKfQoKLmhsanMtc3RyaW5nLAouaGxqcy1yZWdleHAsCi5obGpzLWFkZGl0aW9uLAouaGxqcy1hdHRyaWJ1dGUsCi5obGpzLW1ldGEtc3RyaW5nIHsKICBjb2xvcjogIzUwYTE0ZjsKfQoKLmhsanMtYnVpbHRfaW4sCi5obGpzLWNsYXNzIC5obGpzLXRpdGxlIHsKICBjb2xvcjogI2MxODQwMTsKfQoKLmhsanMtYXR0ciwKLmhsanMtdmFyaWFibGUsCi5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLAouaGxqcy10eXBlLAouaGxqcy1zZWxlY3Rvci1jbGFzcywKLmhsanMtc2VsZWN0b3ItYXR0ciwKLmhsanMtc2VsZWN0b3ItcHNldWRvLAouaGxqcy1udW1iZXIgewogIGNvbG9yOiAjOTg2ODAxOwp9CgouaGxqcy1zeW1ib2wsCi5obGpzLWJ1bGxldCwKLmhsanMtbGluaywKLmhsanMtbWV0YSwKLmhsanMtc2VsZWN0b3ItaWQsCi5obGpzLXRpdGxlIHsKICBjb2xvcjogIzQwNzhmMjsKfQoKLmhsanMtZW1waGFzaXMgewogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoKLmhsanMtbGluayB7CiAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7Cn0K`;
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiMzODNhNDI7YmFja2dyb3VuZDojZmFmYWZhfS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2EwYTFhNztmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYSwuaGxqcy1rZXl3b3Jke2NvbG9yOiNhNjI2YTR9LmhsanMtZGVsZXRpb24sLmhsanMtbmFtZSwuaGxqcy1zZWN0aW9uLC5obGpzLXNlbGVjdG9yLXRhZywuaGxqcy1zdWJzdHtjb2xvcjojZTQ1NjQ5fS5obGpzLWxpdGVyYWx7Y29sb3I6IzAxODRiYn0uaGxqcy1hZGRpdGlvbiwuaGxqcy1hdHRyaWJ1dGUsLmhsanMtbWV0YSAuaGxqcy1zdHJpbmcsLmhsanMtcmVnZXhwLC5obGpzLXN0cmluZ3tjb2xvcjojNTBhMTRmfS5obGpzLWF0dHIsLmhsanMtbnVtYmVyLC5obGpzLXNlbGVjdG9yLWF0dHIsLmhsanMtc2VsZWN0b3ItY2xhc3MsLmhsanMtc2VsZWN0b3ItcHNldWRvLC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLC5obGpzLXR5cGUsLmhsanMtdmFyaWFibGV7Y29sb3I6Izk4NjgwMX0uaGxqcy1idWxsZXQsLmhsanMtbGluaywuaGxqcy1tZXRhLC5obGpzLXNlbGVjdG9yLWlkLC5obGpzLXN5bWJvbCwuaGxqcy10aXRsZXtjb2xvcjojNDA3OGYyfS5obGpzLWJ1aWx0X2luLC5obGpzLWNsYXNzIC5obGpzLXRpdGxlLC5obGpzLXRpdGxlLmNsYXNzX3tjb2xvcjojYzE4NDAxfS5obGpzLWVtcGhhc2lze2ZvbnQtc3R5bGU6aXRhbGljfS5obGpzLXN0cm9uZ3tmb250LXdlaWdodDo3MDB9LmhsanMtbGlua3t0ZXh0LWRlY29yYXRpb246dW5kZXJsaW5lfQ==`;

View File

@@ -1,5 +1,5 @@
module.exports = {
hash:"6608023b8053b48e0eec248644475e33", files: {
hash:"de3871f000c87478973d7cd0913bd3ff", files: {
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },

File diff suppressed because one or more lines are too long

View File

@@ -36,26 +36,6 @@ dialogs.confirm = (parentComponent, message) => {
if (!('dialogbox' in parentComponent)) throw new Error('A "dialogbox" component must be defined on the parent component!');
return dialogs.confirmRef(parentComponent.dialogBox, message);
// return new Promise((resolve) => {
// Keyboard.dismiss();
// parentComponent.dialogbox.confirm({
// content: message,
// ok: {
// callback: () => {
// resolve(true);
// },
// },
// cancel: {
// callback: () => {
// resolve(false);
// },
// },
// });
// });
};
dialogs.pop = (parentComponent, message, buttons, options = null) => {

View File

@@ -5669,6 +5669,7 @@
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"deprecated": "This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.",
"optional": true,
"dependencies": {
"block-stream": "*",
@@ -15702,7 +15703,6 @@
},
"uslug": {
"version": "git+ssh://git@github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
"integrity": "sha512-6zzxOsQp+hbOW4zeplEUhKXnBzYIrqYAVlPepBFz/u5q2OulN7tCmBKyWEzDxaiZOLYnUCTViDLazNoq1J6ciA==",
"from": "uslug@git+https://github.com/laurent22/uslug.git#emoji-support",
"requires": {
"node-emoji": "^1.10.0",

View File

@@ -589,4 +589,25 @@ describe('reducer', function() {
expect(state.selectedFolderId).toEqual(null);
expect(state.selectedNoteIds[0]).toEqual(notes[1].id);
});
// tests for NOTE_UPDATE_ALL about issue #5447
it('should not change selectedNoteIds object when selections are not changed', async () => {
const folders = await createNTestFolders(1);
const notes = await createNTestNotes(5, folders[0]);
{
// Case 1. Selected notes are changed when one of selected notes is deleted.
let state = initTestState(folders, 0, notes, [0, 2, 4]);
state = reducer(state, { type: 'NOTE_UPDATE_ALL', notes: notes.slice(0, 4), notesSource: 'test' });
const expected = [notes[0].id, notes[2].id].sort();
expect([...state.selectedNoteIds].sort()).toEqual(expected);
}
{
// Case 2. Selected notes and object identity are unchanged when notes are not changed.
let state = initTestState(folders, 0, notes, [0, 2, 4]);
const expected = state.selectedNoteIds;
state = reducer(state, { type: 'NOTE_UPDATE_ALL', notes: notes, notesSource: 'test' });
// Object identity is checked. Don't use toEqual() or toStrictEqual() here.
expect(state.selectedNoteIds).toBe(expected);
}
});
});

View File

@@ -433,7 +433,7 @@ function updateSelectedNotesFromExistingNotes(draft: Draft<State>) {
}
}
}
if (JSON.stringify(draft.selectedNoteIds) === JSON.stringify(newSelectedNoteIds)) return;
draft.selectedNoteIds = newSelectedNoteIds;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,75 +1 @@
/*
Atom One Dark With support for ReasonML by Gidi Morris, based off work by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
}
.hljs-keyword, .hljs-operator {
color: #F92672;
}
.hljs-pattern-match {
color: #F92672;
}
.hljs-pattern-match .hljs-constructor {
color: #61aeee;
}
.hljs-function {
color: #61aeee;
}
.hljs-function .hljs-params {
color: #A6E22E;
}
.hljs-function .hljs-params .hljs-typing {
color: #FD971F;
}
.hljs-module-access .hljs-module {
color: #7e57c2;
}
.hljs-constructor {
color: #e2b93d;
}
.hljs-constructor .hljs-string {
color: #9CCC65;
}
.hljs-comment, .hljs-quote {
color: #b18eb1;
font-style: italic;
}
.hljs-doctag, .hljs-formula {
color: #c678dd;
}
.hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string {
color: #98c379;
}
.hljs-built_in, .hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-type, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-number {
color: #d19a66;
}
.hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title {
color: #61aeee;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-keyword,.hljs-operator,.hljs-pattern-match{color:#f92672}.hljs-function,.hljs-pattern-match .hljs-constructor{color:#61aeee}.hljs-function .hljs-params{color:#a6e22e}.hljs-function .hljs-params .hljs-typing{color:#fd971f}.hljs-module-access .hljs-module{color:#7e57c2}.hljs-constructor{color:#e2b93d}.hljs-constructor .hljs-string{color:#9ccc65}.hljs-comment,.hljs-quote{color:#b18eb1;font-style:italic}.hljs-doctag,.hljs-formula{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

View File

@@ -1,96 +1 @@
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
base: #fafafa
mono-1: #383a42
mono-2: #686b77
mono-3: #a0a1a7
hue-1: #0184bb
hue-2: #4078f2
hue-3: #a626a4
hue-4: #50a14f
hue-5: #e45649
hue-5-2: #c91243
hue-6: #986801
hue-6-2: #c18401
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #383a42;
background: #fafafa;
}
.hljs-comment,
.hljs-quote {
color: #a0a1a7;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #a626a4;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e45649;
}
.hljs-literal {
color: #0184bb;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #50a14f;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #c18401;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #986801;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #4078f2;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

File diff suppressed because one or more lines are too long

View File

@@ -29,7 +29,7 @@
"markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"mermaid": "^8.10.2",
"mermaid": "^8.12.1",
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
},
"devDependencies": {
@@ -1998,7 +1998,8 @@
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"node_modules/cache-base": {
"version": "1.0.1",
@@ -2029,15 +2030,6 @@
"node": ">=6"
}
},
"node_modules/camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
"dependencies": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -2135,25 +2127,6 @@
"node": ">=0.10.0"
}
},
"node_modules/clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
"node": ">= 4.0"
}
},
"node_modules/clean-css/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -2286,17 +2259,6 @@
"node": "*"
}
},
"node_modules/css-b64-images": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
"integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI=",
"bin": {
"css-b64-images": "bin/css-b64-images"
},
"engines": {
"node": "*"
}
},
"node_modules/cssom": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@@ -2647,6 +2609,7 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2797,6 +2760,11 @@
"node": ">=8"
}
},
"node_modules/dompurify": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.1.tgz",
"integrity": "sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw=="
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -2839,14 +2807,6 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
},
"node_modules/entity-decode": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
"integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
"dependencies": {
"he": "^1.1.1"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -3542,14 +3502,6 @@
"node": ">=0.10.0"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"bin": {
"he": "bin/he"
}
},
"node_modules/highlight.js": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.2.0.tgz",
@@ -3590,26 +3542,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"dependencies": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
},
"bin": {
"html-minifier": "cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -6045,11 +5977,6 @@
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
},
"node_modules/lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -6234,21 +6161,19 @@
"dev": true
},
"node_modules/mermaid": {
"version": "8.10.2",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.2.tgz",
"integrity": "sha512-Za5MrbAOMbEsyY4ONgGjfYz06sbwF1iNGRzp1sQqpOtvXxjxGu/J1jRJ8QyE9kD/D9zj1/KlRrYegWEvA7eZ5Q==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.12.1.tgz",
"integrity": "sha512-0UCcSF0FLoNcPBsRF4f9OIV32t41fV18//z8o3S+FDz2PbDA1CRGKdQF9IX84VP4Tv9kcgJI/oqJdcBEtB/GPA==",
"dependencies": {
"@braintree/sanitize-url": "^3.1.0",
"d3": "^5.7.0",
"dagre": "^0.8.4",
"d3": "^5.16.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"entity-decode": "^2.0.2",
"graphlib": "^2.1.7",
"he": "^1.2.0",
"khroma": "^1.1.0",
"minify": "^4.1.1",
"moment-mini": "^2.22.1",
"stylis": "^3.5.2"
"dompurify": "2.3.1",
"graphlib": "^2.1.8",
"khroma": "^1.4.1",
"moment-mini": "^2.24.0",
"stylis": "^4.0.10"
}
},
"node_modules/micromatch": {
@@ -6294,26 +6219,6 @@
"node": ">=6"
}
},
"node_modules/minify": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
"integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
"dependencies": {
"clean-css": "^4.1.6",
"css-b64-images": "~0.2.5",
"debug": "^4.1.0",
"html-minifier": "^4.0.0",
"terser": "^4.0.0",
"try-catch": "^2.0.0",
"try-to-catch": "^1.0.2"
},
"bin": {
"minify": "bin/minify.js"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -6365,7 +6270,8 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nanomatch": {
"version": "1.2.13",
@@ -6401,14 +6307,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"node_modules/no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"dependencies": {
"lower-case": "^1.1.1"
}
},
"node_modules/node-emoji": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
@@ -6684,14 +6582,6 @@
"node": ">=6"
}
},
"node_modules/param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"dependencies": {
"no-case": "^2.2.0"
}
},
"node_modules/parse-json": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
@@ -6973,14 +6863,6 @@
"node": ">=0.10.0"
}
},
"node_modules/relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -7661,6 +7543,7 @@
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -7670,6 +7553,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -7876,9 +7760,9 @@
}
},
"node_modules/stylis": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
"integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
},
"node_modules/supports-color": {
"version": "5.5.0",
@@ -7948,30 +7832,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"dependencies": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/terser/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -8084,19 +7944,6 @@
"node": ">=8"
}
},
"node_modules/try-catch": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
"integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/try-to-catch": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
"integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -8172,17 +8019,6 @@
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"node_modules/uglify-js": {
"version": "3.13.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz",
"integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==",
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/union-value": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
@@ -8262,11 +8098,6 @@
"node": ">=0.10.0"
}
},
"node_modules/upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
},
"node_modules/uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
@@ -10235,7 +10066,8 @@
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"cache-base": {
"version": "1.0.1",
@@ -10260,15 +10092,6 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -10347,21 +10170,6 @@
}
}
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
"requires": {
"source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -10475,11 +10283,6 @@
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"css-b64-images": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
"integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
},
"cssom": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@@ -10811,6 +10614,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -10921,6 +10725,11 @@
}
}
},
"dompurify": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.1.tgz",
"integrity": "sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw=="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -10957,14 +10766,6 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
},
"entity-decode": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
"integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
"requires": {
"he": "^1.1.1"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -11512,11 +11313,6 @@
}
}
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"highlight.js": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.2.0.tgz",
@@ -11548,20 +11344,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"requires": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -13427,11 +13209,6 @@
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
},
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -13594,21 +13371,19 @@
"dev": true
},
"mermaid": {
"version": "8.10.2",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.2.tgz",
"integrity": "sha512-Za5MrbAOMbEsyY4ONgGjfYz06sbwF1iNGRzp1sQqpOtvXxjxGu/J1jRJ8QyE9kD/D9zj1/KlRrYegWEvA7eZ5Q==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.12.1.tgz",
"integrity": "sha512-0UCcSF0FLoNcPBsRF4f9OIV32t41fV18//z8o3S+FDz2PbDA1CRGKdQF9IX84VP4Tv9kcgJI/oqJdcBEtB/GPA==",
"requires": {
"@braintree/sanitize-url": "^3.1.0",
"d3": "^5.7.0",
"dagre": "^0.8.4",
"d3": "^5.16.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"entity-decode": "^2.0.2",
"graphlib": "^2.1.7",
"he": "^1.2.0",
"khroma": "^1.1.0",
"minify": "^4.1.1",
"moment-mini": "^2.22.1",
"stylis": "^3.5.2"
"dompurify": "2.3.1",
"graphlib": "^2.1.8",
"khroma": "^1.4.1",
"moment-mini": "^2.24.0",
"stylis": "^4.0.10"
}
},
"micromatch": {
@@ -13642,20 +13417,6 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"minify": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
"integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
"requires": {
"clean-css": "^4.1.6",
"css-b64-images": "~0.2.5",
"debug": "^4.1.0",
"html-minifier": "^4.0.0",
"terser": "^4.0.0",
"try-catch": "^2.0.0",
"try-to-catch": "^1.0.2"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -13700,7 +13461,8 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nanomatch": {
"version": "1.2.13",
@@ -13733,14 +13495,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"requires": {
"lower-case": "^1.1.1"
}
},
"node-emoji": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
@@ -13951,14 +13705,6 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"requires": {
"no-case": "^2.2.0"
}
},
"parse-json": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
@@ -14172,11 +13918,6 @@
"safe-regex": "^1.1.0"
}
},
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
},
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -14731,6 +14472,7 @@
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -14739,7 +14481,8 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
@@ -14905,9 +14648,9 @@
"dev": true
},
"stylis": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
"integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
},
"supports-color": {
"version": "5.5.0",
@@ -14961,23 +14704,6 @@
"supports-hyperlinks": "^2.0.0"
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -15068,16 +14794,6 @@
"punycode": "^2.1.1"
}
},
"try-catch": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
"integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg=="
},
"try-to-catch": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
"integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -15134,11 +14850,6 @@
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"uglify-js": {
"version": "3.13.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz",
"integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g=="
},
"union-value": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
@@ -15201,11 +14912,6 @@
}
}
},
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
},
"uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
@@ -15229,7 +14935,6 @@
},
"uslug": {
"version": "git+ssh://git@github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
"integrity": "sha512-6zzxOsQp+hbOW4zeplEUhKXnBzYIrqYAVlPepBFz/u5q2OulN7tCmBKyWEzDxaiZOLYnUCTViDLazNoq1J6ciA==",
"from": "uslug@git+https://github.com/laurent22/uslug.git#emoji-support",
"requires": {
"node-emoji": "^1.10.0",

View File

@@ -45,7 +45,7 @@
"markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"mermaid": "^8.10.2",
"mermaid": "^8.12.1",
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
},
"gitHead": "80c0089d2c52aff608b2bea74389de5a7f12f2e2"

View File

@@ -1,12 +1,12 @@
{
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.7",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1",
"@koa/cors": "^3.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "2.4.3",
"version": "2.4.7",
"private": true,
"scripts": {
"start-dev": "nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",

View File

@@ -48,6 +48,9 @@ export interface EnvVariables {
BUSINESS_EMAIL?: string;
COOKIES_SECURE?: string;
SLOW_QUERY_LOG_ENABLED?: string;
SLOW_QUERY_LOG_MIN_DURATION?: string; // ms
}
let runningInDocker_: boolean = false;
@@ -56,6 +59,17 @@ export function runningInDocker(): boolean {
return runningInDocker_;
}
function envParseBool(s: string): boolean {
return s === '1';
}
function envParseInt(s: string, defaultValue: number = null): number {
if (!s) return defaultValue === null ? 0 : defaultValue;
const output = Number(s);
if (isNaN(output)) throw new Error(`Invalid number: ${s}`);
return output;
}
function databaseHostFromEnv(runningInDocker: boolean, env: EnvVariables): string {
if (env.POSTGRES_HOST) {
// When running within Docker, the app localhost is different from the
@@ -72,8 +86,16 @@ function databaseHostFromEnv(runningInDocker: boolean, env: EnvVariables): strin
}
function databaseConfigFromEnv(runningInDocker: boolean, env: EnvVariables): DatabaseConfig {
const baseConfig: DatabaseConfig = {
client: DatabaseConfigClient.Null,
name: '',
slowQueryLogEnabled: envParseBool(env.SLOW_QUERY_LOG_ENABLED),
slowQueryLogMinDuration: envParseInt(env.SLOW_QUERY_LOG_MIN_DURATION, 10000),
};
if (env.DB_CLIENT === 'pg') {
return {
...baseConfig,
client: DatabaseConfigClient.PostgreSQL,
name: env.POSTGRES_DATABASE || 'joplin',
user: env.POSTGRES_USER || 'joplin',
@@ -84,6 +106,7 @@ function databaseConfigFromEnv(runningInDocker: boolean, env: EnvVariables): Dat
}
return {
...baseConfig,
client: DatabaseConfigClient.SQLite,
name: env.SQLITE_DATABASE,
asyncStackTraces: true,

View File

@@ -104,25 +104,55 @@ export async function waitForConnection(dbConfig: DatabaseConfig): Promise<Conne
}
}
function makeSlowQueryHandler(duration: number, connection: any, sql: string, bindings: any[]) {
return setTimeout(() => {
try {
logger.warn(`Slow query (${duration}ms+):`, connection.raw(sql, bindings).toString());
} catch (error) {
logger.error('Could not log slow query', { sql, bindings }, error);
}
}, duration);
}
export function setupSlowQueryLog(connection: DbConnection, slowQueryLogMinDuration: number) {
interface QueryInfo {
timeoutId: any;
startTime: number;
}
const queryInfos: Record<any, QueryInfo> = {};
connection.on('query', (data) => {
const timeoutId = makeSlowQueryHandler(slowQueryLogMinDuration, connection, data.sql, data.bindings);
queryInfos[data.__knexQueryUid] = {
timeoutId,
startTime: Date.now(),
};
});
connection.on('query-response', (_response, data) => {
const q = queryInfos[data.__knexQueryUid];
if (q) {
clearTimeout(q.timeoutId);
delete queryInfos[data.__knexQueryUid];
}
});
connection.on('query-error', (_response, data) => {
const q = queryInfos[data.__knexQueryUid];
if (q) {
clearTimeout(q.timeoutId);
delete queryInfos[data.__knexQueryUid];
}
});
}
export async function connectDb(dbConfig: DatabaseConfig): Promise<DbConnection> {
const connection = knex(makeKnexConfig(dbConfig));
const debugSlowQueries = false;
if (debugSlowQueries) {
const startTimes: Record<string, number> = {};
const slowQueryDuration = 10;
connection.on('query', (data) => {
startTimes[data.__knexQueryUid] = Date.now();
});
connection.on('query-response', (_response, data) => {
const duration = Date.now() - startTimes[data.__knexQueryUid];
if (duration < slowQueryDuration) return;
console.info(`SQL: ${data.sql} (${duration}ms)`);
});
if (dbConfig.slowQueryLogEnabled) {
setupSlowQueryLog(connection, dbConfig.slowQueryLogMinDuration);
}
return connection;

View File

@@ -2,6 +2,7 @@ import { routeResponseFormat, Response, RouteResponseFormat, execRequest } from
import { AppContext, Env } from '../utils/types';
import { isView, View } from '../services/MustacheService';
import config from '../config';
import { userIp } from '../utils/requestUtils';
export default async function(ctx: AppContext) {
const requestStartTime = Date.now();
@@ -26,9 +27,9 @@ export default async function(ctx: AppContext) {
}
} catch (error) {
if (error.httpCode >= 400 && error.httpCode < 500) {
ctx.joplin.appLogger().error(`${error.httpCode}: ` + `${ctx.request.method} ${ctx.path}` + ` : ${error.message}`);
ctx.joplin.appLogger().error(`${error.httpCode}: ` + `${ctx.request.method} ${ctx.path}` + `: ${userIp(ctx)}: ${error.message}`);
} else {
ctx.joplin.appLogger().error(error);
ctx.joplin.appLogger().error(userIp(ctx), error);
}
// Uncomment this when getting HTML blobs as errors while running tests.

View File

@@ -7,6 +7,9 @@ import { Models } from './factory';
import * as EventEmitter from 'events';
import { Config } from '../utils/types';
import personalizedUserContentBaseUrl from '@joplin/lib/services/joplinServer/personalizedUserContentBaseUrl';
import Logger from '@joplin/lib/Logger';
const logger = Logger.create('BaseModel');
export interface SaveOptions {
isNew?: boolean;
@@ -163,36 +166,38 @@ export default abstract class BaseModel<T> {
//
// The `name` argument is only for debugging, so that any stuck transaction
// can be more easily identified.
protected async withTransaction<T>(fn: Function, name: string = null): Promise<T> {
const debugTransaction = false;
protected async withTransaction<T>(fn: Function, name: string): Promise<T> {
const debugSteps = false;
const debugTimeout = true;
const timeoutMs = 10000;
const debugTimerId = debugTransaction ? setTimeout(() => {
console.info('Transaction did not complete:', name, txIndex);
}, 5000) : null;
let txIndex = 0;
const txIndex = await this.transactionHandler_.start();
const debugTimerId = debugTimeout ? setTimeout(() => {
logger.error(`Transaction #${txIndex} did not complete:`, name);
logger.error('Transaction stack:');
logger.error(this.transactionHandler_.stackInfo);
}, timeoutMs) : null;
if (debugTransaction) console.info('START', name, txIndex);
txIndex = await this.transactionHandler_.start(name);
if (debugSteps) console.info('START', name, txIndex);
let output: T = null;
try {
output = await fn();
} catch (error) {
if (debugSteps) console.info('ROLLBACK', name, txIndex);
await this.transactionHandler_.rollback(txIndex);
if (debugTransaction) {
console.info('ROLLBACK', name, txIndex);
clearTimeout(debugTimerId);
}
throw error;
} finally {
if (debugTimerId) clearTimeout(debugTimerId);
}
if (debugTransaction) {
console.info('COMMIT', name, txIndex);
clearTimeout(debugTimerId);
}
if (debugSteps) console.info('COMMIT', name, txIndex);
await this.transactionHandler_.commit(txIndex);
return output;

View File

@@ -34,7 +34,7 @@ export default class ItemResourceModel extends BaseModel<ItemResource> {
resource_id: resourceId,
});
}
});
}, 'ItemResourceModel::addResourceIds');
}
public async byItemId(itemId: Uuid): Promise<string[]> {

View File

@@ -48,7 +48,7 @@ export default class NotificationModel extends BaseModel<KeyValue> {
value: this.serializeValue(value),
type,
});
});
}, 'KeyValueModel::setValue');
}
public async value<T>(key: string, defaultValue: Value = null): Promise<T> {

View File

@@ -315,7 +315,7 @@ export default class ShareModel extends BaseModel<Share> {
for (const item of items) {
await this.models().userItem().add(userId, item.id);
}
});
}, 'ShareModel::createSharedFolderUserItems');
}
public async shareFolder(owner: User, folderId: string): Promise<Share> {

View File

@@ -126,7 +126,7 @@ export default class ShareUserModel extends BaseModel<ShareUser> {
}
return this.save({ ...shareUser, status });
});
}, 'ShareUserModel::setStatus');
}
public async deleteByShare(share: Share): Promise<void> {

View File

@@ -91,7 +91,7 @@ export default class SubscriptionModel extends BaseModel<Subscription> {
last_payment_time: now,
last_payment_failed_time: 0,
});
});
}, 'SubscriptionModel::handlePayment');
} else {
// We only update the payment failed time if it's not already set
// since the only thing that matter is the first time the payment
@@ -145,7 +145,7 @@ export default class SubscriptionModel extends BaseModel<Subscription> {
});
return { user, subscription };
});
}, 'SubscriptionModel::saveUserAndSubscription');
}
public async toggleSoftDelete(id: number, isDeleted: boolean) {

View File

@@ -70,7 +70,7 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
await this.add(userId, flagType, { updateUser: false });
}
await this.updateUserFromFlags(userId);
});
}, 'UserFlagModels::addMulti');
}
public async removeMulti(userId: Uuid, flagTypes: UserFlagType[]) {
@@ -79,7 +79,7 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
await this.remove(userId, flagType, { updateUser: false });
}
await this.updateUserFromFlags(userId);
});
}, 'UserFlagModels::removeMulti');
}
// As a general rule the `enabled` and `can_upload` properties should not
@@ -95,17 +95,34 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
enabled: 1,
};
if (flags.find(f => f.type === UserFlagType.AccountWithoutSubscription)) {
const accountWithoutSubscriptionFlag = flags.find(f => f.type === UserFlagType.AccountWithoutSubscription);
const accountOverLimitFlag = flags.find(f => f.type === UserFlagType.AccountOverLimit);
const failedPaymentWarningFlag = flags.find(f => f.type === UserFlagType.FailedPaymentWarning);
const failedPaymentFinalFlag = flags.find(f => f.type === UserFlagType.FailedPaymentFinal);
const subscriptionCancelledFlag = flags.find(f => f.type === UserFlagType.SubscriptionCancelled);
const manuallyDisabledFlag = flags.find(f => f.type === UserFlagType.ManuallyDisabled);
if (accountWithoutSubscriptionFlag) {
newProps.can_upload = 0;
} else if (flags.find(f => f.type === UserFlagType.AccountOverLimit)) {
}
if (accountOverLimitFlag) {
newProps.can_upload = 0;
} else if (flags.find(f => f.type === UserFlagType.FailedPaymentWarning)) {
}
if (failedPaymentWarningFlag) {
newProps.can_upload = 0;
} else if (flags.find(f => f.type === UserFlagType.FailedPaymentFinal)) {
}
if (failedPaymentFinalFlag) {
newProps.enabled = 0;
} else if (flags.find(f => f.type === UserFlagType.SubscriptionCancelled)) {
}
if (subscriptionCancelledFlag) {
newProps.enabled = 0;
} else if (flags.find(f => f.type === UserFlagType.ManuallyDisabled)) {
}
if (manuallyDisabledFlag) {
newProps.enabled = 0;
}

View File

@@ -141,7 +141,7 @@ export default class UserItemModel extends BaseModel<UserItem> {
}
return super.save(userItem, options);
});
}, 'UserItemModel::save');
}
public async delete(_id: string | string[], _options: DeleteOptions = {}): Promise<void> {

View File

@@ -369,7 +369,7 @@ export default class UserModel extends BaseModel<User> {
key: `payment_failed_upload_disabled_${sub.last_payment_failed_time}`,
});
}
});
}, 'UserModel::handleFailedPaymentSubscriptions');
}
public async handleOversizedAccounts() {
@@ -430,12 +430,12 @@ export default class UserModel extends BaseModel<User> {
});
}
}
});
}, 'UserModel::handleOversizedAccounts');
}
private formatValues(user: User): User {
const output: User = { ...user };
if ('email' in output) output.email = user.email.trim().toLowerCase();
if ('email' in output) output.email = (`${user.email}`).trim().toLowerCase();
return output;
}
@@ -466,7 +466,15 @@ export default class UserModel extends BaseModel<User> {
if (isNew) UserModel.eventEmitter.emit('created');
return savedUser;
});
}, 'UserModel::save');
}
public async saveMulti(users: User[], options: SaveOptions = {}): Promise<void> {
await this.withTransaction(async () => {
for (const user of users) {
await this.save(user, options);
}
}, 'UserModel::saveMulti');
}
}

View File

@@ -1,5 +1,5 @@
import config from '../../config';
import { createTestUsers } from '../../tools/debugTools';
import { clearDatabase, createTestUsers, CreateTestUsersOptions } from '../../tools/debugTools';
import { bodyFields } from '../../utils/requestUtils';
import Router from '../../utils/Router';
import { RouteType } from '../../utils/types';
@@ -12,6 +12,8 @@ router.public = true;
interface Query {
action: string;
count?: number;
fromNum?: number;
}
router.post('api/debug', async (_path: SubPath, ctx: AppContext) => {
@@ -20,7 +22,16 @@ router.post('api/debug', async (_path: SubPath, ctx: AppContext) => {
console.info(`Action: ${query.action}`);
if (query.action === 'createTestUsers') {
await createTestUsers(ctx.joplin.db, config());
const options: CreateTestUsersOptions = {};
if ('count' in query) options.count = query.count;
if ('fromNum' in query) options.fromNum = query.fromNum;
await createTestUsers(ctx.joplin.db, config(), options);
}
if (query.action === 'clearDatabase') {
await clearDatabase(ctx.joplin.db);
}
});

View File

@@ -3,7 +3,7 @@ import Router from '../../utils/Router';
import { RouteType } from '../../utils/types';
import { ErrorForbidden } from '../../utils/errors';
import { AppContext } from '../../utils/types';
import { bodyFields } from '../../utils/requestUtils';
import { bodyFields, userIp } from '../../utils/requestUtils';
import { User } from '../../services/database/types';
import limiterLoginBruteForce from '../../utils/request/limiterLoginBruteForce';
@@ -12,7 +12,7 @@ const router = new Router(RouteType.Api);
router.public = true;
router.post('api/sessions', async (_path: SubPath, ctx: AppContext) => {
await limiterLoginBruteForce(ctx.ip);
await limiterLoginBruteForce(userIp(ctx));
const fields: User = await bodyFields(ctx.req);
const user = await ctx.joplin.models.user().login(fields.email, fields.password);

View File

@@ -2,7 +2,7 @@ import { SubPath, redirect, makeUrl, UrlType } from '../../utils/routeUtils';
import Router from '../../utils/Router';
import { RouteType } from '../../utils/types';
import { AppContext } from '../../utils/types';
import { formParse } from '../../utils/requestUtils';
import { formParse, userIp } from '../../utils/requestUtils';
import config from '../../config';
import defaultView from '../../utils/defaultView';
import { View } from '../../services/MustacheService';
@@ -27,7 +27,7 @@ router.get('login', async (_path: SubPath, _ctx: AppContext) => {
});
router.post('login', async (_path: SubPath, ctx: AppContext) => {
await limiterLoginBruteForce(ctx.ip);
await limiterLoginBruteForce(userIp(ctx));
try {
const body = await formParse(ctx.req);

View File

@@ -137,6 +137,14 @@ export const postHandlers: PostHandlers = {
//
// - The public config is under packages/server/stripeConfig.json
// - The private config is in the server .env file
//
// # Failed Stripe cli login
//
// If the tool show this error, with code "api_key_expired":
//
// > FATAL Error while authenticating with Stripe: Authorization failed
//
// Need to logout and login again to refresh the CLI token - `stripe logout && stripe login`
webhook: async (stripe: Stripe, _path: SubPath, ctx: AppContext, event: Stripe.Event = null, logErrors: boolean = true) => {
event = event ? event : await stripeEvent(stripe, ctx.req);
@@ -426,7 +434,7 @@ const getHandlers: Record<string, StripeRouteHandler> = {
<body>
<button id="checkout">Subscribe</button>
<script>
var PRICE_ID = ${basicPrice.id};
var PRICE_ID = ${JSON.stringify(basicPrice.id)};
function handleResult() {
console.info('Redirected to checkout');

View File

@@ -15,7 +15,7 @@ import uuidgen from '../../utils/uuidgen';
import { formatMaxItemSize, formatMaxTotalSize, formatTotalSize, formatTotalSizePercent, yesOrNo } from '../../utils/strings';
import { getCanShareFolder, totalSizeClass } from '../../models/utils/user';
import { yesNoDefaultOptions, yesNoOptions } from '../../utils/views/select';
import { confirmUrl } from '../../utils/urlUtils';
import { confirmUrl, stripePortalUrl } from '../../utils/urlUtils';
import { cancelSubscriptionByUserId, updateSubscriptionType } from '../../utils/stripe';
import { createCsrfTag } from '../../utils/csrf';
import { formatDateTime } from '../../utils/time';
@@ -175,6 +175,7 @@ router.get('users/:id', async (path: SubPath, ctx: AppContext, user: User = null
view.content.canShareFolderOptions = yesNoDefaultOptions(user, 'can_share_folder');
view.content.canUploadOptions = yesNoOptions(user, 'can_upload');
view.content.userFlags = userFlags;
view.content.stripePortalUrl = stripePortalUrl();
view.jsFiles.push('zxcvbn');
view.cssFiles.push('index/user');

View File

@@ -1,9 +1,14 @@
import { DbConnection, dropTables, migrateLatest } from '../db';
import newModelFactory from '../models/factory';
import { AccountType } from '../models/UserModel';
import { UserFlagType } from '../services/database/types';
import { User, UserFlagType } from '../services/database/types';
import { Config } from '../utils/types';
export interface CreateTestUsersOptions {
count?: number;
fromNum?: number;
}
export async function handleDebugCommands(argv: any, db: DbConnection, config: Config): Promise<boolean> {
if (argv.debugCreateTestUsers) {
await createTestUsers(db, config);
@@ -14,51 +19,79 @@ export async function handleDebugCommands(argv: any, db: DbConnection, config: C
return true;
}
export async function createTestUsers(db: DbConnection, config: Config) {
export async function clearDatabase(db: DbConnection) {
await dropTables(db);
await migrateLatest(db);
}
export async function createTestUsers(db: DbConnection, config: Config, options: CreateTestUsersOptions = null) {
options = {
count: 0,
fromNum: 1,
...options,
};
const password = 'hunter1hunter2hunter3';
const models = newModelFactory(db, config);
for (let userNum = 1; userNum <= 2; userNum++) {
await models.user().save({
email: `user${userNum}@example.com`,
password,
full_name: `User ${userNum}`,
});
}
if (options.count) {
const models = newModelFactory(db, config);
{
const { user } = await models.subscription().saveUserAndSubscription(
'usersub@example.com',
'With Sub',
AccountType.Basic,
'usr_111',
'sub_111'
);
await models.user().save({ id: user.id, password });
}
const users: User[] = [];
{
const { user, subscription } = await models.subscription().saveUserAndSubscription(
'userfailedpayment@example.com',
'Failed Payment',
AccountType.Basic,
'usr_222',
'sub_222'
);
await models.user().save({ id: user.id, password });
await models.subscription().handlePayment(subscription.stripe_subscription_id, false);
}
for (let i = 0; i < options.count; i++) {
const userNum = i + options.fromNum;
users.push({
email: `user${userNum}@example.com`,
password,
full_name: `User ${userNum}`,
});
}
{
const user = await models.user().save({
email: 'userwithflags@example.com',
password,
full_name: 'User Withflags',
});
await models.user().saveMulti(users);
} else {
await dropTables(db);
await migrateLatest(db);
const models = newModelFactory(db, config);
await models.userFlag().add(user.id, UserFlagType.AccountOverLimit);
for (let userNum = 1; userNum <= 2; userNum++) {
await models.user().save({
email: `user${userNum}@example.com`,
password,
full_name: `User ${userNum}`,
});
}
{
const { user } = await models.subscription().saveUserAndSubscription(
'usersub@example.com',
'With Sub',
AccountType.Basic,
'usr_111',
'sub_111'
);
await models.user().save({ id: user.id, password });
}
{
const { user, subscription } = await models.subscription().saveUserAndSubscription(
'userfailedpayment@example.com',
'Failed Payment',
AccountType.Basic,
'usr_222',
'sub_222'
);
await models.user().save({ id: user.id, password });
await models.subscription().handlePayment(subscription.stripe_subscription_id, false);
}
{
const user = await models.user().save({
email: 'userwithflags@example.com',
password,
full_name: 'User Withflags',
});
await models.userFlag().add(user.id, UserFlagType.AccountOverLimit);
}
}
}

View File

@@ -1,6 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
interface TransactionInfo {
name: string;
index: number;
timestamp: Date;
}
// This transaction handler allows abstracting away the complexity of managing nested transactions
// within models.
// Any method in a model can start a transaction and, if one is already started, it
@@ -9,7 +15,7 @@ import { DbConnection } from '../db';
// Set logEnabled_ to `true` to see what happens with nested transactions.
export default class TransactionHandler {
private transactionStack_: number[] = [];
private transactionStack_: TransactionInfo[] = [];
private activeTransaction_: Knex.Transaction = null;
private transactionIndex_: number = 0;
private logEnabled_: boolean = false;
@@ -36,7 +42,15 @@ export default class TransactionHandler {
return this.activeTransaction_;
}
public async start(): Promise<number> {
public get stackInfo(): string {
const output: string[] = [];
for (const t of this.transactionStack_) {
output.push(`#${t.index}: ${t.name}: ${t.timestamp.toUTCString()}`);
}
return output.join('\n');
}
public async start(name: string): Promise<number> {
const txIndex = ++this.transactionIndex_;
this.log(`Starting transaction: ${txIndex}`);
@@ -47,14 +61,19 @@ export default class TransactionHandler {
this.log(`Got transaction: ${txIndex}`);
}
this.transactionStack_.push(txIndex);
this.transactionStack_.push({
name,
index: txIndex,
timestamp: new Date(),
});
return txIndex;
}
private finishTransaction(txIndex: number): boolean {
if (!this.transactionStack_.length) throw new Error('Committing but no transaction was started');
const lastTxIndex = this.transactionStack_.pop();
if (lastTxIndex !== txIndex) throw new Error(`Committing a transaction but was not last to start one: ${txIndex}. Expected: ${lastTxIndex}`);
const lastTx = this.transactionStack_.pop();
if (lastTx.index !== txIndex) throw new Error(`Committing a transaction but was not last to start one: ${txIndex}. Expected: ${lastTx.index}`);
return !this.transactionStack_.length;
}

View File

@@ -70,3 +70,8 @@ export function contextSessionId(ctx: AppContext, throwIfNotFound = true): strin
export function isApiRequest(ctx: AppContext): boolean {
return ctx.path.indexOf('/api/') === 0;
}
export function userIp(ctx: AppContext): string {
if (ctx.headers['x-real-ip']) return ctx.headers['x-real-ip'];
return ctx.ip;
}

View File

@@ -50,6 +50,7 @@ export interface AppContext extends Koa.Context {
}
export enum DatabaseConfigClient {
Null = 'null',
PostgreSQL = 'pg',
SQLite = 'sqlite3',
}
@@ -64,6 +65,8 @@ export interface DatabaseConfig {
user?: string;
password?: string;
asyncStackTraces?: boolean;
slowQueryLogEnabled?: boolean;
slowQueryLogMinDuration?: number;
}
export interface MailerConfig {

View File

@@ -4,16 +4,20 @@
Most of your details can be found in your Profile page. To open it, click on the Profile button - this is the button in the top right corner, with your name or email on it.
## How can I cancel my account?
Click on the [Profile button](#how-can-i-change-my-details), then scroll down and click on "Cancel subscription".
## How can I get more space?
If you are on a Basic account, you may upgrade to a Pro account to get more space. Click on the [Profile button](#how-can-i-change-my-details), then scroll down and select "Upgrade account".
If you are already on a Pro account, and you need more space for specific reasons, please contact us as we may increase the cap in some cases.
## How can I manage my payment details?
To update your card or other payment details, click on the [Profile button](#how-can-i-change-my-details), then scroll down and click on "Manage payment details".
## How can I cancel my account?
Click on the [Profile button](#how-can-i-change-my-details), then scroll down and click on "Cancel subscription".
## Further information
- [Joplin Offical Website](https://joplinapp.org)

View File

@@ -131,6 +131,9 @@
{{#showUpdateSubscriptionPro}}
<a href="{{{global.baseUrl}}}/upgrade" class="button is-warning block">Upgrade to Pro</a>
{{/showUpdateSubscriptionPro}}
{{#showCancelSubscription}}
<p class="block"><a href="{{stripePortalUrl}}">Manage payment details</a></p>
{{/showCancelSubscription}}
{{#showCancelSubscription}}
<p id="user_cancel_subscription_link" class="block"><a href="#">Cancel subscription</a></p>
<input type="submit" id="user_cancel_subscription_button" name="user_cancel_subscription_button" class="button is-danger" value="Cancel subscription" />

5
packages/style.min.css vendored Normal file
View File

@@ -0,0 +1,5 @@
.encryption-config-test > .item {
font-weight: bold;
}
/*# sourceMappingURL=style.min.css.map */

View File

@@ -1,8 +1,6 @@
import { execCommand2, rootDir } from './tool-utils';
import * as moment from 'moment';
const DockerImageName = 'joplin/server';
function getVersionFromTag(tagName: string, isPreRelease: boolean): string {
if (tagName.indexOf('server-') !== 0) throw new Error(`Invalid tag: ${tagName}`);
const s = tagName.split('-');
@@ -14,10 +12,6 @@ function getIsPreRelease(tagName: string): boolean {
return tagName.indexOf('-beta') > 0;
}
function normalizePlatform(platform: string) {
return platform.replace(/\//g, '-');
}
async function main() {
const argv = require('yargs').argv;
if (!argv.tagName) throw new Error('--tag-name not provided');
@@ -33,11 +27,7 @@ async function main() {
} catch (error) {
console.info('Could not get git commit: metadata revision field will be empty');
}
const buildArgs = [
`--build-arg BUILD_DATE="${buildDate}"`,
`--build-arg REVISION="${revision}"`,
`--build-arg VERSION="${imageVersion}"`,
];
const buildArgs = `--build-arg BUILD_DATE="${buildDate}" --build-arg REVISION="${revision}" --build-arg VERSION="${imageVersion}"`;
const dockerTags: string[] = [];
const versionPart = imageVersion.split('.');
dockerTags.push(isPreRelease ? 'beta' : 'latest');
@@ -54,48 +44,10 @@ async function main() {
console.info('isPreRelease:', isPreRelease);
console.info('Docker tags:', dockerTags.join(', '));
const platforms = [
'linux/amd64',
'linux/arm64',
'linux/arm/v7',
];
// this will build a bunch of local image tags named: ${imageVersion}-${platform} with the slashes replaced with dashes
for (const platform of platforms) {
const normalizedPlatform = normalizePlatform(platform);
await execCommand2([
'docker', 'build',
'--platform', platform,
'-t', `${DockerImageName}:${imageVersion}-${normalizedPlatform}`,
...buildArgs,
'-f', 'Dockerfile.server',
'.',
]);
if (pushImages) {
await execCommand2([
'docker', 'push', `${DockerImageName}:${imageVersion}-${normalizedPlatform}`,
]);
}
}
// now we have to create the right manifests and push them
if (pushImages) {
for (const tag of dockerTags) {
// manifest create requires the tags being amended in to exist on the remote, so this all can only happen if pushImages is true
const platformArgs: string[] = [];
for (const platform in platforms) {
platformArgs.concat('--amend', `${DockerImageName}:${imageVersion}-${normalizePlatform(platform)}`);
}
await execCommand2([
'docker', 'manifest', 'create',
`${DockerImageName}:${tag}`,
...platformArgs,
]);
await execCommand2([
'docker', 'manifest', 'push',
`${DockerImageName}:${tag}`,
]);
}
await execCommand2(`docker build -t "joplin/server:${imageVersion}" ${buildArgs} -f Dockerfile.server .`);
for (const tag of dockerTags) {
await execCommand2(`docker tag "joplin/server:${imageVersion}" "joplin/server:${tag}"`);
if (pushImages) await execCommand2(`docker push joplin/server:${tag}`);
}
}

View File

@@ -0,0 +1,45 @@
const sass = require('sass');
const fs = require('fs-extra');
// The SASS doc claims that renderSync is twice as fast as render, so if speed
// turns out to be an issue we could use that instead. The advantage of async is
// that we can run complation of each file in parallel (and running other async
// gulp tasks in parallel too).
// sasss.render is old school async, so convert it to a promise here.
async function sassRender(options) {
return new Promise((resolve, reject) => {
sass.render(options, ((error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
}));
});
}
module.exports = async function compileSass(inputPaths, outputPath) {
const promises = [];
for (const inputPath of inputPaths) {
console.info(`Compiling ${inputPath}...`);
promises.push(sassRender({
file: inputPath,
sourceMap: true,
outFile: outputPath,
}));
}
const results = await Promise.all(promises);
const cssString = results.map(r => r.css.toString()).join('\n');
const mapString = results.map(r => r.map.toString()).join('\n');
await Promise.all([
fs.writeFile(outputPath, cssString, 'utf8'),
fs.writeFile(`${outputPath}.map`, mapString, 'utf8'),
]);
console.info(`Generated ${outputPath}`);
};

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 12.09.2021\n"
"Last-Translator: mrkaato\n"
"Language-Team: \n"
"Language: fi_FI\n"
@@ -14,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.4.1\n"
"X-Generator: Poedit 3.0\n"
#: packages/app-cli/app/app-gui.js:452
msgid "To delete a tag, untag the associated notes."
@@ -133,6 +135,7 @@ msgid ""
"Runs the commands contained in the text file. There should be one command "
"per line."
msgstr ""
"Suorittaa tekstitiedoston komennot. Riviä kohden pitäisi olla yksi komento."
#: packages/app-cli/app/command-cat.js:14
msgid "Displays the given note."
@@ -599,8 +602,8 @@ msgid ""
"\n"
"%s"
msgstr ""
"Sets the property <name> of the given <note> to the given [value]. Possible "
"properties are:\n"
"Asettaa ominaisuuden <name> annetusta <note> annettuun [arvoon]. Mahdolliset "
"ominaisuudet ovat:\n"
"\n"
"%s"
@@ -647,7 +650,7 @@ msgstr ""
#: packages/app-desktop/gui/DropboxLoginScreen.js:30
#: packages/app-mobile/components/screens/dropbox-login.js:59
msgid "Step 1: Open this URL in your browser to authorise the application:"
msgstr "Vaihe 1: Avaa tämä URL-osoite selaimessa valtuuttaaksesi sovelluksen:"
msgstr "Vaihe 1: Avaa tämä URL osoite selaimessa valtuuttaaksesi sovelluksen:"
#: packages/app-cli/app/command-sync.js:94
#: packages/app-desktop/gui/DropboxLoginScreen.js:32
@@ -908,11 +911,11 @@ msgstr "Lataa"
#: packages/app-desktop/checkForUpdates.js:189
msgid "Skip this version"
msgstr ""
msgstr "Ohita tämä versio"
#: packages/app-desktop/checkForUpdates.js:189
msgid "Full changelog"
msgstr ""
msgstr "Täysi muutosloki"
#: packages/app-desktop/commands/copyDevCommand.js:18
msgid "Copy dev mode command to clipboard"
@@ -939,9 +942,8 @@ msgid "Stop"
msgstr "Seis"
#: packages/app-desktop/commands/toggleSafeMode.js:18
#, fuzzy
msgid "Toggle safe mode"
msgstr "Näytä sivupalkki"
msgstr "Vaihda turvalliseen tilaan"
#: packages/app-desktop/gui/ClipperConfigScreen.js:34
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:40
@@ -951,7 +953,7 @@ msgstr "Tunnus on kopioitu leikepöydälle!"
#: packages/app-desktop/gui/ClipperConfigScreen.js:37
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:44
msgid "Are you sure you want to renew the authorisation token?"
msgstr ""
msgstr "Haluatko varmasti uusia valtuutustunnuksen?"
#: packages/app-desktop/gui/ClipperConfigScreen.js:67
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:84
@@ -1053,7 +1055,7 @@ msgstr ""
#: packages/app-desktop/gui/ClipperConfigScreen.js:111
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:222
msgid "Renew token"
msgstr ""
msgstr "Uusi tunnus"
#: packages/app-desktop/gui/ConfigScreen/ButtonBar.js:27
msgid "Apply"
@@ -1179,13 +1181,12 @@ msgid "You do not have any installed plugin."
msgstr "Sinulla ei ole asennettua laajennusta."
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js:232
#, fuzzy
msgid "Could not connect to plugin repository"
msgstr "Laajennuksen asentaminen epäonnistui: %s"
msgstr "Yhteyden muodostaminen laajennusten arkistoon epäonnistui"
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js:234
msgid "Try again"
msgstr ""
msgstr "Yritä uudestaan"
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js:242
msgid "Plugin tools"
@@ -1219,19 +1220,16 @@ msgid "Submit"
msgstr "Lähetä"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:73
#, fuzzy
msgid "Source: "
msgstr "Lähde"
msgstr "Lähde: "
#: packages/app-desktop/gui/EncryptionConfigScreen.js:76
#, fuzzy
msgid "Created: "
msgstr "Luotu: %s"
msgstr "Luotu: "
#: packages/app-desktop/gui/EncryptionConfigScreen.js:79
#, fuzzy
msgid "Updated: "
msgstr "Päivitetty: %s"
msgstr "Päivitetty: "
#: packages/app-desktop/gui/EncryptionConfigScreen.js:84
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:96
@@ -1241,9 +1239,8 @@ msgid "Save"
msgstr "Tallenna"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:87
#, fuzzy
msgid "Disable"
msgstr "Poistettu käytöstä"
msgstr "Poista käytöstä"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:87
#: packages/app-mobile/components/screens/encryption-config.js:157
@@ -1355,11 +1352,11 @@ msgstr "Pääavaimet"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:142
msgid "Hide disabled master keys"
msgstr ""
msgstr "Piilota käytöstä poistetut pääavaimet"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:142
msgid "Show disabled master keys"
msgstr ""
msgstr "Näytä käytöstä poistetut pääavaimet"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:143
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:343
@@ -1380,7 +1377,7 @@ msgstr "Aktiivinen"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:149
msgid "Date"
msgstr ""
msgstr "Päivämäärä"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:150
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:329
@@ -1388,14 +1385,12 @@ msgid "Password"
msgstr "Salasana"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:151
#, fuzzy
msgid "Valid"
msgstr "Virheellinen"
msgstr "Kelvollinen"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:152
#, fuzzy
msgid "Actions"
msgstr "Toiminta"
msgstr "Toiminnot"
#: packages/app-desktop/gui/EncryptionConfigScreen.js:182
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:244
@@ -1575,12 +1570,12 @@ msgstr "Sulje ikkuna"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:24
msgid "Preferences"
msgstr "Asetukset"
msgstr "Oletusasetukset"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:24
#: packages/app-desktop/gui/MenuBar.js:310 packages/app-desktop/gui/Root.js:163
msgid "Options"
msgstr "Vaihtoehdot"
msgstr "Asetukset"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:31
msgid "Invalid"
@@ -1591,10 +1586,12 @@ msgid ""
"Safe mode is currently active. Note rendering and all plugins are "
"temporarily disabled."
msgstr ""
"Turvallinen tila on tällä hetkellä aktiivinen. Huomautusten renderöinti ja "
"kaikki laajennukset on tilapäisesti poistettu käytöstä."
#: packages/app-desktop/gui/MainScreen/MainScreen.js:470
msgid "Disable safe mode and restart"
msgstr ""
msgstr "Lopeta turvallinen tila ja käynnistä uudelleen"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:473
msgid ""
@@ -1638,16 +1635,16 @@ msgstr "Lisätietoja"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:487
#, javascript-format
msgid "%s (%s) would like to share a notebook with you."
msgstr ""
msgstr "%s (%s) haluaa jakaa muistikirjan kanssasi."
#: packages/app-desktop/gui/MainScreen/MainScreen.js:487
msgid "Accept"
msgstr ""
msgstr "Hyväksy"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:487
#: packages/app-desktop/gui/Root.js:121
msgid "Reject"
msgstr ""
msgstr "Hylkää"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:490
msgid "Some items cannot be synchronised."
@@ -1666,9 +1663,8 @@ msgid "Use the arrows to move the layout items. Press \"Escape\" to exit."
msgstr "Siirrä asettelukohteita nuolilla. Paina \"Esc\" poistuaksesi."
#: packages/app-desktop/gui/MainScreen/commands/commandPalette.js:18
#, fuzzy
msgid "Command palette..."
msgstr "Komentovalikoima"
msgstr "Komentovalikoima..."
#: packages/app-desktop/gui/MainScreen/commands/editAlarm.js:20
#: packages/app-mobile/components/SelectDateTimeDialog.js:84
@@ -1683,7 +1679,7 @@ msgstr "Aseta hälytys:"
#: packages/app-desktop/gui/MainScreen/commands/exportPdf.js:20
#: packages/app-desktop/gui/MainScreen/commands/exportPdf.js:32
msgid "PDF File"
msgstr "PDF-tiedosto"
msgstr "PDF tiedosto"
#: packages/app-desktop/gui/MainScreen/commands/gotoAnything.js:23
#: packages/app-desktop/plugins/GotoAnything.js:497
@@ -1766,14 +1762,12 @@ msgid "Note properties"
msgstr "Muistiinpanon ominaisuudet"
#: packages/app-desktop/gui/MainScreen/commands/showShareFolderDialog.js:16
#, fuzzy
msgid "Share notebook..."
msgstr "Muistiinpanon jakaminen..."
msgstr "Muistikirjan jakaminen..."
#: packages/app-desktop/gui/MainScreen/commands/showShareNoteDialog.js:16
#, fuzzy
msgid "Publish note..."
msgstr "Muistiinpanon jakaminen..."
msgstr "Muistiinpanon julkaiseminen..."
#: packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js:19
#: packages/lib/services/spellChecker/SpellCheckerService.js:180
@@ -1833,7 +1827,7 @@ msgstr "Joplinista"
#: packages/app-desktop/gui/MenuBar.js:367
msgid "Preferences..."
msgstr "Asetukset..."
msgstr "Oletusasetukset..."
#: packages/app-desktop/gui/MenuBar.js:377
#: packages/app-desktop/gui/MenuBar.js:618
@@ -1861,7 +1855,7 @@ msgstr "&Näytä"
#: packages/app-desktop/gui/MenuBar.js:506
msgid "Layout button sequence"
msgstr "Asettele painike järjestys"
msgstr "Aseta painike järjestys"
#: packages/app-desktop/gui/MenuBar.js:551
#: packages/app-desktop/gui/MenuBar.js:557
@@ -1882,12 +1876,11 @@ msgstr "&Mene"
#: packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js:18
#: packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js:18
msgid "Focus"
msgstr "Keskittää"
msgstr "Osoita"
#: packages/app-desktop/gui/MenuBar.js:585
#, fuzzy
msgid "Note&book"
msgstr "Muistikirjat"
msgstr "&Muistikirjat"
#: packages/app-desktop/gui/MenuBar.js:591
msgid "&Note"
@@ -2049,23 +2042,23 @@ msgstr "Muokkaa"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:17
msgid "Highlight"
msgstr ""
msgstr "Korosta"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:22
msgid "Strikethrough"
msgstr ""
msgstr "Yliviivaus"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:27
msgid "Insert"
msgstr ""
msgstr "Aseta"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:33
msgid "Superscript"
msgstr ""
msgstr "Yläindeksi"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:39
msgid "Subscript"
msgstr ""
msgstr "Alaindeksi"
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:285
msgid "Click to add tags..."
@@ -2162,9 +2155,8 @@ msgid "Delete line"
msgstr "Poista rivi"
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:92
#, fuzzy
msgid "Duplicate line"
msgstr "Duplikaatti, toinen samanlainen"
msgstr "Rivin kaksoiskappale"
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:96
msgid "Undo"
@@ -2372,18 +2364,16 @@ msgstr ""
"Varoitus: kaikkia resursseja ei näytetä suorituskyvyn vuoksi (raja: %s)."
#: packages/app-desktop/gui/Root.js:106
#, fuzzy
msgid "Confirmation"
msgstr "Konfigurointi"
msgstr "Vahvistus"
#: packages/app-desktop/gui/Root.js:119
msgid "The Web Clipper needs your authorisation to access your data."
msgstr ""
msgstr "Web Clipper tarvitsee valtuutuksen tietojen käyttöön."
#: packages/app-desktop/gui/Root.js:120
#, fuzzy
msgid "Grant authorisation"
msgstr "Valtuutuksen tunnus:"
msgstr "Myönnä lupa"
#: packages/app-desktop/gui/Root.js:160
msgid "OneDrive Login"
@@ -2398,19 +2388,20 @@ msgid "Note attachments"
msgstr "Muistiinpanon liitteet"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:141
#, fuzzy
msgid "Unshare"
msgstr "Jaa"
msgstr "Poista jakaminen"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:183
msgid ""
"Delete this invitation? The recipient will no longer have access to this "
"shared notebook."
msgstr ""
"Poistetaanko tämä kutsu? Vastaanottaja ei voi enää käyttää tätä jaettua "
"muistikirjaa."
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:197
msgid "Add recipient:"
msgstr ""
msgstr "Lisää vastaanottaja:"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:200
#: packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
@@ -2420,45 +2411,43 @@ msgstr "Jaa"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:209
msgid "Recipient has not yet accepted the invitation"
msgstr ""
msgstr "Vastaanottaja ei ole vielä hyväksynyt kutsua"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:210
msgid "Recipient has rejected the invitation"
msgstr ""
msgstr "Vastaanottaja on hylännyt kutsun"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:211
msgid "Recipient has accepted the invitation"
msgstr ""
msgstr "Vastaanottaja on hyväksynyt kutsun"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:221
msgid "Recipients:"
msgstr ""
msgstr "Vastaanottajat:"
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:233
#, fuzzy
msgid "Synchronizing..."
msgstr "Synkronoidaan..."
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:234
#, fuzzy
msgid "Sharing notebook..."
msgstr "Muistiinpanon jakaminen..."
msgstr "Jaetaan muistikirjaa..."
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:244
msgid ""
"Unshare this notebook? The recipients will no longer have access to its "
"content."
msgstr ""
"Poistetaanko tämän muistikirjan jakaminen? Vastaanottajilla ei ole enää "
"pääsyä sen sisältöön."
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:254
#, fuzzy
msgid "Share Notebook"
msgstr "Jaa muistiinpanoja"
msgstr "Jaa muistikirja"
#: packages/app-desktop/gui/ShareNoteDialog.js:144
#, fuzzy
msgid "Unpublish note"
msgstr "Jaa"
msgstr "Peruuta muistiinpanon julkaisu"
#: packages/app-desktop/gui/ShareNoteDialog.js:171
msgid "Synchronising..."
@@ -2484,7 +2473,7 @@ msgstr ""
#: packages/app-desktop/gui/ShareNoteDialog.js:187
msgid "Publish Notes"
msgstr ""
msgstr "Julkaise muistiinpanot"
#: packages/app-desktop/gui/ShareNoteDialog.js:189
msgid "Copy Shareable Link"
@@ -2565,32 +2554,28 @@ msgid "Retry"
msgstr "Yritä uudelleen"
#: packages/app-desktop/gui/StatusScreen/StatusScreen.js:137
#, fuzzy
msgid "Advanced tools"
msgstr "Lisäasetukset"
msgstr "Lisätyökalut"
#: packages/app-desktop/gui/StatusScreen/StatusScreen.js:139
#, fuzzy
msgid "Export debug report"
msgstr "Vie virheenkorjausraportti"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:157
#, fuzzy
msgid "Sync your notes"
msgstr "Lajittele muistiinpanot"
msgstr "Synkronoi muistiinpanosi"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:158
msgid "Publish notes to the internet"
msgstr ""
msgstr "Julkaise muistiinpanot Internetissä"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:159
#, fuzzy
msgid "Collaborate on notebooks with others"
msgstr "Luo ensin muistikirja"
msgstr "Muistikirjojen yhteiskäyttö muiden kanssa"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:182
msgid "Thank you! Your Joplin Cloud account is now setup and ready to use."
msgstr ""
msgstr "Kiitos! Your Joplin Cloud tilisi on nyt määritetty ja käyttövalmis."
#: packages/app-desktop/gui/SyncWizard/Dialog.js:190
#, javascript-format
@@ -2600,30 +2585,34 @@ msgid ""
"\n"
"%s"
msgstr ""
"Joplin Cloud tilisi määrittämisessä tapahtui virhe. Vahvista "
"sähköpostiosoitteesi ja salasanasi sekä yritä uudelleen. Virhe oli:\n"
"\n"
"%s"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:203
msgid "Login below."
msgstr ""
msgstr "Kirjaudu sisään alla."
#: packages/app-desktop/gui/SyncWizard/Dialog.js:205
#, fuzzy
msgid "Or create an account."
msgstr "Luo uuden muistiinpanon."
msgstr "Tai luo tili."
#: packages/app-desktop/gui/SyncWizard/Dialog.js:210
msgid "Login"
msgstr ""
msgstr "Kirjaudu"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:231
#, fuzzy
msgid "Select"
msgstr "Valitse kaikki"
msgstr "Valitse"
#: packages/app-desktop/gui/SyncWizard/Dialog.js:278
msgid ""
"Joplin can synchronise your notes using various providers. Select one from "
"the list below."
msgstr ""
"Joplin voi synkronoida muistiinpanosi eri palveluntarjoajien avulla. Valitse "
"yksi alla olevasta luettelosta."
#: packages/app-desktop/gui/utils/NoteListUtils.js:43
msgid "Duplicate"
@@ -2644,7 +2633,7 @@ msgstr "Vaihda muistiinpanotyyppiin"
#: packages/app-desktop/gui/utils/NoteListUtils.js:93
msgid "Switch to to-do type"
msgstr "Siirry tehtävätyyppiin"
msgstr "Vaihda tehtävätyyppiin"
#: packages/app-desktop/gui/utils/NoteListUtils.js:100
#: packages/app-mobile/components/screens/Note.js:847
@@ -2797,7 +2786,7 @@ msgstr "Vain virheenkorjausta varten: vie profiilisi ulkoiselle SD-kortille."
#: packages/app-mobile/components/screens/ConfigScreen.js:436
msgid "Feature flags"
msgstr ""
msgstr "Ominaisuus liput"
#: packages/app-mobile/components/screens/ConfigScreen.js:439
msgid "More information"
@@ -2809,8 +2798,8 @@ msgid ""
"them in your phone settings, in Apps > Joplin > Permissions"
msgstr ""
"Toimiakseen oikein sovellus tarvitsee seuraavat käyttöoikeudet. Ota ne "
"käyttöön puhelimesi asetuksissa, valitsemalla Sovellukset> Joplin> "
"Käyttöoikeudet"
"käyttöön puhelimesi asetuksissa, valitsemalla Sovellukset> Joplin> Luvat "
"(käyttöoikeudet)"
#: packages/app-mobile/components/screens/ConfigScreen.js:446
msgid ""
@@ -2822,11 +2811,11 @@ msgstr ""
#: packages/app-mobile/components/screens/ConfigScreen.js:447
msgid "- Camera: to allow taking a picture and attaching it to a note."
msgstr "- Kamera: sallii kuvan ottamisen ja liittämisen muistiinpanoon."
msgstr "- Kamera: sallia kuvan ottamisen ja liittämisen muistiinpanoon."
#: packages/app-mobile/components/screens/ConfigScreen.js:448
msgid "- Location: to allow attaching geo-location information to a note."
msgstr "- Sijainti: sallii paikkatietojen liittämisen muistiinpanoon."
msgstr "- Sijainti: sallia paikkatietojen liittämisen muistiinpanoon."
#: packages/app-mobile/components/screens/ConfigScreen.js:459
msgid "Joplin website"
@@ -2931,7 +2920,7 @@ msgstr "Näytä kartalla"
#: packages/app-mobile/components/screens/Note.js:761
msgid "Go to source URL"
msgstr "Siirry lähteen URL-osoitteeseen"
msgstr "Siirry lähteen URL osoitteeseen"
#: packages/app-mobile/components/screens/Note.js:790
msgid "Attach..."
@@ -3048,7 +3037,7 @@ msgstr "Uusi muistikirja"
#: packages/app-mobile/components/side-menu-content.js:351
msgid "Mobile data - auto-sync disabled"
msgstr ""
msgstr "Mobiilidata - automaattinen synkronointi poistettu käytöstä"
#: packages/lib/BaseApplication.js:154 packages/lib/BaseApplication.js:166
#: packages/lib/BaseApplication.js:198
@@ -3070,7 +3059,7 @@ msgid ""
"%s"
msgstr ""
"Yhteyden muodostaminen Joplinin palvelimelle epäonnistui. Tarkista "
"vaihtoehdot Synkronointiasetukset-näytössä. Koko virhe oli:\n"
"synkronoinnin asetukset. Koko virhe oli:\n"
"\n"
"%s"
@@ -3087,15 +3076,17 @@ msgid "File system"
msgstr "Tiedostojärjestelmä"
#: packages/lib/SyncTargetJoplinCloud.js:28
#, fuzzy
msgid "Joplin Cloud"
msgstr "Joplin Foorumi"
msgstr "Joplin Cloud"
#: packages/lib/SyncTargetJoplinCloud.js:31
msgid ""
"Joplin's own sync service. Also gives access to Joplin-specific features "
"such as publishing notes or collaborating on notebooks with others."
msgstr ""
"Joplinin oma synkronointipalvelu. Voit myös käyttää Joplin "
"erityisominaisuuksia, kuten muistiinpanojen julkaisemista tai muistikirjojen "
"yhteiskäyttöä muiden kanssa."
#: packages/lib/SyncTargetJoplinServer.js:60
msgid "Joplin Server"
@@ -3107,7 +3098,7 @@ msgstr "Nextcloud"
#: packages/lib/SyncTargetNone.js:22
msgid "(None)"
msgstr ""
msgstr "(Ei mitään)"
#: packages/lib/SyncTargetOneDrive.js:32
msgid "OneDrive"
@@ -3157,9 +3148,9 @@ msgid "Cancelling..."
msgstr "Peruutetaan..."
#: packages/lib/Synchronizer.js:159
#, fuzzy, javascript-format
#, javascript-format
msgid "Completed: %s (%s)"
msgstr "Valmis: %s"
msgstr "Valmis: %s (%s)"
#: packages/lib/Synchronizer.js:161
#, javascript-format
@@ -3196,8 +3187,8 @@ msgid ""
"Error. Please check that URL, username, password, etc. are correct and that "
"the sync target is accessible. The reported error was:"
msgstr ""
"Virhe. Tarkista URL-osoite, käyttäjänimi, salasana jne. ovat oikeita ja "
"synkronointikohde on käytettävissä. Raportoitu virhe oli:"
"Virhe. Tarkista URL osoite, käyttäjänimi, salasana jne. ovat oikeita ja "
"synkronointi kohde on käytettävissä. Raportoitu virhe oli:"
#: packages/lib/components/shared/dropbox-login-shared.js:39
msgid "The application has been authorised!"
@@ -3252,7 +3243,7 @@ msgstr "Purettuja kohteita: %s / %s"
#: packages/lib/components/shared/encryption-config-shared.js:151
#, javascript-format
msgid "Encryption will be enabled using the master key created on %s"
msgstr ""
msgstr "Salaus otetaan käyttöön käyttämällä luotua pääavainta %s"
#: packages/lib/models/BaseItem.js:721
msgid "Encrypted"
@@ -3322,9 +3313,8 @@ msgid "Error"
msgstr "Virhe"
#: packages/lib/models/Resource.js:408
#, fuzzy
msgid "Conflicts (attachments)"
msgstr "Muistiinpanon liitteet"
msgstr "Ristiriidat (liitteet)"
#: packages/lib/models/Resource.js:422
#, javascript-format
@@ -3400,7 +3390,7 @@ msgstr "OLED Dark"
#: packages/lib/models/Setting.js:152
msgid "Open Sync Wizard..."
msgstr ""
msgstr "Avaa ohjattu synkronointitoiminto."
#: packages/lib/models/Setting.js:162
msgid "Synchronisation target"
@@ -3464,23 +3454,20 @@ msgid "Joplin Server URL"
msgstr "Joplin palvelimen URL-osoite"
#: packages/lib/models/Setting.js:348
#, fuzzy
msgid "Joplin Server email"
msgstr "Joplin Palvelin"
msgstr "Joplin palvelimen sähköposti"
#: packages/lib/models/Setting.js:359
msgid "Joplin Server password"
msgstr "Joplin palvelimen salasana"
#: packages/lib/models/Setting.js:386
#, fuzzy
msgid "Joplin Cloud email"
msgstr "Joplin Palvelin"
msgstr "Joplin Cloud sähköposti"
#: packages/lib/models/Setting.js:397
#, fuzzy
msgid "Joplin Cloud password"
msgstr "Joplin palvelimen salasana"
msgstr "Joplin Cloud salasana"
#: packages/lib/models/Setting.js:409
msgid "Attachment download behaviour"
@@ -3492,8 +3479,8 @@ msgid ""
"In \"Auto\", they are downloaded when you open the note. In \"Always\", all "
"the attachments are downloaded whether you open the note or not."
msgstr ""
"Manuaalisessa tilassa liitteet ladataan vain, kun napsautat niitä. \"Auto\" -"
"kohdassa ne ladataan, kun avaat muistiinpanon. \"Aina\" -kohdassa kaikki "
"Manuaalisessa tilassa liitteet ladataan vain, kun napsautat niitä. \"Auto\" "
"valinnassa ne ladataan, kun avaat muistiinpanon. \"Aina\" valinnassa kaikki "
"liitteet ladataan riippumatta siitä, avaatko muistiinpanon vai et."
#: packages/lib/models/Setting.js:413
@@ -3720,11 +3707,12 @@ msgid ""
"Used for most text in the markdown editor. If not found, a generic "
"proportional (variable width) font is used."
msgstr ""
"Käytetään useimmissa teksteissä markdown editorissa. Jos sitä ei löydy, "
"käytetään yleistä suhteellista fonttia (vaihtelevaa leveyttä)."
#: packages/lib/models/Setting.js:779
#, fuzzy
msgid "Editor monospace font family"
msgstr "Editorin fonttiperhe"
msgstr "Editorin monospace fonttiperhe"
#: packages/lib/models/Setting.js:780
msgid ""
@@ -3732,14 +3720,17 @@ msgid ""
"tables, checkboxes, code). If not found, a generic monospace (fixed width) "
"font is used."
msgstr ""
"Käytetään, kun tekstiin tarvitaan kiinteäleveyksinen fontti (esim. taulukot, "
"valintaruudut, koodi). Jos sitä ei löydy, käytetään yleistä monospace "
"fonttia (kiinteäleveyksinen)."
#: packages/lib/models/Setting.js:783
msgid "Editor maximum width"
msgstr ""
msgstr "Editorin enimmäisleveys"
#: packages/lib/models/Setting.js:783
msgid "Set it to 0 to make it take the complete available space."
msgstr ""
msgstr "Aseta arvoksi 0, jotta se vie koko käytettävissä olevan tilan."
#: packages/lib/models/Setting.js:802
msgid "Custom stylesheet for rendered Markdown"
@@ -3751,11 +3742,11 @@ msgstr "Mukautettu tyylitaulukko Joplinin sovellustyyleille"
#: packages/lib/models/Setting.js:828
msgid "Re-upload local data to sync target"
msgstr ""
msgstr "Lataa paikalliset tiedot uudelleen kohteen synkronointia varten"
#: packages/lib/models/Setting.js:838
msgid "Delete local data and re-download from sync target"
msgstr ""
msgstr "Poista paikalliset tiedot ja lataa uudelleen synkronointikohteesta"
#: packages/lib/models/Setting.js:843
msgid "Automatically update the application"
@@ -3792,7 +3783,7 @@ msgstr "%d tuntia"
#: packages/lib/models/Setting.js:871
msgid "Synchronise only over WiFi connection"
msgstr ""
msgstr "Synkronoi vain WiFi yhteyden kautta"
#: packages/lib/models/Setting.js:878
msgid "Text editor command"
@@ -3809,7 +3800,7 @@ msgstr ""
#: packages/lib/models/Setting.js:879
msgid "Page size for PDF export"
msgstr "PDF-viennin sivukoko"
msgstr "PDF viennin sivukoko"
#: packages/lib/models/Setting.js:881
msgid "A4"
@@ -3837,15 +3828,15 @@ msgstr "Legal"
#: packages/lib/models/Setting.js:889
msgid "Page orientation for PDF export"
msgstr "Sivun suunta PDF-vientiä varten"
msgstr "Sivun suunta PDF vientiä varten"
#: packages/lib/models/Setting.js:891
msgid "Portrait"
msgstr "Pysty"
msgstr "Pystysuunta"
#: packages/lib/models/Setting.js:892
msgid "Landscape"
msgstr "Vaaka"
msgstr "Vaakasuunta"
#: packages/lib/models/Setting.js:902
msgid "Keyboard Mode"
@@ -3861,11 +3852,11 @@ msgstr "Vim"
#: packages/lib/models/Setting.js:920
msgid "Do not resize images"
msgstr ""
msgstr "Älä muuta kuvien kokoa"
#: packages/lib/models/Setting.js:935
msgid "Custom TLS certificates"
msgstr "Mukautetut TLS-varmenteet"
msgstr "Mukautetut TLS varmenteet"
#: packages/lib/models/Setting.js:936
msgid ""
@@ -3876,13 +3867,13 @@ msgid ""
msgstr ""
"Pilkuilla erotettu luettelo hakemistoiden poluista, joilta varmenteet "
"ladataan, tai polku yksittäisiin varmennetiedostoihin. Esimerkiksi: /my/"
"cert_dir, /other/custom.pem. Huomaa, että jos teet muutoksia TLS-asetuksiin, "
"cert_dir, /other/custom.pem. Huomaa, että jos teet muutoksia TLS asetuksiin, "
"sinun on tallennettava muutokset ennen kuin napsautat \"Tarkista "
"synkronointiasetukset\"."
#: packages/lib/models/Setting.js:958
msgid "Ignore TLS certificate errors"
msgstr "Ohita TLS-varmenteen virheet"
msgstr "Ohita TLS varmenteen virheet"
#: packages/lib/models/Setting.js:967
msgid "Fail-safe"
@@ -3893,15 +3884,15 @@ msgid ""
"Fail-safe: Do not wipe out local data when sync target is empty (often the "
"result of a misconfiguration or bug)"
msgstr ""
"Vikasietoinen: Älä pyyhi paikallisia tietoja, kun synkronointikohde on tyhjä "
"(usein virheellisen määritysvirheen tai virheen seurauksena)"
"Vikaturvallinen: Älä pyyhi paikallisia tietoja, kun synkronointikohde on "
"tyhjä (usein virheellisen määritysvirheen tai virheen seurauksena)"
#: packages/lib/models/Setting.js:972
msgid ""
"Specify the port that should be used by the API server. If not set, a "
"default will be used."
msgstr ""
"Määritä portti, jota API-palvelimen on käytettävä. Jos sitä ei ole "
"Määritä portti, jota API palvelimen on käytettävä. Jos sitä ei ole "
"määritetty, käytetään oletusarvoa."
#: packages/lib/models/Setting.js:977
@@ -3933,10 +3924,10 @@ msgid ""
"item with a factor of 2 will take twice as much space as an item with a "
"factor of 1.Restart app to see changes."
msgstr ""
"Kerroin-ominaisuus määrittää, miten kohde kasvaa tai kutistuu niin, että se "
"Kerroin ominaisuus määrittää, miten kohde kasvaa tai kutistuu niin, että se "
"sopii säiliössä olevaan tilaan suhteessa muihin kohteisiin. Näin ollen "
"kohde, jonka kerroin on 2, vie kaksi kertaa enemmän tilaa kuin kohde, jonka "
"kerroin on 1.Restart-sovellus muutosten näkemistä varten."
"kerroin on 1.Restart sovellus muutosten näkemistä varten."
#: packages/lib/models/Setting.js:1029
msgid "Note list growth factor"
@@ -4010,7 +4001,7 @@ msgstr ""
#: packages/lib/models/Setting.js:1709
#, javascript-format
msgid "Notes and settings are stored in: %s"
msgstr "Muistiinpanot ja asetukset tallennetaan: %s"
msgstr "Muistiinpanot ja oletusasetukset tallennetaan: %s"
#: packages/lib/models/Tag.js:223
#, javascript-format
@@ -4213,28 +4204,28 @@ msgstr "Muistiinpano \"%s\" on palautettu muistikirjaan \"%s\"."
#: packages/lib/services/interop/InteropService.js:48
#: packages/lib/services/interop/InteropService.js:57
msgid "Joplin Export File"
msgstr "Joplin Vie tiedosto"
msgstr "Joplin vie tiedosto"
#: packages/lib/services/interop/InteropService.js:50
#: packages/lib/services/interop/InteropService.js:58
msgid "Joplin Export Directory"
msgstr "Joplin Vie hakemisto"
msgstr "Joplin vie hakemisto"
#: packages/lib/services/interop/InteropService.js:51
msgid "Evernote Export File (as Markdown)"
msgstr "Evernote Vie tiedosto (kuten Markdown)"
msgstr "Evernote vie tiedosto (kuten Markdown)"
#: packages/lib/services/interop/InteropService.js:52
msgid "Evernote Export File (as HTML)"
msgstr "Evernote Vie tiedosto (kuten HTML)"
msgstr "Evernote vie tiedosto (kuten HTML)"
#: packages/lib/services/interop/InteropService.js:60
msgid "HTML File"
msgstr "HTML Tiedosto"
msgstr "HTML tiedosto"
#: packages/lib/services/interop/InteropService.js:61
msgid "HTML Directory"
msgstr "HTML Hakemisto"
msgstr "HTML hakemisto"
#: packages/lib/services/interop/InteropService.js:127
#, javascript-format
@@ -4324,19 +4315,19 @@ msgid ""
"The default admin password is insecure and has not been changed! [Change it "
"now](%s)"
msgstr ""
"Järjestelmänvalvojan oletussalasana on epävarma, eikä sitä ole muutettu "
"[Vaihda se nyt] (%s)"
"Järjestelmänvalvojan oletussalasana on turvaton, eikä sitä ole muutettu "
"[Vaihda se nyt](%s)"
#: packages/server/dist/models/UserModel.js:199
#: packages/server/dist/models/UserModel.js:204
#, fuzzy
msgid "attachment"
msgstr "Liitteet"
msgstr "liite"
#: packages/server/dist/models/UserModel.js:199
#, javascript-format
msgid "Cannot save %s \"%s\" because it is larger than the allowed limit (%s)"
msgstr ""
"Tallennus ei onnistu %s \"%s\" koska se on suurempi kuin sallittu raja (%s)"
#: packages/server/dist/models/UserModel.js:204
#, javascript-format
@@ -4344,6 +4335,8 @@ msgid ""
"Cannot save %s \"%s\" because it would go over the total allowed size (%s) "
"for this account"
msgstr ""
"Kohdetta %s \"%s\" ei voida tallentaa, koska se ylittäisi tämän tilin "
"sallitun kokonaiskoon (%s)"
#, javascript-format
#~ msgid "%s %s (%s)"
@@ -4370,8 +4363,8 @@ msgstr ""
#~ msgid "Templates"
#~ msgstr "Mallit"
#~ msgid "Share Notes"
#~ msgstr "Jaa muistiinpanoja"
#~ msgid "Full Release Notes"
#~ msgstr "Täydelliset julkaisutiedot"
#~ msgid "Joplin Server Directory"
#~ msgstr "Joplin palvelimen hakemisto"
@@ -4379,25 +4372,11 @@ msgstr ""
#~ msgid "Joplin Server username"
#~ msgstr "Joplin palvelimen käyttäjänimi"
#, fuzzy
#~ msgid "marked text"
#~ msgstr "korostettu teksti"
#, fuzzy
#~ msgid "Mark"
#~ msgstr "Merkintä"
#~ msgid "Full Release Notes"
#~ msgstr "Täydelliset julkaisutiedot"
#, fuzzy
#~ msgid ""
#~ "If the font is incorrect or empty, it will default to a generic monospace "
#~ "font."
#~ msgstr ""
#~ "Tämän on oltava *monospace* fontti, muuten se ei toimi oikein. Jos "
#~ "kirjasin on väärä tai tyhjä, se on oletusarvoisesti yleinen monospace "
#~ "fontti."
#~ "Jos fontti on väärä tai tyhjä, se on oletuksena yleinen monospace fontti."
#~ msgid ""
#~ "This should be a *monospace* font or some elements will render "

View File

@@ -6,7 +6,7 @@
"packages": {
"": {
"name": "@joplin/tools",
"version": "2.3.0",
"version": "2.4.1",
"license": "MIT",
"dependencies": {
"execa": "^4.1.0",
@@ -31,6 +31,7 @@
"@types/mustache": "^0.8.32",
"@types/node": "^14.14.6",
"gulp": "^4.0.2",
"sass": "^1.39.2",
"sqlite3": "^5.0.0",
"typescript": "^4.1.3"
}
@@ -4197,6 +4198,18 @@
"integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -4654,6 +4667,159 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
"version": "1.39.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.39.2.tgz",
"integrity": "sha512-4/6Vn2RPc+qNwSclUSKvssh7dqK1Ih3FfHBW16I/GfH47b3scbYeOw65UIrYG7PkweFiKbpJjgkf5CV8EMmvzw==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/sass/node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/sass/node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/sass/node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/sass/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/sass/node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sass/node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/sass/node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -9271,6 +9437,12 @@
"integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==",
"dev": true
},
"picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"dev": true
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -9642,6 +9814,116 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sass": {
"version": "1.39.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.39.2.tgz",
"integrity": "sha512-4/6Vn2RPc+qNwSclUSKvssh7dqK1Ih3FfHBW16I/GfH47b3scbYeOw65UIrYG7PkweFiKbpJjgkf5CV8EMmvzw==",
"dev": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",

View File

@@ -42,6 +42,7 @@
"@types/mustache": "^0.8.32",
"@types/node": "^14.14.6",
"gulp": "^4.0.2",
"sass": "^1.39.2",
"sqlite3": "^5.0.0",
"typescript": "^4.1.3"
},

View File

@@ -79,6 +79,14 @@
{
"name": "cuongtransc",
"id": "808091"
},
{
"name": "iamwillbar",
"id": "3266447"
},
{
"name": "marcdw1289",
"id": "42319182"
}
],
"orgs": [

View File

@@ -147,7 +147,6 @@ export function execCommandVerbose(commandName: string, args: string[] = []) {
interface ExecCommandOptions {
showInput?: boolean;
showOutput?: boolean;
showError?: boolean;
quiet?: boolean;
}
@@ -161,7 +160,6 @@ export async function execCommand2(command: string | string[], options: ExecComm
options = {
showInput: true,
showOutput: true,
showError: true,
quiet: false,
...options,
};
@@ -169,7 +167,6 @@ export async function execCommand2(command: string | string[], options: ExecComm
if (options.quiet) {
options.showInput = false;
options.showOutput = false;
options.showError = false;
}
if (options.showInput) {
@@ -185,7 +182,6 @@ export async function execCommand2(command: string | string[], options: ExecComm
args.splice(0, 1);
const promise = execa(executableName, args);
if (options.showOutput) promise.stdout.pipe(process.stdout);
if (options.showError) promise.stderr.pipe(process.stderr);
const result = await promise;
return result.stdout.trim();
}

View File

@@ -1,5 +1,15 @@
# Joplin Server Changelog
## [server-v2.4.7-beta](https://github.com/laurent22/joplin/releases/tag/server-v2.4.7-beta) (Pre-release) - 2021-09-15T15:58:46Z
- Improved: Improve flag logic (c229821)
- Fixed: Fixed handling of brute force limiter by getting correct user IP (3ce947e)
## [server-v2.4.6-beta](https://github.com/laurent22/joplin/releases/tag/server-v2.4.6-beta) (Pre-release) - 2021-09-14T15:02:21Z
- New: Add link to Stripe subscription page to manage payment details (4e7fe66)
- New: Add transaction info to debug deadlock issues (01b653f)
## [server-v2.4.3-beta](https://github.com/laurent22/joplin/releases/tag/server-v2.4.3-beta) (Pre-release) - 2021-09-02T17:49:11Z
- New: Added Help page for Joplin Cloud (6520a48)