You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-11 00:21:45 +02:00
Compare commits
4 Commits
server_tas
...
refactor_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdf49302f9 | ||
|
|
c5fa20dadb | ||
|
|
9dbab0413a | ||
|
|
a03e9811b6 |
@@ -76,9 +76,6 @@ 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
|
||||
@@ -112,9 +109,6 @@ 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
|
||||
|
||||
8
.github/workflows/github-actions-main.yml
vendored
8
.github/workflows/github-actions-main.yml
vendored
@@ -35,6 +35,14 @@ 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
6
.gitignore
vendored
@@ -61,9 +61,6 @@ 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
|
||||
@@ -97,9 +94,6 @@ 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
|
||||
|
||||
BIN
Assets/WebsiteAssets/images/flags/country-squared/pf.png
Normal file
BIN
Assets/WebsiteAssets/images/flags/country-squared/pf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 297 B |
4
packages/app-cli/.gitignore
vendored
4
packages/app-cli/.gitignore
vendored
@@ -23,6 +23,4 @@ tests/support/dropbox-auth.txt
|
||||
tests/support/nextcloud-auth.json
|
||||
tests/support/onedrive-auth.txt
|
||||
build/
|
||||
patches/
|
||||
createUsers-*.txt
|
||||
tools/temp/
|
||||
patches/
|
||||
@@ -89,7 +89,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
flags = cliUtils.parseFlags(flags);
|
||||
|
||||
if (!flags.arg) {
|
||||
if (flags.short) booleanFlags.push(flags.short);
|
||||
booleanFlags.push(flags.short);
|
||||
if (flags.long) booleanFlags.push(flags.long);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
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;
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/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
|
||||
@@ -10,7 +10,6 @@
|
||||
"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"
|
||||
},
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// This script can be used to simulate a running production environment, by
|
||||
// having multiple users in parallel changing notes and synchronising.
|
||||
//
|
||||
// To get it working:
|
||||
//
|
||||
// - Run the Postgres database -- `sudo docker-compose --file docker-compose.db-dev.yml up`
|
||||
// - Update the DB parameters in ~/joplin-credentials/server.env to use the dev
|
||||
// database
|
||||
// - Run the server - `JOPLIN_IS_TESTING=1 npm run start-dev`
|
||||
// - Then run this script - `node populateDatabase.js`
|
||||
//
|
||||
// Currently it doesn't actually create the users, so that should be done using:
|
||||
//
|
||||
// curl --data '{"action": "createTestUsers", "count": 400, "fromNum": 1}' -H 'Content-Type: application/json' http://api.joplincloud.local:22300/api/debug
|
||||
//
|
||||
// That will create n users with email `user<n>@example.com`
|
||||
|
||||
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']);
|
||||
|
||||
const focusUserNum = 400;
|
||||
|
||||
while (true) {
|
||||
let userNum = randomInt(minUserNum, maxUserNum);
|
||||
|
||||
if (Math.random() >= .7) userNum = focusUserNum;
|
||||
|
||||
void processUser(userNum);
|
||||
await waitForProcessing(10);
|
||||
}
|
||||
};
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
.encryption-config-test {
|
||||
& > .item {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
const gulp = require('gulp');
|
||||
const utils = require('@joplin/tools/gulp/utils');
|
||||
const compileSass = require('@joplin/tools/compileSass');
|
||||
|
||||
const tasks = {
|
||||
compileScripts: {
|
||||
@@ -21,14 +20,6 @@ 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);
|
||||
@@ -40,7 +31,6 @@ const buildParallel = [
|
||||
'copyTinyMceLangs',
|
||||
'updateIgnoredTypeScriptBuild',
|
||||
'buildCommandIndex',
|
||||
'compileSass',
|
||||
];
|
||||
|
||||
gulp.task('build', gulp.parallel(...buildParallel));
|
||||
|
||||
4
packages/app-desktop/package-lock.json
generated
4
packages/app-desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.5",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
||||
5
packages/app-desktop/style.min.css
vendored
5
packages/app-desktop/style.min.css
vendored
@@ -1,5 +0,0 @@
|
||||
.encryption-config-test > .item {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=style.min.css.map */
|
||||
@@ -1 +1 @@
|
||||
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiNhYmIyYmY7YmFja2dyb3VuZDojMjgyYzM0fS5obGpzLWtleXdvcmQsLmhsanMtb3BlcmF0b3IsLmhsanMtcGF0dGVybi1tYXRjaHtjb2xvcjojZjkyNjcyfS5obGpzLWZ1bmN0aW9uLC5obGpzLXBhdHRlcm4tbWF0Y2ggLmhsanMtY29uc3RydWN0b3J7Y29sb3I6IzYxYWVlZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXN7Y29sb3I6I2E2ZTIyZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5ne2NvbG9yOiNmZDk3MWZ9LmhsanMtbW9kdWxlLWFjY2VzcyAuaGxqcy1tb2R1bGV7Y29sb3I6IzdlNTdjMn0uaGxqcy1jb25zdHJ1Y3Rvcntjb2xvcjojZTJiOTNkfS5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZ3tjb2xvcjojOWNjYzY1fS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2IxOGViMTtmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYXtjb2xvcjojYzY3OGRkfS5obGpzLWRlbGV0aW9uLC5obGpzLW5hbWUsLmhsanMtc2VjdGlvbiwuaGxqcy1zZWxlY3Rvci10YWcsLmhsanMtc3Vic3R7Y29sb3I6I2UwNmM3NX0uaGxqcy1saXRlcmFse2NvbG9yOiM1NmI2YzJ9LmhsanMtYWRkaXRpb24sLmhsanMtYXR0cmlidXRlLC5obGpzLW1ldGEgLmhsanMtc3RyaW5nLC5obGpzLXJlZ2V4cCwuaGxqcy1zdHJpbmd7Y29sb3I6Izk4YzM3OX0uaGxqcy1idWlsdF9pbiwuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSwuaGxqcy10aXRsZS5jbGFzc197Y29sb3I6I2U2YzA3Yn0uaGxqcy1hdHRyLC5obGpzLW51bWJlciwuaGxqcy1zZWxlY3Rvci1hdHRyLC5obGpzLXNlbGVjdG9yLWNsYXNzLC5obGpzLXNlbGVjdG9yLXBzZXVkbywuaGxqcy10ZW1wbGF0ZS12YXJpYWJsZSwuaGxqcy10eXBlLC5obGpzLXZhcmlhYmxle2NvbG9yOiNkMTlhNjZ9LmhsanMtYnVsbGV0LC5obGpzLWxpbmssLmhsanMtbWV0YSwuaGxqcy1zZWxlY3Rvci1pZCwuaGxqcy1zeW1ib2wsLmhsanMtdGl0bGV7Y29sb3I6IzYxYWVlZX0uaGxqcy1lbXBoYXNpc3tmb250LXN0eWxlOml0YWxpY30uaGxqcy1zdHJvbmd7Zm9udC13ZWlnaHQ6NzAwfS5obGpzLWxpbmt7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX0=`;
|
||||
module.exports = `LyoKCkF0b20gT25lIERhcmsgV2l0aCBzdXBwb3J0IGZvciBSZWFzb25NTCBieSBHaWRpIE1vcnJpcywgYmFzZWQgb2ZmIHdvcmsgYnkgRGFuaWVsIEdhbWFnZQoKT3JpZ2luYWwgT25lIERhcmsgU3ludGF4IHRoZW1lIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2F0b20vb25lLWRhcmstc3ludGF4CgoqLwouaGxqcyB7CiAgZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsKICBjb2xvcjogI2FiYjJiZjsKICBiYWNrZ3JvdW5kOiAjMjgyYzM0Owp9Ci5obGpzLWtleXdvcmQsIC5obGpzLW9wZXJhdG9yIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIC5obGpzLWNvbnN0cnVjdG9yIHsKICBjb2xvcjogIzYxYWVlZTsKfQouaGxqcy1mdW5jdGlvbiB7CiAgY29sb3I6ICM2MWFlZWU7Cn0KLmhsanMtZnVuY3Rpb24gLmhsanMtcGFyYW1zIHsKICBjb2xvcjogI0E2RTIyRTsKfQouaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5nIHsKICBjb2xvcjogI0ZEOTcxRjsKfQouaGxqcy1tb2R1bGUtYWNjZXNzIC5obGpzLW1vZHVsZSB7CiAgY29sb3I6ICM3ZTU3YzI7Cn0KLmhsanMtY29uc3RydWN0b3IgewogIGNvbG9yOiAjZTJiOTNkOwp9Ci5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZyB7CiAgY29sb3I6ICM5Q0NDNjU7Cn0KLmhsanMtY29tbWVudCwgLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYjE4ZWIxOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQouaGxqcy1kb2N0YWcsIC5obGpzLWZvcm11bGEgewogIGNvbG9yOiAjYzY3OGRkOwp9Ci5obGpzLXNlY3Rpb24sIC5obGpzLW5hbWUsIC5obGpzLXNlbGVjdG9yLXRhZywgLmhsanMtZGVsZXRpb24sIC5obGpzLXN1YnN0IHsKICBjb2xvcjogI2UwNmM3NTsKfQouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzU2YjZjMjsKfQouaGxqcy1zdHJpbmcsIC5obGpzLXJlZ2V4cCwgLmhsanMtYWRkaXRpb24sIC5obGpzLWF0dHJpYnV0ZSwgLmhsanMtbWV0YS1zdHJpbmcgewogIGNvbG9yOiAjOThjMzc5Owp9Ci5obGpzLWJ1aWx0X2luLCAuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSB7CiAgY29sb3I6ICNlNmMwN2I7Cn0KLmhsanMtYXR0ciwgLmhsanMtdmFyaWFibGUsIC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLCAuaGxqcy10eXBlLCAuaGxqcy1zZWxlY3Rvci1jbGFzcywgLmhsanMtc2VsZWN0b3ItYXR0ciwgLmhsanMtc2VsZWN0b3ItcHNldWRvLCAuaGxqcy1udW1iZXIgewogIGNvbG9yOiAjZDE5YTY2Owp9Ci5obGpzLXN5bWJvbCwgLmhsanMtYnVsbGV0LCAuaGxqcy1saW5rLCAuaGxqcy1tZXRhLCAuaGxqcy1zZWxlY3Rvci1pZCwgLmhsanMtdGl0bGUgewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQouaGxqcy1saW5rIHsKICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKfQo=`;
|
||||
@@ -1 +1 @@
|
||||
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiMzODNhNDI7YmFja2dyb3VuZDojZmFmYWZhfS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2EwYTFhNztmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYSwuaGxqcy1rZXl3b3Jke2NvbG9yOiNhNjI2YTR9LmhsanMtZGVsZXRpb24sLmhsanMtbmFtZSwuaGxqcy1zZWN0aW9uLC5obGpzLXNlbGVjdG9yLXRhZywuaGxqcy1zdWJzdHtjb2xvcjojZTQ1NjQ5fS5obGpzLWxpdGVyYWx7Y29sb3I6IzAxODRiYn0uaGxqcy1hZGRpdGlvbiwuaGxqcy1hdHRyaWJ1dGUsLmhsanMtbWV0YSAuaGxqcy1zdHJpbmcsLmhsanMtcmVnZXhwLC5obGpzLXN0cmluZ3tjb2xvcjojNTBhMTRmfS5obGpzLWF0dHIsLmhsanMtbnVtYmVyLC5obGpzLXNlbGVjdG9yLWF0dHIsLmhsanMtc2VsZWN0b3ItY2xhc3MsLmhsanMtc2VsZWN0b3ItcHNldWRvLC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLC5obGpzLXR5cGUsLmhsanMtdmFyaWFibGV7Y29sb3I6Izk4NjgwMX0uaGxqcy1idWxsZXQsLmhsanMtbGluaywuaGxqcy1tZXRhLC5obGpzLXNlbGVjdG9yLWlkLC5obGpzLXN5bWJvbCwuaGxqcy10aXRsZXtjb2xvcjojNDA3OGYyfS5obGpzLWJ1aWx0X2luLC5obGpzLWNsYXNzIC5obGpzLXRpdGxlLC5obGpzLXRpdGxlLmNsYXNzX3tjb2xvcjojYzE4NDAxfS5obGpzLWVtcGhhc2lze2ZvbnQtc3R5bGU6aXRhbGljfS5obGpzLXN0cm9uZ3tmb250LXdlaWdodDo3MDB9LmhsanMtbGlua3t0ZXh0LWRlY29yYXRpb246dW5kZXJsaW5lfQ==`;
|
||||
module.exports = `LyoKCkF0b20gT25lIExpZ2h0IGJ5IERhbmllbCBHYW1hZ2UKT3JpZ2luYWwgT25lIExpZ2h0IFN5bnRheCB0aGVtZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9hdG9tL29uZS1saWdodC1zeW50YXgKCmJhc2U6ICAgICNmYWZhZmEKbW9uby0xOiAgIzM4M2E0Mgptb25vLTI6ICAjNjg2Yjc3Cm1vbm8tMzogICNhMGExYTcKaHVlLTE6ICAgIzAxODRiYgpodWUtMjogICAjNDA3OGYyCmh1ZS0zOiAgICNhNjI2YTQKaHVlLTQ6ICAgIzUwYTE0ZgpodWUtNTogICAjZTQ1NjQ5Cmh1ZS01LTI6ICNjOTEyNDMKaHVlLTY6ICAgIzk4NjgwMQpodWUtNi0yOiAjYzE4NDAxCgoqLwoKLmhsanMgewogIGRpc3BsYXk6IGJsb2NrOwogIG92ZXJmbG93LXg6IGF1dG87CiAgcGFkZGluZzogMC41ZW07CiAgY29sb3I6ICMzODNhNDI7CiAgYmFja2dyb3VuZDogI2ZhZmFmYTsKfQoKLmhsanMtY29tbWVudCwKLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYTBhMWE3OwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtZG9jdGFnLAouaGxqcy1rZXl3b3JkLAouaGxqcy1mb3JtdWxhIHsKICBjb2xvcjogI2E2MjZhNDsKfQoKLmhsanMtc2VjdGlvbiwKLmhsanMtbmFtZSwKLmhsanMtc2VsZWN0b3ItdGFnLAouaGxqcy1kZWxldGlvbiwKLmhsanMtc3Vic3QgewogIGNvbG9yOiAjZTQ1NjQ5Owp9CgouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzAxODRiYjsKfQoKLmhsanMtc3RyaW5nLAouaGxqcy1yZWdleHAsCi5obGpzLWFkZGl0aW9uLAouaGxqcy1hdHRyaWJ1dGUsCi5obGpzLW1ldGEtc3RyaW5nIHsKICBjb2xvcjogIzUwYTE0ZjsKfQoKLmhsanMtYnVpbHRfaW4sCi5obGpzLWNsYXNzIC5obGpzLXRpdGxlIHsKICBjb2xvcjogI2MxODQwMTsKfQoKLmhsanMtYXR0ciwKLmhsanMtdmFyaWFibGUsCi5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLAouaGxqcy10eXBlLAouaGxqcy1zZWxlY3Rvci1jbGFzcywKLmhsanMtc2VsZWN0b3ItYXR0ciwKLmhsanMtc2VsZWN0b3ItcHNldWRvLAouaGxqcy1udW1iZXIgewogIGNvbG9yOiAjOTg2ODAxOwp9CgouaGxqcy1zeW1ib2wsCi5obGpzLWJ1bGxldCwKLmhsanMtbGluaywKLmhsanMtbWV0YSwKLmhsanMtc2VsZWN0b3ItaWQsCi5obGpzLXRpdGxlIHsKICBjb2xvcjogIzQwNzhmMjsKfQoKLmhsanMtZW1waGFzaXMgewogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoKLmhsanMtbGluayB7CiAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7Cn0K`;
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
hash:"de3871f000c87478973d7cd0913bd3ff", files: {
|
||||
hash:"6608023b8053b48e0eec248644475e33", 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
@@ -36,6 +36,26 @@ 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) => {
|
||||
|
||||
2
packages/lib/package-lock.json
generated
2
packages/lib/package-lock.json
generated
@@ -5669,7 +5669,6 @@
|
||||
"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": "*",
|
||||
@@ -15703,6 +15702,7 @@
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -589,25 +589,4 @@ 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
@@ -1 +1,75 @@
|
||||
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}
|
||||
/*
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1 +1,96 @@
|
||||
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}
|
||||
/*
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
12
packages/renderer/assets/mermaid/mermaid.min.js
vendored
12
packages/renderer/assets/mermaid/mermaid.min.js
vendored
File diff suppressed because one or more lines are too long
399
packages/renderer/package-lock.json
generated
399
packages/renderer/package-lock.json
generated
@@ -29,7 +29,7 @@
|
||||
"markdown-it-sup": "^1.0.0",
|
||||
"markdown-it-toc-done-right": "^4.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"mermaid": "^8.12.1",
|
||||
"mermaid": "^8.10.2",
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1998,8 +1998,7 @@
|
||||
"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==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"node_modules/cache-base": {
|
||||
"version": "1.0.1",
|
||||
@@ -2030,6 +2029,15 @@
|
||||
"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",
|
||||
@@ -2127,6 +2135,25 @@
|
||||
"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",
|
||||
@@ -2259,6 +2286,17 @@
|
||||
"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",
|
||||
@@ -2609,7 +2647,6 @@
|
||||
"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"
|
||||
},
|
||||
@@ -2760,11 +2797,6 @@
|
||||
"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",
|
||||
@@ -2807,6 +2839,14 @@
|
||||
"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",
|
||||
@@ -3502,6 +3542,14 @@
|
||||
"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",
|
||||
@@ -3542,6 +3590,26 @@
|
||||
"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",
|
||||
@@ -5977,6 +6045,11 @@
|
||||
"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",
|
||||
@@ -6161,19 +6234,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.12.1.tgz",
|
||||
"integrity": "sha512-0UCcSF0FLoNcPBsRF4f9OIV32t41fV18//z8o3S+FDz2PbDA1CRGKdQF9IX84VP4Tv9kcgJI/oqJdcBEtB/GPA==",
|
||||
"version": "8.10.2",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.2.tgz",
|
||||
"integrity": "sha512-Za5MrbAOMbEsyY4ONgGjfYz06sbwF1iNGRzp1sQqpOtvXxjxGu/J1jRJ8QyE9kD/D9zj1/KlRrYegWEvA7eZ5Q==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^3.1.0",
|
||||
"d3": "^5.16.0",
|
||||
"dagre": "^0.8.5",
|
||||
"d3": "^5.7.0",
|
||||
"dagre": "^0.8.4",
|
||||
"dagre-d3": "^0.6.4",
|
||||
"dompurify": "2.3.1",
|
||||
"graphlib": "^2.1.8",
|
||||
"khroma": "^1.4.1",
|
||||
"moment-mini": "^2.24.0",
|
||||
"stylis": "^4.0.10"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
@@ -6219,6 +6294,26 @@
|
||||
"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",
|
||||
@@ -6270,8 +6365,7 @@
|
||||
"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==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nanomatch": {
|
||||
"version": "1.2.13",
|
||||
@@ -6307,6 +6401,14 @@
|
||||
"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",
|
||||
@@ -6582,6 +6684,14 @@
|
||||
"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",
|
||||
@@ -6863,6 +6973,14 @@
|
||||
"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",
|
||||
@@ -7543,7 +7661,6 @@
|
||||
"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"
|
||||
@@ -7553,7 +7670,6 @@
|
||||
"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"
|
||||
}
|
||||
@@ -7760,9 +7876,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
|
||||
"integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
|
||||
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
@@ -7832,6 +7948,30 @@
|
||||
"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",
|
||||
@@ -7944,6 +8084,19 @@
|
||||
"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",
|
||||
@@ -8019,6 +8172,17 @@
|
||||
"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",
|
||||
@@ -8098,6 +8262,11 @@
|
||||
"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",
|
||||
@@ -10066,8 +10235,7 @@
|
||||
"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==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"cache-base": {
|
||||
"version": "1.0.1",
|
||||
@@ -10092,6 +10260,15 @@
|
||||
"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",
|
||||
@@ -10170,6 +10347,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -10283,6 +10475,11 @@
|
||||
"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",
|
||||
@@ -10614,7 +10811,6 @@
|
||||
"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"
|
||||
}
|
||||
@@ -10725,11 +10921,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -10766,6 +10957,14 @@
|
||||
"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",
|
||||
@@ -11313,6 +11512,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -11344,6 +11548,20 @@
|
||||
"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",
|
||||
@@ -13209,6 +13427,11 @@
|
||||
"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",
|
||||
@@ -13371,19 +13594,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"mermaid": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.12.1.tgz",
|
||||
"integrity": "sha512-0UCcSF0FLoNcPBsRF4f9OIV32t41fV18//z8o3S+FDz2PbDA1CRGKdQF9IX84VP4Tv9kcgJI/oqJdcBEtB/GPA==",
|
||||
"version": "8.10.2",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.2.tgz",
|
||||
"integrity": "sha512-Za5MrbAOMbEsyY4ONgGjfYz06sbwF1iNGRzp1sQqpOtvXxjxGu/J1jRJ8QyE9kD/D9zj1/KlRrYegWEvA7eZ5Q==",
|
||||
"requires": {
|
||||
"@braintree/sanitize-url": "^3.1.0",
|
||||
"d3": "^5.16.0",
|
||||
"dagre": "^0.8.5",
|
||||
"d3": "^5.7.0",
|
||||
"dagre": "^0.8.4",
|
||||
"dagre-d3": "^0.6.4",
|
||||
"dompurify": "2.3.1",
|
||||
"graphlib": "^2.1.8",
|
||||
"khroma": "^1.4.1",
|
||||
"moment-mini": "^2.24.0",
|
||||
"stylis": "^4.0.10"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
@@ -13417,6 +13642,20 @@
|
||||
"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",
|
||||
@@ -13461,8 +13700,7 @@
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
@@ -13495,6 +13733,14 @@
|
||||
"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",
|
||||
@@ -13705,6 +13951,14 @@
|
||||
"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",
|
||||
@@ -13918,6 +14172,11 @@
|
||||
"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",
|
||||
@@ -14472,7 +14731,6 @@
|
||||
"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"
|
||||
@@ -14481,8 +14739,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14648,9 +14905,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"stylis": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
|
||||
"integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
|
||||
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
@@ -14704,6 +14961,23 @@
|
||||
"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",
|
||||
@@ -14794,6 +15068,16 @@
|
||||
"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",
|
||||
@@ -14850,6 +15134,11 @@
|
||||
"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",
|
||||
@@ -14912,6 +15201,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -14935,6 +15229,7 @@
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"markdown-it-sup": "^1.0.0",
|
||||
"markdown-it-toc-done-right": "^4.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"mermaid": "^8.12.1",
|
||||
"mermaid": "^8.10.2",
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
|
||||
},
|
||||
"gitHead": "80c0089d2c52aff608b2bea74389de5a7f12f2e2"
|
||||
|
||||
4
packages/server/package-lock.json
generated
4
packages/server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@joplin/server",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@joplin/server",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.3",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||
"@koa/cors": "^3.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/server",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start-dev": "nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",
|
||||
|
||||
@@ -48,9 +48,6 @@ 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;
|
||||
@@ -59,17 +56,6 @@ 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
|
||||
@@ -86,16 +72,8 @@ 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',
|
||||
@@ -106,7 +84,6 @@ function databaseConfigFromEnv(runningInDocker: boolean, env: EnvVariables): Dat
|
||||
}
|
||||
|
||||
return {
|
||||
...baseConfig,
|
||||
client: DatabaseConfigClient.SQLite,
|
||||
name: env.SQLITE_DATABASE,
|
||||
asyncStackTraces: true,
|
||||
|
||||
@@ -104,63 +104,25 @@ 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> = {};
|
||||
|
||||
// These queries do not return a response, so "query-response" is not
|
||||
// called.
|
||||
const ignoredQueries = /^BEGIN|SAVEPOINT|RELEASE SAVEPOINT|COMMIT|ROLLBACK/gi;
|
||||
|
||||
connection.on('query', (data) => {
|
||||
const sql: string = data.sql;
|
||||
|
||||
if (!sql || sql.match(ignoredQueries)) return;
|
||||
|
||||
const timeoutId = makeSlowQueryHandler(slowQueryLogMinDuration, connection, 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));
|
||||
|
||||
if (dbConfig.slowQueryLogEnabled) {
|
||||
setupSlowQueryLog(connection, dbConfig.slowQueryLogMinDuration);
|
||||
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)`);
|
||||
});
|
||||
}
|
||||
|
||||
return connection;
|
||||
|
||||
@@ -42,13 +42,6 @@ async function handleSqliteInProdNotification(ctx: AppContext) {
|
||||
}
|
||||
}
|
||||
|
||||
function levelClassName(level: NotificationLevel): string {
|
||||
if (level === NotificationLevel.Important) return 'is-warning';
|
||||
if (level === NotificationLevel.Normal) return 'is-info';
|
||||
if (level === NotificationLevel.Error) return 'is-danger';
|
||||
throw new Error(`Unknown level: ${level}`);
|
||||
}
|
||||
|
||||
async function makeNotificationViews(ctx: AppContext): Promise<NotificationView[]> {
|
||||
const markdownIt = new MarkdownIt();
|
||||
|
||||
@@ -59,7 +52,7 @@ async function makeNotificationViews(ctx: AppContext): Promise<NotificationView[
|
||||
views.push({
|
||||
id: n.id,
|
||||
messageHtml: markdownIt.render(n.message),
|
||||
levelClassName: levelClassName(n.level),
|
||||
level: n.level === NotificationLevel.Important ? 'warning' : 'info',
|
||||
closeUrl: notificationModel.closeUrl(n.id),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ 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();
|
||||
@@ -27,9 +26,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}` + `: ${userIp(ctx)}: ${error.message}`);
|
||||
ctx.joplin.appLogger().error(`${error.httpCode}: ` + `${ctx.request.method} ${ctx.path}` + ` : ${error.message}`);
|
||||
} else {
|
||||
ctx.joplin.appLogger().error(userIp(ctx), error);
|
||||
ctx.joplin.appLogger().error(error);
|
||||
}
|
||||
|
||||
// Uncomment this when getting HTML blobs as errors while running tests.
|
||||
|
||||
@@ -7,9 +7,6 @@ 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;
|
||||
@@ -166,38 +163,36 @@ 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): Promise<T> {
|
||||
const debugSteps = false;
|
||||
const debugTimeout = true;
|
||||
const timeoutMs = 10000;
|
||||
protected async withTransaction<T>(fn: Function, name: string = null): Promise<T> {
|
||||
const debugTransaction = false;
|
||||
|
||||
let txIndex = 0;
|
||||
const debugTimerId = debugTransaction ? setTimeout(() => {
|
||||
console.info('Transaction did not complete:', name, txIndex);
|
||||
}, 5000) : null;
|
||||
|
||||
const debugTimerId = debugTimeout ? setTimeout(() => {
|
||||
logger.error(`Transaction #${txIndex} did not complete:`, name);
|
||||
logger.error('Transaction stack:');
|
||||
logger.error(this.transactionHandler_.stackInfo);
|
||||
}, timeoutMs) : null;
|
||||
const txIndex = await this.transactionHandler_.start();
|
||||
|
||||
txIndex = await this.transactionHandler_.start(name);
|
||||
|
||||
if (debugSteps) console.info('START', name, txIndex);
|
||||
if (debugTransaction) 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 (debugSteps) console.info('COMMIT', name, txIndex);
|
||||
if (debugTransaction) {
|
||||
console.info('COMMIT', name, txIndex);
|
||||
clearTimeout(debugTimerId);
|
||||
}
|
||||
|
||||
await this.transactionHandler_.commit(txIndex);
|
||||
return output;
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class ItemResourceModel extends BaseModel<ItemResource> {
|
||||
resource_id: resourceId,
|
||||
});
|
||||
}
|
||||
}, 'ItemResourceModel::addResourceIds');
|
||||
});
|
||||
}
|
||||
|
||||
public async byItemId(itemId: Uuid): Promise<string[]> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Notification, NotificationLevel, Uuid } from '../services/database/types';
|
||||
import { ErrorUnprocessableEntity } from '../utils/errors';
|
||||
import uuidgen from '../utils/uuidgen';
|
||||
import BaseModel, { ValidateOptions } from './BaseModel';
|
||||
|
||||
export enum NotificationKey {
|
||||
Any = 'any',
|
||||
ConfirmEmail = 'confirmEmail',
|
||||
PasswordSet = 'passwordSet',
|
||||
EmailConfirmed = 'emailConfirmed',
|
||||
@@ -54,10 +52,6 @@ export default class NotificationModel extends BaseModel<Notification> {
|
||||
level: NotificationLevel.Normal,
|
||||
message: 'Thank you! Your account has been successfully upgraded to Pro.',
|
||||
},
|
||||
[NotificationKey.Any]: {
|
||||
level: NotificationLevel.Normal,
|
||||
message: '',
|
||||
},
|
||||
};
|
||||
|
||||
const type = notificationTypes[key];
|
||||
@@ -78,9 +72,7 @@ export default class NotificationModel extends BaseModel<Notification> {
|
||||
}
|
||||
}
|
||||
|
||||
const actualKey = key === NotificationKey.Any ? `any_${uuidgen()}` : key;
|
||||
|
||||
return this.save({ key: actualKey, message, level, owner_id: userId });
|
||||
return this.save({ key, message, level, owner_id: userId });
|
||||
}
|
||||
|
||||
public async markAsRead(userId: Uuid, key: NotificationKey): Promise<void> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -91,14 +91,14 @@ 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
|
||||
// failed.
|
||||
//
|
||||
// We don't update the user can_upload and enabled properties here
|
||||
// because it's done after a few days from TaskService.
|
||||
// because it's done after a few days from CronService.
|
||||
if (!sub.last_payment_failed_time) {
|
||||
const user = await this.models().user().load(sub.user_id, { fields: ['email', 'id', 'full_name'] });
|
||||
|
||||
@@ -145,7 +145,7 @@ export default class SubscriptionModel extends BaseModel<Subscription> {
|
||||
});
|
||||
|
||||
return { user, subscription };
|
||||
}, 'SubscriptionModel::saveUserAndSubscription');
|
||||
});
|
||||
}
|
||||
|
||||
public async toggleSoftDelete(id: number, isDeleted: boolean) {
|
||||
|
||||
@@ -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,34 +95,17 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
|
||||
enabled: 1,
|
||||
};
|
||||
|
||||
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) {
|
||||
if (flags.find(f => f.type === UserFlagType.AccountWithoutSubscription)) {
|
||||
newProps.can_upload = 0;
|
||||
}
|
||||
|
||||
if (accountOverLimitFlag) {
|
||||
} else if (flags.find(f => f.type === UserFlagType.AccountOverLimit)) {
|
||||
newProps.can_upload = 0;
|
||||
}
|
||||
|
||||
if (failedPaymentWarningFlag) {
|
||||
} else if (flags.find(f => f.type === UserFlagType.FailedPaymentWarning)) {
|
||||
newProps.can_upload = 0;
|
||||
}
|
||||
|
||||
if (failedPaymentFinalFlag) {
|
||||
} else if (flags.find(f => f.type === UserFlagType.FailedPaymentFinal)) {
|
||||
newProps.enabled = 0;
|
||||
}
|
||||
|
||||
if (subscriptionCancelledFlag) {
|
||||
} else if (flags.find(f => f.type === UserFlagType.SubscriptionCancelled)) {
|
||||
newProps.enabled = 0;
|
||||
}
|
||||
|
||||
if (manuallyDisabledFlag) {
|
||||
} else if (flags.find(f => f.type === UserFlagType.ManuallyDisabled)) {
|
||||
newProps.enabled = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,15 +466,7 @@ 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');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import config from '../../config';
|
||||
import { clearDatabase, createTestUsers, CreateTestUsersOptions } from '../../tools/debugTools';
|
||||
import { createTestUsers } from '../../tools/debugTools';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
@@ -12,8 +12,6 @@ router.public = true;
|
||||
|
||||
interface Query {
|
||||
action: string;
|
||||
count?: number;
|
||||
fromNum?: number;
|
||||
}
|
||||
|
||||
router.post('api/debug', async (_path: SubPath, ctx: AppContext) => {
|
||||
@@ -22,16 +20,7 @@ router.post('api/debug', async (_path: SubPath, ctx: AppContext) => {
|
||||
console.info(`Action: ${query.action}`);
|
||||
|
||||
if (query.action === 'createTestUsers') {
|
||||
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);
|
||||
await createTestUsers(ctx.joplin.db, config());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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, userIp } from '../../utils/requestUtils';
|
||||
import { bodyFields } 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(userIp(ctx));
|
||||
await limiterLoginBruteForce(ctx.ip);
|
||||
|
||||
const fields: User = await bodyFields(ctx.req);
|
||||
const user = await ctx.joplin.models.user().login(fields.email, fields.password);
|
||||
|
||||
@@ -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, userIp } from '../../utils/requestUtils';
|
||||
import { formParse } 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(userIp(ctx));
|
||||
await limiterLoginBruteForce(ctx.ip);
|
||||
|
||||
try {
|
||||
const body = await formParse(ctx.req);
|
||||
|
||||
@@ -137,14 +137,6 @@ 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);
|
||||
@@ -434,7 +426,7 @@ const getHandlers: Record<string, StripeRouteHandler> = {
|
||||
<body>
|
||||
<button id="checkout">Subscribe</button>
|
||||
<script>
|
||||
var PRICE_ID = ${JSON.stringify(basicPrice.id)};
|
||||
var PRICE_ID = ${basicPrice.id};
|
||||
|
||||
function handleResult() {
|
||||
console.info('Redirected to checkout');
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import { makeUrl, redirect, SubPath, UrlType } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { ErrorBadRequest, ErrorForbidden } from '../../utils/errors';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
import { makeTableView, Row, Table } from '../../utils/views/table';
|
||||
import { yesOrNo } from '../../utils/strings';
|
||||
import { formatDateTime } from '../../utils/time';
|
||||
import { createCsrfTag } from '../../utils/csrf';
|
||||
import { RunType } from '../../services/TaskService';
|
||||
import { NotificationKey } from '../../models/NotificationModel';
|
||||
import { NotificationLevel } from '../../services/database/types';
|
||||
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
|
||||
router.post('tasks', async (_path: SubPath, ctx: AppContext) => {
|
||||
const user = ctx.joplin.owner;
|
||||
if (!user.is_admin) throw new ErrorForbidden();
|
||||
|
||||
const taskService = ctx.joplin.services.tasks;
|
||||
const fields: any = await bodyFields(ctx.req);
|
||||
|
||||
if (fields.startTaskButton) {
|
||||
const errors: Error[] = [];
|
||||
|
||||
for (const k of Object.keys(fields)) {
|
||||
if (k.startsWith('checkbox_')) {
|
||||
const taskId = k.substr(9);
|
||||
try {
|
||||
void taskService.runTask(taskId, RunType.Manual);
|
||||
} catch (error) {
|
||||
errors.push(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
await ctx.joplin.models.notification().add(
|
||||
user.id,
|
||||
NotificationKey.Any,
|
||||
NotificationLevel.Error,
|
||||
`Some tasks could not be started: ${errors.join('. ')}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new ErrorBadRequest('Invalid action');
|
||||
}
|
||||
|
||||
return redirect(ctx, makeUrl(UrlType.Tasks));
|
||||
});
|
||||
|
||||
router.get('tasks', async (_path: SubPath, ctx: AppContext) => {
|
||||
const user = ctx.joplin.owner;
|
||||
if (!user.is_admin) throw new ErrorForbidden();
|
||||
|
||||
const taskService = ctx.joplin.services.tasks;
|
||||
|
||||
const taskRows: Row[] = [];
|
||||
for (const [taskId, task] of Object.entries(taskService.tasks)) {
|
||||
const state = taskService.taskState(taskId);
|
||||
|
||||
taskRows.push([
|
||||
{
|
||||
value: `checkbox_${taskId}`,
|
||||
checkbox: true,
|
||||
},
|
||||
{
|
||||
value: taskId,
|
||||
},
|
||||
{
|
||||
value: task.description,
|
||||
},
|
||||
{
|
||||
value: task.schedule,
|
||||
},
|
||||
{
|
||||
value: yesOrNo(state.running),
|
||||
},
|
||||
{
|
||||
value: state.lastRunTime ? formatDateTime(state.lastRunTime) : '-',
|
||||
},
|
||||
{
|
||||
value: state.lastCompletionTime ? formatDateTime(state.lastCompletionTime) : '-',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const table: Table = {
|
||||
headers: [
|
||||
{
|
||||
name: 'select',
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
label: 'ID',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
},
|
||||
{
|
||||
name: 'schedule',
|
||||
label: 'Schedule',
|
||||
},
|
||||
{
|
||||
name: 'running',
|
||||
label: 'Running',
|
||||
},
|
||||
{
|
||||
name: 'lastRunTime',
|
||||
label: 'Last Run',
|
||||
},
|
||||
{
|
||||
name: 'lastCompletionTime',
|
||||
label: 'Last Completion',
|
||||
},
|
||||
],
|
||||
rows: taskRows,
|
||||
};
|
||||
|
||||
return {
|
||||
...defaultView('tasks', 'Tasks'),
|
||||
content: {
|
||||
itemTable: makeTableView(table),
|
||||
postUrl: makeUrl(UrlType.Tasks),
|
||||
csrfTag: await createCsrfTag(ctx),
|
||||
},
|
||||
cssFiles: ['index/tasks'],
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -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, stripePortalUrl } from '../../utils/urlUtils';
|
||||
import { confirmUrl } from '../../utils/urlUtils';
|
||||
import { cancelSubscriptionByUserId, updateSubscriptionType } from '../../utils/stripe';
|
||||
import { createCsrfTag } from '../../utils/csrf';
|
||||
import { formatDateTime } from '../../utils/time';
|
||||
@@ -175,7 +175,6 @@ 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');
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
import { Routers } from '../utils/routeUtils';
|
||||
|
||||
import apiBatch from './api/batch';
|
||||
import apiBatchItems from './api/batch_items';
|
||||
import apiDebug from './api/debug';
|
||||
import apiEvents from './api/events';
|
||||
import apiBatchItems from './api/batch_items';
|
||||
import apiItems from './api/items';
|
||||
import apiPing from './api/ping';
|
||||
import apiSessions from './api/sessions';
|
||||
import apiUsers from './api/users';
|
||||
import apiShares from './api/shares';
|
||||
import apiShareUsers from './api/share_users';
|
||||
import apiUsers from './api/users';
|
||||
|
||||
import indexChanges from './index/changes';
|
||||
import indexHelp from './index/help';
|
||||
import indexHome from './index/home';
|
||||
import indexItems from './index/items';
|
||||
import indexLogin from './index/login';
|
||||
import indexLogout from './index/logout';
|
||||
import indexNotifications from './index/notifications';
|
||||
import indexPassword from './index/password';
|
||||
import indexPrivacy from './index/privacy';
|
||||
import indexShares from './index/shares';
|
||||
import indexSignup from './index/signup';
|
||||
import indexStripe from './index/stripe';
|
||||
import indexTasks from './index/tasks';
|
||||
import indexTerms from './index/terms';
|
||||
import indexUpgrade from './index/upgrade';
|
||||
import indexShares from './index/shares';
|
||||
import indexUsers from './index/users';
|
||||
import indexStripe from './index/stripe';
|
||||
import indexTerms from './index/terms';
|
||||
import indexPrivacy from './index/privacy';
|
||||
import indexUpgrade from './index/upgrade';
|
||||
import indexHelp from './index/help';
|
||||
|
||||
import defaultRoute from './default';
|
||||
|
||||
@@ -57,7 +56,6 @@ const routes: Routers = {
|
||||
'privacy': indexPrivacy,
|
||||
'upgrade': indexUpgrade,
|
||||
'help': indexHelp,
|
||||
'tasks': indexTasks,
|
||||
|
||||
'': defaultRoute,
|
||||
};
|
||||
|
||||
42
packages/server/src/services/CronService.ts
Normal file
42
packages/server/src/services/CronService.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import BaseService from './BaseService';
|
||||
const cron = require('node-cron');
|
||||
|
||||
const logger = Logger.create('cron');
|
||||
|
||||
async function runCronTask(name: string, callback: Function) {
|
||||
const startTime = Date.now();
|
||||
logger.info(`Running task "${name}"`);
|
||||
try {
|
||||
await callback();
|
||||
} catch (error) {
|
||||
logger.error(`On task "${name}"`, error);
|
||||
}
|
||||
logger.info(`Completed task "${name}" in ${Date.now() - startTime}ms`);
|
||||
}
|
||||
|
||||
export default class CronService extends BaseService {
|
||||
|
||||
public async runInBackground() {
|
||||
cron.schedule('0 */6 * * *', async () => {
|
||||
await runCronTask('deleteExpiredTokens', async () => this.models.token().deleteExpiredTokens());
|
||||
});
|
||||
|
||||
cron.schedule('0 * * * *', async () => {
|
||||
await runCronTask('updateTotalSizes', async () => this.models.item().updateTotalSizes());
|
||||
});
|
||||
|
||||
cron.schedule('0 12 * * *', async () => {
|
||||
await runCronTask('handleBetaUserEmails', async () => this.models.user().handleBetaUserEmails());
|
||||
});
|
||||
|
||||
cron.schedule('0 13 * * *', async () => {
|
||||
await runCronTask('handleFailedPaymentSubscriptions', async () => this.models.user().handleFailedPaymentSubscriptions());
|
||||
});
|
||||
|
||||
cron.schedule('0 14 * * *', async () => {
|
||||
await runCronTask('handleOversizedAccounts', async () => this.models.user().handleOversizedAccounts());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import config from '../config';
|
||||
import { Models } from '../models/factory';
|
||||
import { afterAllTests, beforeAllDb, beforeEachDb, expectThrow, models, msleep } from '../utils/testing/testUtils';
|
||||
import { Env } from '../utils/types';
|
||||
import TaskService, { RunType, Task } from './TaskService';
|
||||
|
||||
const newService = () => {
|
||||
return new TaskService(Env.Dev, models(), config());
|
||||
};
|
||||
|
||||
describe('TaskService', function() {
|
||||
|
||||
beforeAll(async () => {
|
||||
await beforeAllDb('TaskService');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await afterAllTests();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEachDb();
|
||||
});
|
||||
|
||||
test('should register a task', async function() {
|
||||
const service = newService();
|
||||
|
||||
const task: Task = {
|
||||
id: 'test',
|
||||
description: '',
|
||||
run: (_models: Models) => {},
|
||||
schedule: '',
|
||||
};
|
||||
|
||||
service.registerTask(task);
|
||||
|
||||
expect(service.tasks['test']).toBeTruthy();
|
||||
await expectThrow(async () => service.registerTask(task));
|
||||
});
|
||||
|
||||
test('should run a task', async function() {
|
||||
const service = newService();
|
||||
|
||||
let finishTask = false;
|
||||
let taskHasRan = false;
|
||||
|
||||
const task: Task = {
|
||||
id: 'test',
|
||||
description: '',
|
||||
run: async (_models: Models) => {
|
||||
const iid = setInterval(() => {
|
||||
if (finishTask) {
|
||||
clearInterval(iid);
|
||||
taskHasRan = true;
|
||||
}
|
||||
}, 1);
|
||||
},
|
||||
schedule: '',
|
||||
};
|
||||
|
||||
service.registerTask(task);
|
||||
|
||||
expect(service.taskState('test').running).toBe(false);
|
||||
|
||||
const startTime = new Date();
|
||||
|
||||
void service.runTask('test', RunType.Manual);
|
||||
expect(service.taskState('test').running).toBe(true);
|
||||
expect(service.taskState('test').lastCompletionTime).toBeFalsy();
|
||||
expect(service.taskState('test').lastRunTime.getTime()).toBeGreaterThanOrEqual(startTime.getTime());
|
||||
|
||||
await msleep(1);
|
||||
finishTask = true;
|
||||
await msleep(3);
|
||||
|
||||
expect(taskHasRan).toBe(true);
|
||||
expect(service.taskState('test').running).toBe(false);
|
||||
expect(service.taskState('test').lastCompletionTime.getTime()).toBeGreaterThan(startTime.getTime());
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,108 +0,0 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { Models } from '../models/factory';
|
||||
import BaseService from './BaseService';
|
||||
const cron = require('node-cron');
|
||||
|
||||
const logger = Logger.create('TaskService');
|
||||
|
||||
type TaskId = string;
|
||||
|
||||
export enum RunType {
|
||||
Scheduled = 1,
|
||||
Manual = 2,
|
||||
}
|
||||
|
||||
const runTypeToString = (runType: RunType) => {
|
||||
if (runType === RunType.Scheduled) return 'scheduled';
|
||||
if (runType === RunType.Manual) return 'manual';
|
||||
throw new Error(`Unknown run type: ${runType}`);
|
||||
};
|
||||
|
||||
export interface Task {
|
||||
id: TaskId;
|
||||
description: string;
|
||||
schedule: string;
|
||||
run(models: Models): void;
|
||||
}
|
||||
|
||||
export type Tasks = Record<TaskId, Task>;
|
||||
|
||||
interface TaskState {
|
||||
running: boolean;
|
||||
lastRunTime: Date;
|
||||
lastCompletionTime: Date;
|
||||
}
|
||||
|
||||
const defaultTaskState: TaskState = {
|
||||
running: false,
|
||||
lastRunTime: null,
|
||||
lastCompletionTime: null,
|
||||
};
|
||||
|
||||
export default class TaskService extends BaseService {
|
||||
|
||||
private tasks_: Tasks = {};
|
||||
private taskStates_: Record<TaskId, TaskState> = {};
|
||||
|
||||
public registerTask(task: Task) {
|
||||
if (this.tasks_[task.id]) throw new Error(`Already a task with this ID: ${task.id}`);
|
||||
this.tasks_[task.id] = task;
|
||||
this.taskStates_[task.id] = { ...defaultTaskState };
|
||||
}
|
||||
|
||||
public registerTasks(tasks: Task[]) {
|
||||
for (const task of tasks) this.registerTask(task);
|
||||
}
|
||||
|
||||
public get tasks(): Tasks {
|
||||
return this.tasks_;
|
||||
}
|
||||
|
||||
public taskState(id: TaskId): TaskState {
|
||||
if (!this.taskStates_[id]) throw new Error(`No such task: ${id}`);
|
||||
return this.taskStates_[id];
|
||||
}
|
||||
|
||||
// TODO: add tests
|
||||
|
||||
public async runTask(id: TaskId, runType: RunType) {
|
||||
const state = this.taskState(id);
|
||||
if (state.running) throw new Error(`Task is already running: ${id}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
this.taskStates_[id] = {
|
||||
...this.taskStates_[id],
|
||||
running: true,
|
||||
lastRunTime: new Date(),
|
||||
};
|
||||
|
||||
try {
|
||||
logger.info(`Running "${id}" (${runTypeToString(runType)})...`);
|
||||
await this.tasks_[id].run(this.models);
|
||||
} catch (error) {
|
||||
logger.error(`On task "${id}"`, error);
|
||||
}
|
||||
|
||||
this.taskStates_[id] = {
|
||||
...this.taskStates_[id],
|
||||
running: false,
|
||||
lastCompletionTime: new Date(),
|
||||
};
|
||||
|
||||
logger.info(`Completed "${id}" in ${Date.now() - startTime}ms`);
|
||||
}
|
||||
|
||||
public async runInBackground() {
|
||||
for (const [taskId, task] of Object.entries(this.tasks_)) {
|
||||
if (!task.schedule) continue;
|
||||
|
||||
logger.info(`Scheduling task "${taskId}": ${task.schedule}`);
|
||||
|
||||
cron.schedule(task.schedule, async () => {
|
||||
await this.runTask(taskId, RunType.Scheduled);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ export enum ItemAddressingType {
|
||||
}
|
||||
|
||||
export enum NotificationLevel {
|
||||
Error = 5,
|
||||
Important = 10,
|
||||
Normal = 20,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import CronService from './CronService';
|
||||
import EmailService from './EmailService';
|
||||
import MustacheService from './MustacheService';
|
||||
import ShareService from './ShareService';
|
||||
import TaskService from './TaskService';
|
||||
|
||||
export interface Services {
|
||||
share: ShareService;
|
||||
email: EmailService;
|
||||
cron: CronService;
|
||||
mustache: MustacheService;
|
||||
tasks: TaskService;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { DbConnection, dropTables, migrateLatest } from '../db';
|
||||
import newModelFactory from '../models/factory';
|
||||
import { AccountType } from '../models/UserModel';
|
||||
import { User, UserFlagType } from '../services/database/types';
|
||||
import { 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);
|
||||
@@ -19,79 +14,51 @@ export async function handleDebugCommands(argv: any, db: DbConnection, config: C
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function clearDatabase(db: DbConnection) {
|
||||
export async function createTestUsers(db: DbConnection, config: Config) {
|
||||
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);
|
||||
|
||||
if (options.count) {
|
||||
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}`,
|
||||
});
|
||||
}
|
||||
|
||||
const users: User[] = [];
|
||||
{
|
||||
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 });
|
||||
}
|
||||
|
||||
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, 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);
|
||||
}
|
||||
|
||||
await models.user().saveMulti(users);
|
||||
} else {
|
||||
await dropTables(db);
|
||||
await migrateLatest(db);
|
||||
const models = newModelFactory(db, config);
|
||||
{
|
||||
const user = await models.user().save({
|
||||
email: 'userwithflags@example.com',
|
||||
password,
|
||||
full_name: 'User Withflags',
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
await models.userFlag().add(user.id, UserFlagType.AccountOverLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
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
|
||||
@@ -15,7 +9,7 @@ interface TransactionInfo {
|
||||
// Set logEnabled_ to `true` to see what happens with nested transactions.
|
||||
export default class TransactionHandler {
|
||||
|
||||
private transactionStack_: TransactionInfo[] = [];
|
||||
private transactionStack_: number[] = [];
|
||||
private activeTransaction_: Knex.Transaction = null;
|
||||
private transactionIndex_: number = 0;
|
||||
private logEnabled_: boolean = false;
|
||||
@@ -42,15 +36,7 @@ export default class TransactionHandler {
|
||||
return this.activeTransaction_;
|
||||
}
|
||||
|
||||
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> {
|
||||
public async start(): Promise<number> {
|
||||
const txIndex = ++this.transactionIndex_;
|
||||
this.log(`Starting transaction: ${txIndex}`);
|
||||
|
||||
@@ -61,19 +47,14 @@ export default class TransactionHandler {
|
||||
this.log(`Got transaction: ${txIndex}`);
|
||||
}
|
||||
|
||||
this.transactionStack_.push({
|
||||
name,
|
||||
index: txIndex,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
this.transactionStack_.push(txIndex);
|
||||
return txIndex;
|
||||
}
|
||||
|
||||
private finishTransaction(txIndex: number): boolean {
|
||||
if (!this.transactionStack_.length) throw new Error('Committing but no transaction was started');
|
||||
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}`);
|
||||
const lastTxIndex = this.transactionStack_.pop();
|
||||
if (lastTxIndex !== txIndex) throw new Error(`Committing a transaction but was not last to start one: ${txIndex}. Expected: ${lastTxIndex}`);
|
||||
return !this.transactionStack_.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +70,3 @@ 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;
|
||||
}
|
||||
|
||||
@@ -271,7 +271,6 @@ export enum UrlType {
|
||||
Login = 'login',
|
||||
Terms = 'terms',
|
||||
Privacy = 'privacy',
|
||||
Tasks = 'tasks',
|
||||
}
|
||||
|
||||
export function makeUrl(urlType: UrlType): string {
|
||||
|
||||
@@ -7,15 +7,15 @@ import routes from '../routes/routes';
|
||||
import ShareService from '../services/ShareService';
|
||||
import { Services } from '../services/types';
|
||||
import EmailService from '../services/EmailService';
|
||||
import CronService from '../services/CronService';
|
||||
import MustacheService from '../services/MustacheService';
|
||||
import setupTaskService from './setupTaskService';
|
||||
|
||||
async function setupServices(env: Env, models: Models, config: Config): Promise<Services> {
|
||||
const output: Services = {
|
||||
share: new ShareService(env, models, config),
|
||||
email: new EmailService(env, models, config),
|
||||
cron: new CronService(env, models, config),
|
||||
mustache: new MustacheService(config.viewDir, config.baseUrl),
|
||||
tasks: setupTaskService(env, models, config),
|
||||
};
|
||||
|
||||
await output.mustache.loadPartials();
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Models } from '../models/factory';
|
||||
import TaskService, { Task } from '../services/TaskService';
|
||||
import { Config, Env } from './types';
|
||||
|
||||
export default function(env: Env, models: Models, config: Config): TaskService {
|
||||
const taskService = new TaskService(env, models, config);
|
||||
|
||||
let tasks: Task[] = [
|
||||
{
|
||||
id: 'deleteExpiredTokens',
|
||||
description: 'Delete expired tokens',
|
||||
schedule: '0 */6 * * *',
|
||||
run: (models: Models) => models.token().deleteExpiredTokens(),
|
||||
},
|
||||
{
|
||||
id: 'updateTotalSizes',
|
||||
description: 'Update total sizes',
|
||||
schedule: '0 * * * *',
|
||||
run: (models: Models) => models.item().updateTotalSizes(),
|
||||
},
|
||||
{
|
||||
id: 'handleOversizedAccounts',
|
||||
description: 'Process oversized accounts',
|
||||
schedule: '0 14 * * *',
|
||||
run: (models: Models) => models.user().handleOversizedAccounts(),
|
||||
},
|
||||
];
|
||||
|
||||
if (config.isJoplinCloud) {
|
||||
tasks = tasks.concat([
|
||||
{
|
||||
id: 'handleBetaUserEmails',
|
||||
description: 'Process beta user emails',
|
||||
schedule: '0 12 * * *',
|
||||
run: (models: Models) => models.user().handleBetaUserEmails(),
|
||||
},
|
||||
{
|
||||
id: 'handleFailedPaymentSubscriptions',
|
||||
description: 'Process failed payment subscriptions',
|
||||
schedule: '0 13 * * *',
|
||||
run: (models: Models) => models.user().handleFailedPaymentSubscriptions(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
taskService.registerTasks(tasks);
|
||||
|
||||
return taskService;
|
||||
}
|
||||
@@ -3,5 +3,5 @@ import { Services } from '../services/types';
|
||||
export default async function startServices(services: Services) {
|
||||
void services.share.runInBackground();
|
||||
void services.email.runInBackground();
|
||||
void services.tasks.runInBackground();
|
||||
void services.cron.runInBackground();
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ export function msleep(ms: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export function formatDateTime(ms: number | Date): string {
|
||||
ms = ms instanceof Date ? ms.getTime() : ms;
|
||||
export function formatDateTime(ms: number): string {
|
||||
return `${dayjs(ms).format('D MMM YY HH:mm:ss')} (${defaultTimezone()})`;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export enum Env {
|
||||
export interface NotificationView {
|
||||
id: Uuid;
|
||||
messageHtml: string;
|
||||
levelClassName: string;
|
||||
level: string;
|
||||
closeUrl: string;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ export interface AppContext extends Koa.Context {
|
||||
}
|
||||
|
||||
export enum DatabaseConfigClient {
|
||||
Null = 'null',
|
||||
PostgreSQL = 'pg',
|
||||
SQLite = 'sqlite3',
|
||||
}
|
||||
@@ -65,8 +64,6 @@ export interface DatabaseConfig {
|
||||
user?: string;
|
||||
password?: string;
|
||||
asyncStackTraces?: boolean;
|
||||
slowQueryLogEnabled?: boolean;
|
||||
slowQueryLogMinDuration?: number;
|
||||
}
|
||||
|
||||
export interface MailerConfig {
|
||||
|
||||
@@ -4,13 +4,11 @@ import { setQueryParameters } from '../urlUtils';
|
||||
const defaultSortOrder = PaginationOrderDir.ASC;
|
||||
|
||||
function headerIsSelectedClass(name: string, pagination: Pagination): string {
|
||||
if (!pagination) return '';
|
||||
const orderBy = pagination.order[0].by;
|
||||
return name === orderBy ? 'is-selected' : '';
|
||||
}
|
||||
|
||||
function headerSortIconDir(name: string, pagination: Pagination): string {
|
||||
if (!pagination) return '';
|
||||
const orderBy = pagination.order[0].by;
|
||||
const orderDir = orderBy === name ? pagination.order[0].dir : defaultSortOrder;
|
||||
return orderDir === PaginationOrderDir.ASC ? 'up' : 'down';
|
||||
@@ -37,7 +35,6 @@ interface HeaderView {
|
||||
|
||||
interface RowItem {
|
||||
value: string;
|
||||
checkbox?: boolean;
|
||||
url?: string;
|
||||
stretch?: boolean;
|
||||
}
|
||||
@@ -48,7 +45,6 @@ interface RowItemView {
|
||||
value: string;
|
||||
classNames: string[];
|
||||
url: string;
|
||||
checkbox: boolean;
|
||||
}
|
||||
|
||||
type RowView = RowItemView[];
|
||||
@@ -56,10 +52,10 @@ type RowView = RowItemView[];
|
||||
export interface Table {
|
||||
headers: Header[];
|
||||
rows: Row[];
|
||||
baseUrl?: string;
|
||||
requestQuery?: any;
|
||||
pageCount?: number;
|
||||
pagination?: Pagination;
|
||||
baseUrl: string;
|
||||
requestQuery: any;
|
||||
pageCount: number;
|
||||
pagination: Pagination;
|
||||
}
|
||||
|
||||
export interface TableView {
|
||||
@@ -81,7 +77,7 @@ export function makeTablePagination(query: any, defaultOrderField: string, defau
|
||||
function makeHeaderView(header: Header, parentBaseUrl: string, baseUrlQuery: PaginationQueryParams, pagination: Pagination): HeaderView {
|
||||
return {
|
||||
label: header.label,
|
||||
sortLink: !pagination ? null : setQueryParameters(parentBaseUrl, { ...baseUrlQuery, 'order_by': header.name, 'order_dir': headerNextOrder(header.name, pagination) }),
|
||||
sortLink: setQueryParameters(parentBaseUrl, { ...baseUrlQuery, 'order_by': header.name, 'order_dir': headerNextOrder(header.name, pagination) }),
|
||||
classNames: [header.stretch ? 'stretch' : 'nowrap', headerIsSelectedClass(header.name, pagination)],
|
||||
iconDir: headerSortIconDir(header.name, pagination),
|
||||
};
|
||||
@@ -93,21 +89,14 @@ function makeRowView(row: Row): RowView {
|
||||
value: rowItem.value,
|
||||
classNames: [rowItem.stretch ? 'stretch' : 'nowrap'],
|
||||
url: rowItem.url,
|
||||
checkbox: rowItem.checkbox,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function makeTableView(table: Table): TableView {
|
||||
let paginationLinks: PageLink[] = [];
|
||||
let baseUrlQuery: PaginationQueryParams = null;
|
||||
let pagination: Pagination = null;
|
||||
|
||||
if (table.pageCount) {
|
||||
baseUrlQuery = filterPaginationQueryParams(table.requestQuery);
|
||||
pagination = table.pagination;
|
||||
paginationLinks = createPaginationLinks(pagination.page, table.pageCount, setQueryParameters(table.baseUrl, { ...baseUrlQuery, 'page': 'PAGE_NUMBER' }));
|
||||
}
|
||||
const baseUrlQuery = filterPaginationQueryParams(table.requestQuery);
|
||||
const pagination = table.pagination;
|
||||
const paginationLinks = createPaginationLinks(pagination.page, table.pageCount, setQueryParameters(table.baseUrl, { ...baseUrlQuery, 'page': 'PAGE_NUMBER' }));
|
||||
|
||||
return {
|
||||
headers: table.headers.map(h => makeHeaderView(h, table.baseUrl, baseUrlQuery, pagination)),
|
||||
|
||||
@@ -4,20 +4,16 @@
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<form method='POST' action="{{postUrl}}">
|
||||
{{{csrfTag}}}
|
||||
|
||||
{{#itemTable}}
|
||||
{{>table}}
|
||||
{{/itemTable}}
|
||||
|
||||
<div class="block">
|
||||
<input class="button is-link" type="submit" value="Start selected tasks" name="startTaskButton"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -131,9 +131,6 @@
|
||||
{{#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" />
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
{{/global.owner.is_admin}}
|
||||
<a class="navbar-item" href="{{{global.baseUrl}}}/items">Items</a>
|
||||
<a class="navbar-item" href="{{{global.baseUrl}}}/changes">Log</a>
|
||||
{{#global.owner.is_admin}}
|
||||
<a class="navbar-item" href="{{{global.baseUrl}}}/tasks">Tasks</a>
|
||||
{{/global.owner.is_admin}}
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
{{#global.isJoplinCloud}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{#global.hasNotifications}}
|
||||
{{#global.notifications}}
|
||||
<div class="notification {{levelClassName}}" id="notification-{{id}}">
|
||||
<div class="notification is-{{level}}" id="notification-{{id}}">
|
||||
<button data-close-url="{{closeUrl}}" data-id="{{id}}" class="delete close-notification-button"></button>
|
||||
{{{messageHtml}}}
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-hoverable ">
|
||||
<thead>
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
{{#headers}}
|
||||
{{>tableHeader}}
|
||||
{{/headers}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#rows}}
|
||||
<tr>
|
||||
{{#headers}}
|
||||
{{>tableHeader}}
|
||||
{{/headers}}
|
||||
{{#.}}
|
||||
{{>tableRowItem}}
|
||||
{{/.}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#rows}}
|
||||
<tr>
|
||||
{{#.}}
|
||||
{{>tableRowItem}}
|
||||
{{/.}}
|
||||
</tr>
|
||||
{{/rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{>pagination}}
|
||||
@@ -1,8 +1,3 @@
|
||||
<th class="{{#classNames}}{{.}} {{/classNames}}">
|
||||
{{#sortLink}}
|
||||
<a href="{{sortLink}}" class="sort-button">{{label}} <i class="fas fa-caret-{{iconDir}}"></i></a>
|
||||
{{/sortLink}}
|
||||
{{^sortLink}}
|
||||
{{label}}
|
||||
{{/sortLink}}
|
||||
<a href="{{sortLink}}" class="sort-button">{{label}} <i class="fas fa-caret-{{iconDir}}"></i></a>
|
||||
</th>
|
||||
@@ -1,8 +1,3 @@
|
||||
<td class="{{#classNames}}{{.}} {{/classNames}}">
|
||||
{{#checkbox}}
|
||||
<input type="checkbox" name="{{value}}"/>
|
||||
{{/checkbox}}
|
||||
{{^checkbox}}
|
||||
{{#url}}<a href="{{.}}"></span>{{/url}}{{value}}</a>
|
||||
{{/checkbox}}
|
||||
{{#url}}<a href="{{.}}"></span>{{/url}}{{value}}</a>
|
||||
</td>
|
||||
5
packages/style.min.css
vendored
5
packages/style.min.css
vendored
@@ -1,5 +0,0 @@
|
||||
.encryption-config-test > .item {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=style.min.css.map */
|
||||
@@ -1,6 +1,8 @@
|
||||
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('-');
|
||||
@@ -12,6 +14,10 @@ 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');
|
||||
@@ -27,7 +33,11 @@ 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');
|
||||
@@ -44,10 +54,48 @@ async function main() {
|
||||
console.info('isPreRelease:', isPreRelease);
|
||||
console.info('Docker tags:', dockerTags.join(', '));
|
||||
|
||||
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}`);
|
||||
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}`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
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
@@ -7,8 +7,6 @@ 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"
|
||||
@@ -16,7 +14,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 3.0\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
|
||||
#: packages/app-cli/app/app-gui.js:452
|
||||
msgid "To delete a tag, untag the associated notes."
|
||||
@@ -135,7 +133,6 @@ 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."
|
||||
@@ -602,8 +599,8 @@ msgid ""
|
||||
"\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
"Asettaa ominaisuuden <name> annetusta <note> annettuun [arvoon]. Mahdolliset "
|
||||
"ominaisuudet ovat:\n"
|
||||
"Sets the property <name> of the given <note> to the given [value]. Possible "
|
||||
"properties are:\n"
|
||||
"\n"
|
||||
"%s"
|
||||
|
||||
@@ -650,7 +647,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
|
||||
@@ -911,11 +908,11 @@ msgstr "Lataa"
|
||||
|
||||
#: packages/app-desktop/checkForUpdates.js:189
|
||||
msgid "Skip this version"
|
||||
msgstr "Ohita tämä versio"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/checkForUpdates.js:189
|
||||
msgid "Full changelog"
|
||||
msgstr "Täysi muutosloki"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/commands/copyDevCommand.js:18
|
||||
msgid "Copy dev mode command to clipboard"
|
||||
@@ -942,8 +939,9 @@ msgid "Stop"
|
||||
msgstr "Seis"
|
||||
|
||||
#: packages/app-desktop/commands/toggleSafeMode.js:18
|
||||
#, fuzzy
|
||||
msgid "Toggle safe mode"
|
||||
msgstr "Vaihda turvalliseen tilaan"
|
||||
msgstr "Näytä sivupalkki"
|
||||
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.js:34
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:40
|
||||
@@ -953,7 +951,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 "Haluatko varmasti uusia valtuutustunnuksen?"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.js:67
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:84
|
||||
@@ -1055,7 +1053,7 @@ msgstr ""
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.js:111
|
||||
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:222
|
||||
msgid "Renew token"
|
||||
msgstr "Uusi tunnus"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/ButtonBar.js:27
|
||||
msgid "Apply"
|
||||
@@ -1181,12 +1179,13 @@ 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 "Yhteyden muodostaminen laajennusten arkistoon epäonnistui"
|
||||
msgstr "Laajennuksen asentaminen epäonnistui: %s"
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js:234
|
||||
msgid "Try again"
|
||||
msgstr "Yritä uudestaan"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js:242
|
||||
msgid "Plugin tools"
|
||||
@@ -1220,16 +1219,19 @@ 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: "
|
||||
msgstr "Luotu: %s"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:79
|
||||
#, fuzzy
|
||||
msgid "Updated: "
|
||||
msgstr "Päivitetty: "
|
||||
msgstr "Päivitetty: %s"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:84
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:96
|
||||
@@ -1239,8 +1241,9 @@ msgid "Save"
|
||||
msgstr "Tallenna"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:87
|
||||
#, fuzzy
|
||||
msgid "Disable"
|
||||
msgstr "Poista käytöstä"
|
||||
msgstr "Poistettu käytöstä"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:87
|
||||
#: packages/app-mobile/components/screens/encryption-config.js:157
|
||||
@@ -1352,11 +1355,11 @@ msgstr "Pääavaimet"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:142
|
||||
msgid "Hide disabled master keys"
|
||||
msgstr "Piilota käytöstä poistetut pääavaimet"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:142
|
||||
msgid "Show disabled master keys"
|
||||
msgstr "Näytä käytöstä poistetut pääavaimet"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:143
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:343
|
||||
@@ -1377,7 +1380,7 @@ msgstr "Aktiivinen"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:149
|
||||
msgid "Date"
|
||||
msgstr "Päivämäärä"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:150
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:329
|
||||
@@ -1385,12 +1388,14 @@ msgid "Password"
|
||||
msgstr "Salasana"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:151
|
||||
#, fuzzy
|
||||
msgid "Valid"
|
||||
msgstr "Kelvollinen"
|
||||
msgstr "Virheellinen"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:152
|
||||
#, fuzzy
|
||||
msgid "Actions"
|
||||
msgstr "Toiminnot"
|
||||
msgstr "Toiminta"
|
||||
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.js:182
|
||||
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:244
|
||||
@@ -1570,12 +1575,12 @@ msgstr "Sulje ikkuna"
|
||||
|
||||
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:24
|
||||
msgid "Preferences"
|
||||
msgstr "Oletusasetukset"
|
||||
msgstr "Asetukset"
|
||||
|
||||
#: 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 "Asetukset"
|
||||
msgstr "Vaihtoehdot"
|
||||
|
||||
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:31
|
||||
msgid "Invalid"
|
||||
@@ -1586,12 +1591,10 @@ 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 "Lopeta turvallinen tila ja käynnistä uudelleen"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/MainScreen.js:473
|
||||
msgid ""
|
||||
@@ -1635,16 +1638,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 "%s (%s) haluaa jakaa muistikirjan kanssasi."
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/MainScreen.js:487
|
||||
msgid "Accept"
|
||||
msgstr "Hyväksy"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/MainScreen.js:487
|
||||
#: packages/app-desktop/gui/Root.js:121
|
||||
msgid "Reject"
|
||||
msgstr "Hylkää"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/MainScreen.js:490
|
||||
msgid "Some items cannot be synchronised."
|
||||
@@ -1663,8 +1666,9 @@ 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
|
||||
@@ -1679,7 +1683,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
|
||||
@@ -1762,12 +1766,14 @@ msgid "Note properties"
|
||||
msgstr "Muistiinpanon ominaisuudet"
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/showShareFolderDialog.js:16
|
||||
#, fuzzy
|
||||
msgid "Share notebook..."
|
||||
msgstr "Muistikirjan jakaminen..."
|
||||
msgstr "Muistiinpanon jakaminen..."
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/showShareNoteDialog.js:16
|
||||
#, fuzzy
|
||||
msgid "Publish note..."
|
||||
msgstr "Muistiinpanon julkaiseminen..."
|
||||
msgstr "Muistiinpanon jakaminen..."
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js:19
|
||||
#: packages/lib/services/spellChecker/SpellCheckerService.js:180
|
||||
@@ -1827,7 +1833,7 @@ msgstr "Joplinista"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:367
|
||||
msgid "Preferences..."
|
||||
msgstr "Oletusasetukset..."
|
||||
msgstr "Asetukset..."
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:377
|
||||
#: packages/app-desktop/gui/MenuBar.js:618
|
||||
@@ -1855,7 +1861,7 @@ msgstr "&Näytä"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:506
|
||||
msgid "Layout button sequence"
|
||||
msgstr "Aseta painike järjestys"
|
||||
msgstr "Asettele painike järjestys"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:551
|
||||
#: packages/app-desktop/gui/MenuBar.js:557
|
||||
@@ -1876,11 +1882,12 @@ msgstr "&Mene"
|
||||
#: packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js:18
|
||||
#: packages/app-desktop/gui/Sidebar/commands/focusElementSideBar.js:18
|
||||
msgid "Focus"
|
||||
msgstr "Osoita"
|
||||
msgstr "Keskittää"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:585
|
||||
#, fuzzy
|
||||
msgid "Note&book"
|
||||
msgstr "&Muistikirjat"
|
||||
msgstr "Muistikirjat"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.js:591
|
||||
msgid "&Note"
|
||||
@@ -2042,23 +2049,23 @@ msgstr "Muokkaa"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:17
|
||||
msgid "Highlight"
|
||||
msgstr "Korosta"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:22
|
||||
msgid "Strikethrough"
|
||||
msgstr "Yliviivaus"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:27
|
||||
msgid "Insert"
|
||||
msgstr "Aseta"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:33
|
||||
msgid "Superscript"
|
||||
msgstr "Yläindeksi"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js:39
|
||||
msgid "Subscript"
|
||||
msgstr "Alaindeksi"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:285
|
||||
msgid "Click to add tags..."
|
||||
@@ -2155,8 +2162,9 @@ msgid "Delete line"
|
||||
msgstr "Poista rivi"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:92
|
||||
#, fuzzy
|
||||
msgid "Duplicate line"
|
||||
msgstr "Rivin kaksoiskappale"
|
||||
msgstr "Duplikaatti, toinen samanlainen"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:96
|
||||
msgid "Undo"
|
||||
@@ -2364,16 +2372,18 @@ msgstr ""
|
||||
"Varoitus: kaikkia resursseja ei näytetä suorituskyvyn vuoksi (raja: %s)."
|
||||
|
||||
#: packages/app-desktop/gui/Root.js:106
|
||||
#, fuzzy
|
||||
msgid "Confirmation"
|
||||
msgstr "Vahvistus"
|
||||
msgstr "Konfigurointi"
|
||||
|
||||
#: packages/app-desktop/gui/Root.js:119
|
||||
msgid "The Web Clipper needs your authorisation to access your data."
|
||||
msgstr "Web Clipper tarvitsee valtuutuksen tietojen käyttöön."
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/Root.js:120
|
||||
#, fuzzy
|
||||
msgid "Grant authorisation"
|
||||
msgstr "Myönnä lupa"
|
||||
msgstr "Valtuutuksen tunnus:"
|
||||
|
||||
#: packages/app-desktop/gui/Root.js:160
|
||||
msgid "OneDrive Login"
|
||||
@@ -2388,20 +2398,19 @@ msgid "Note attachments"
|
||||
msgstr "Muistiinpanon liitteet"
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:141
|
||||
#, fuzzy
|
||||
msgid "Unshare"
|
||||
msgstr "Poista jakaminen"
|
||||
msgstr "Jaa"
|
||||
|
||||
#: 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 "Lisää vastaanottaja:"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:200
|
||||
#: packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
|
||||
@@ -2411,43 +2420,45 @@ msgstr "Jaa"
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:209
|
||||
msgid "Recipient has not yet accepted the invitation"
|
||||
msgstr "Vastaanottaja ei ole vielä hyväksynyt kutsua"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:210
|
||||
msgid "Recipient has rejected the invitation"
|
||||
msgstr "Vastaanottaja on hylännyt kutsun"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:211
|
||||
msgid "Recipient has accepted the invitation"
|
||||
msgstr "Vastaanottaja on hyväksynyt kutsun"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.js:221
|
||||
msgid "Recipients:"
|
||||
msgstr "Vastaanottajat:"
|
||||
msgstr ""
|
||||
|
||||
#: 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 "Jaetaan muistikirjaa..."
|
||||
msgstr "Muistiinpanon jakaminen..."
|
||||
|
||||
#: 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 muistikirja"
|
||||
msgstr "Jaa muistiinpanoja"
|
||||
|
||||
#: packages/app-desktop/gui/ShareNoteDialog.js:144
|
||||
#, fuzzy
|
||||
msgid "Unpublish note"
|
||||
msgstr "Peruuta muistiinpanon julkaisu"
|
||||
msgstr "Jaa"
|
||||
|
||||
#: packages/app-desktop/gui/ShareNoteDialog.js:171
|
||||
msgid "Synchronising..."
|
||||
@@ -2473,7 +2484,7 @@ msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareNoteDialog.js:187
|
||||
msgid "Publish Notes"
|
||||
msgstr "Julkaise muistiinpanot"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/ShareNoteDialog.js:189
|
||||
msgid "Copy Shareable Link"
|
||||
@@ -2554,28 +2565,32 @@ msgid "Retry"
|
||||
msgstr "Yritä uudelleen"
|
||||
|
||||
#: packages/app-desktop/gui/StatusScreen/StatusScreen.js:137
|
||||
#, fuzzy
|
||||
msgid "Advanced tools"
|
||||
msgstr "Lisätyökalut"
|
||||
msgstr "Lisäasetukset"
|
||||
|
||||
#: 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 "Synkronoi muistiinpanosi"
|
||||
msgstr "Lajittele muistiinpanot"
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:158
|
||||
msgid "Publish notes to the internet"
|
||||
msgstr "Julkaise muistiinpanot Internetissä"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:159
|
||||
#, fuzzy
|
||||
msgid "Collaborate on notebooks with others"
|
||||
msgstr "Muistikirjojen yhteiskäyttö muiden kanssa"
|
||||
msgstr "Luo ensin muistikirja"
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:182
|
||||
msgid "Thank you! Your Joplin Cloud account is now setup and ready to use."
|
||||
msgstr "Kiitos! Your Joplin Cloud tilisi on nyt määritetty ja käyttövalmis."
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:190
|
||||
#, javascript-format
|
||||
@@ -2585,34 +2600,30 @@ 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 "Kirjaudu sisään alla."
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:205
|
||||
#, fuzzy
|
||||
msgid "Or create an account."
|
||||
msgstr "Tai luo tili."
|
||||
msgstr "Luo uuden muistiinpanon."
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:210
|
||||
msgid "Login"
|
||||
msgstr "Kirjaudu"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.js:231
|
||||
#, fuzzy
|
||||
msgid "Select"
|
||||
msgstr "Valitse"
|
||||
msgstr "Valitse kaikki"
|
||||
|
||||
#: 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"
|
||||
@@ -2633,7 +2644,7 @@ msgstr "Vaihda muistiinpanotyyppiin"
|
||||
|
||||
#: packages/app-desktop/gui/utils/NoteListUtils.js:93
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Vaihda tehtävätyyppiin"
|
||||
msgstr "Siirry tehtävätyyppiin"
|
||||
|
||||
#: packages/app-desktop/gui/utils/NoteListUtils.js:100
|
||||
#: packages/app-mobile/components/screens/Note.js:847
|
||||
@@ -2786,7 +2797,7 @@ msgstr "Vain virheenkorjausta varten: vie profiilisi ulkoiselle SD-kortille."
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.js:436
|
||||
msgid "Feature flags"
|
||||
msgstr "Ominaisuus liput"
|
||||
msgstr ""
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.js:439
|
||||
msgid "More information"
|
||||
@@ -2798,8 +2809,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> Luvat "
|
||||
"(käyttöoikeudet)"
|
||||
"käyttöön puhelimesi asetuksissa, valitsemalla Sovellukset> Joplin> "
|
||||
"Käyttöoikeudet"
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.js:446
|
||||
msgid ""
|
||||
@@ -2811,11 +2822,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: sallia kuvan ottamisen ja liittämisen muistiinpanoon."
|
||||
msgstr "- Kamera: sallii 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: sallia paikkatietojen liittämisen muistiinpanoon."
|
||||
msgstr "- Sijainti: sallii paikkatietojen liittämisen muistiinpanoon."
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.js:459
|
||||
msgid "Joplin website"
|
||||
@@ -2920,7 +2931,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..."
|
||||
@@ -3037,7 +3048,7 @@ msgstr "Uusi muistikirja"
|
||||
|
||||
#: packages/app-mobile/components/side-menu-content.js:351
|
||||
msgid "Mobile data - auto-sync disabled"
|
||||
msgstr "Mobiilidata - automaattinen synkronointi poistettu käytöstä"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/BaseApplication.js:154 packages/lib/BaseApplication.js:166
|
||||
#: packages/lib/BaseApplication.js:198
|
||||
@@ -3059,7 +3070,7 @@ msgid ""
|
||||
"%s"
|
||||
msgstr ""
|
||||
"Yhteyden muodostaminen Joplinin palvelimelle epäonnistui. Tarkista "
|
||||
"synkronoinnin asetukset. Koko virhe oli:\n"
|
||||
"vaihtoehdot Synkronointiasetukset-näytössä. Koko virhe oli:\n"
|
||||
"\n"
|
||||
"%s"
|
||||
|
||||
@@ -3076,17 +3087,15 @@ msgid "File system"
|
||||
msgstr "Tiedostojärjestelmä"
|
||||
|
||||
#: packages/lib/SyncTargetJoplinCloud.js:28
|
||||
#, fuzzy
|
||||
msgid "Joplin Cloud"
|
||||
msgstr "Joplin Cloud"
|
||||
msgstr "Joplin Foorumi"
|
||||
|
||||
#: 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"
|
||||
@@ -3098,7 +3107,7 @@ msgstr "Nextcloud"
|
||||
|
||||
#: packages/lib/SyncTargetNone.js:22
|
||||
msgid "(None)"
|
||||
msgstr "(Ei mitään)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/SyncTargetOneDrive.js:32
|
||||
msgid "OneDrive"
|
||||
@@ -3148,9 +3157,9 @@ msgid "Cancelling..."
|
||||
msgstr "Peruutetaan..."
|
||||
|
||||
#: packages/lib/Synchronizer.js:159
|
||||
#, javascript-format
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Completed: %s (%s)"
|
||||
msgstr "Valmis: %s (%s)"
|
||||
msgstr "Valmis: %s"
|
||||
|
||||
#: packages/lib/Synchronizer.js:161
|
||||
#, javascript-format
|
||||
@@ -3187,8 +3196,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 "
|
||||
"synkronointi kohde on käytettävissä. Raportoitu virhe oli:"
|
||||
"Virhe. Tarkista URL-osoite, käyttäjänimi, salasana jne. ovat oikeita ja "
|
||||
"synkronointikohde on käytettävissä. Raportoitu virhe oli:"
|
||||
|
||||
#: packages/lib/components/shared/dropbox-login-shared.js:39
|
||||
msgid "The application has been authorised!"
|
||||
@@ -3243,7 +3252,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 "Salaus otetaan käyttöön käyttämällä luotua pääavainta %s"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/BaseItem.js:721
|
||||
msgid "Encrypted"
|
||||
@@ -3313,8 +3322,9 @@ msgid "Error"
|
||||
msgstr "Virhe"
|
||||
|
||||
#: packages/lib/models/Resource.js:408
|
||||
#, fuzzy
|
||||
msgid "Conflicts (attachments)"
|
||||
msgstr "Ristiriidat (liitteet)"
|
||||
msgstr "Muistiinpanon liitteet"
|
||||
|
||||
#: packages/lib/models/Resource.js:422
|
||||
#, javascript-format
|
||||
@@ -3390,7 +3400,7 @@ msgstr "OLED Dark"
|
||||
|
||||
#: packages/lib/models/Setting.js:152
|
||||
msgid "Open Sync Wizard..."
|
||||
msgstr "Avaa ohjattu synkronointitoiminto."
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:162
|
||||
msgid "Synchronisation target"
|
||||
@@ -3454,20 +3464,23 @@ msgid "Joplin Server URL"
|
||||
msgstr "Joplin palvelimen URL-osoite"
|
||||
|
||||
#: packages/lib/models/Setting.js:348
|
||||
#, fuzzy
|
||||
msgid "Joplin Server email"
|
||||
msgstr "Joplin palvelimen sähköposti"
|
||||
msgstr "Joplin Palvelin"
|
||||
|
||||
#: 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 Cloud sähköposti"
|
||||
msgstr "Joplin Palvelin"
|
||||
|
||||
#: packages/lib/models/Setting.js:397
|
||||
#, fuzzy
|
||||
msgid "Joplin Cloud password"
|
||||
msgstr "Joplin Cloud salasana"
|
||||
msgstr "Joplin palvelimen salasana"
|
||||
|
||||
#: packages/lib/models/Setting.js:409
|
||||
msgid "Attachment download behaviour"
|
||||
@@ -3479,8 +3492,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\" "
|
||||
"valinnassa ne ladataan, kun avaat muistiinpanon. \"Aina\" valinnassa kaikki "
|
||||
"Manuaalisessa tilassa liitteet ladataan vain, kun napsautat niitä. \"Auto\" -"
|
||||
"kohdassa ne ladataan, kun avaat muistiinpanon. \"Aina\" -kohdassa kaikki "
|
||||
"liitteet ladataan riippumatta siitä, avaatko muistiinpanon vai et."
|
||||
|
||||
#: packages/lib/models/Setting.js:413
|
||||
@@ -3707,12 +3720,11 @@ 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 monospace fonttiperhe"
|
||||
msgstr "Editorin fonttiperhe"
|
||||
|
||||
#: packages/lib/models/Setting.js:780
|
||||
msgid ""
|
||||
@@ -3720,17 +3732,14 @@ 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 "Editorin enimmäisleveys"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:783
|
||||
msgid "Set it to 0 to make it take the complete available space."
|
||||
msgstr "Aseta arvoksi 0, jotta se vie koko käytettävissä olevan tilan."
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:802
|
||||
msgid "Custom stylesheet for rendered Markdown"
|
||||
@@ -3742,11 +3751,11 @@ msgstr "Mukautettu tyylitaulukko Joplinin sovellustyyleille"
|
||||
|
||||
#: packages/lib/models/Setting.js:828
|
||||
msgid "Re-upload local data to sync target"
|
||||
msgstr "Lataa paikalliset tiedot uudelleen kohteen synkronointia varten"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:838
|
||||
msgid "Delete local data and re-download from sync target"
|
||||
msgstr "Poista paikalliset tiedot ja lataa uudelleen synkronointikohteesta"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:843
|
||||
msgid "Automatically update the application"
|
||||
@@ -3783,7 +3792,7 @@ msgstr "%d tuntia"
|
||||
|
||||
#: packages/lib/models/Setting.js:871
|
||||
msgid "Synchronise only over WiFi connection"
|
||||
msgstr "Synkronoi vain WiFi yhteyden kautta"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.js:878
|
||||
msgid "Text editor command"
|
||||
@@ -3800,7 +3809,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"
|
||||
@@ -3828,15 +3837,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 "Pystysuunta"
|
||||
msgstr "Pysty"
|
||||
|
||||
#: packages/lib/models/Setting.js:892
|
||||
msgid "Landscape"
|
||||
msgstr "Vaakasuunta"
|
||||
msgstr "Vaaka"
|
||||
|
||||
#: packages/lib/models/Setting.js:902
|
||||
msgid "Keyboard Mode"
|
||||
@@ -3852,11 +3861,11 @@ msgstr "Vim"
|
||||
|
||||
#: packages/lib/models/Setting.js:920
|
||||
msgid "Do not resize images"
|
||||
msgstr "Älä muuta kuvien kokoa"
|
||||
msgstr ""
|
||||
|
||||
#: 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 ""
|
||||
@@ -3867,13 +3876,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"
|
||||
@@ -3884,15 +3893,15 @@ msgid ""
|
||||
"Fail-safe: Do not wipe out local data when sync target is empty (often the "
|
||||
"result of a misconfiguration or bug)"
|
||||
msgstr ""
|
||||
"Vikaturvallinen: Älä pyyhi paikallisia tietoja, kun synkronointikohde on "
|
||||
"tyhjä (usein virheellisen määritysvirheen tai virheen seurauksena)"
|
||||
"Vikasietoinen: Ä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
|
||||
@@ -3924,10 +3933,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"
|
||||
@@ -4001,7 +4010,7 @@ msgstr ""
|
||||
#: packages/lib/models/Setting.js:1709
|
||||
#, javascript-format
|
||||
msgid "Notes and settings are stored in: %s"
|
||||
msgstr "Muistiinpanot ja oletusasetukset tallennetaan: %s"
|
||||
msgstr "Muistiinpanot ja asetukset tallennetaan: %s"
|
||||
|
||||
#: packages/lib/models/Tag.js:223
|
||||
#, javascript-format
|
||||
@@ -4204,28 +4213,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
|
||||
@@ -4315,19 +4324,19 @@ msgid ""
|
||||
"The default admin password is insecure and has not been changed! [Change it "
|
||||
"now](%s)"
|
||||
msgstr ""
|
||||
"Järjestelmänvalvojan oletussalasana on turvaton, eikä sitä ole muutettu "
|
||||
"[Vaihda se nyt](%s)"
|
||||
"Järjestelmänvalvojan oletussalasana on epävarma, 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 "liite"
|
||||
msgstr "Liitteet"
|
||||
|
||||
#: 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
|
||||
@@ -4335,8 +4344,6 @@ 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)"
|
||||
@@ -4363,8 +4370,8 @@ msgstr ""
|
||||
#~ msgid "Templates"
|
||||
#~ msgstr "Mallit"
|
||||
|
||||
#~ msgid "Full Release Notes"
|
||||
#~ msgstr "Täydelliset julkaisutiedot"
|
||||
#~ msgid "Share Notes"
|
||||
#~ msgstr "Jaa muistiinpanoja"
|
||||
|
||||
#~ msgid "Joplin Server Directory"
|
||||
#~ msgstr "Joplin palvelimen hakemisto"
|
||||
@@ -4372,11 +4379,25 @@ 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 ""
|
||||
#~ "Jos fontti on väärä tai tyhjä, se on oletuksena yleinen monospace fontti."
|
||||
#~ "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."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "This should be a *monospace* font or some elements will render "
|
||||
|
||||
284
packages/tools/package-lock.json
generated
284
packages/tools/package-lock.json
generated
@@ -6,7 +6,7 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@joplin/tools",
|
||||
"version": "2.4.1",
|
||||
"version": "2.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"execa": "^4.1.0",
|
||||
@@ -31,7 +31,6 @@
|
||||
"@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"
|
||||
}
|
||||
@@ -4198,18 +4197,6 @@
|
||||
"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",
|
||||
@@ -4667,159 +4654,6 @@
|
||||
"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",
|
||||
@@ -9437,12 +9271,6 @@
|
||||
"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",
|
||||
@@ -9814,116 +9642,6 @@
|
||||
"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",
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
"@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"
|
||||
},
|
||||
|
||||
@@ -79,14 +79,6 @@
|
||||
{
|
||||
"name": "cuongtransc",
|
||||
"id": "808091"
|
||||
},
|
||||
{
|
||||
"name": "iamwillbar",
|
||||
"id": "3266447"
|
||||
},
|
||||
{
|
||||
"name": "marcdw1289",
|
||||
"id": "42319182"
|
||||
}
|
||||
],
|
||||
"orgs": [
|
||||
|
||||
@@ -147,6 +147,7 @@ export function execCommandVerbose(commandName: string, args: string[] = []) {
|
||||
interface ExecCommandOptions {
|
||||
showInput?: boolean;
|
||||
showOutput?: boolean;
|
||||
showError?: boolean;
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
@@ -160,6 +161,7 @@ export async function execCommand2(command: string | string[], options: ExecComm
|
||||
options = {
|
||||
showInput: true,
|
||||
showOutput: true,
|
||||
showError: true,
|
||||
quiet: false,
|
||||
...options,
|
||||
};
|
||||
@@ -167,6 +169,7 @@ export async function execCommand2(command: string | string[], options: ExecComm
|
||||
if (options.quiet) {
|
||||
options.showInput = false;
|
||||
options.showOutput = false;
|
||||
options.showError = false;
|
||||
}
|
||||
|
||||
if (options.showInput) {
|
||||
@@ -182,6 +185,7 @@ 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();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# 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)
|
||||
|
||||
Reference in New Issue
Block a user