mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 19:36:12 +01:00
Compare commits
9 Commits
v0.60.2-be
...
v0.60.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e22f77eae7 | ||
|
|
3223e76787 | ||
|
|
74400dad97 | ||
|
|
1b68adf3e4 | ||
|
|
bea39f37ee | ||
|
|
6548149107 | ||
|
|
6015a067ec | ||
|
|
4a1ecd906b | ||
|
|
004cfe1965 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"version": "0.60.1-beta",
|
"version": "0.60.2-beta",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"version": "0.60.1-beta",
|
"version": "0.60.2-beta",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.60.2-beta",
|
"version": "0.60.3",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
14
src/etapi/backup.js
Normal file
14
src/etapi/backup.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const eu = require("./etapi_utils");
|
||||||
|
const backupService = require("../services/backup");
|
||||||
|
|
||||||
|
function register(router) {
|
||||||
|
eu.route(router, 'put', '/etapi/backup/:backupName', async (req, res, next) => {
|
||||||
|
await backupService.backupNow(req.params.backupName);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
register
|
||||||
|
};
|
||||||
@@ -33,13 +33,7 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json; charset=utf-8:
|
application/json; charset=utf-8:
|
||||||
schema:
|
schema:
|
||||||
properties:
|
$ref: '#/components/schemas/NoteWithBranch'
|
||||||
note:
|
|
||||||
$ref: '#/components/schemas/Note'
|
|
||||||
description: Created note
|
|
||||||
branch:
|
|
||||||
$ref: '#/components/schemas/Branch'
|
|
||||||
description: Created branch
|
|
||||||
default:
|
default:
|
||||||
description: unexpected error
|
description: unexpected error
|
||||||
content:
|
content:
|
||||||
@@ -291,6 +285,29 @@ paths:
|
|||||||
application/json; charset=utf-8:
|
application/json; charset=utf-8:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Error'
|
$ref: '#/components/schemas/Error'
|
||||||
|
/notes/{noteId}/import:
|
||||||
|
parameters:
|
||||||
|
- name: noteId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EntityId'
|
||||||
|
post:
|
||||||
|
description: Imports ZIP file into a given note.
|
||||||
|
operationId: importZip
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: note created
|
||||||
|
content:
|
||||||
|
application/json; charset=utf-8:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NoteWithBranch'
|
||||||
|
default:
|
||||||
|
description: unexpected error
|
||||||
|
content:
|
||||||
|
application/json; charset=utf-8:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
/notes/{noteId}/note-revision:
|
/notes/{noteId}/note-revision:
|
||||||
parameters:
|
parameters:
|
||||||
- name: noteId
|
- name: noteId
|
||||||
@@ -700,7 +717,26 @@ paths:
|
|||||||
application/json; charset=utf-8:
|
application/json; charset=utf-8:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Error'
|
$ref: '#/components/schemas/Error'
|
||||||
|
/backup/{backupName}:
|
||||||
|
parameters:
|
||||||
|
- name: backupName
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: If the backupName is e.g. "now", then the backup will be written to "backup-now.db" file
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/StringId'
|
||||||
|
put:
|
||||||
|
description: Create a database backup under a given name
|
||||||
|
operationId: createBackup
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: backup has been created
|
||||||
|
default:
|
||||||
|
description: unexpected error
|
||||||
|
content:
|
||||||
|
application/json; charset=utf-8:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
EtapiTokenAuth:
|
EtapiTokenAuth:
|
||||||
@@ -833,6 +869,13 @@ components:
|
|||||||
utcDateModified:
|
utcDateModified:
|
||||||
$ref: '#/components/schemas/UtcDateTime'
|
$ref: '#/components/schemas/UtcDateTime'
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
NoteWithBranch:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
note:
|
||||||
|
$ref: '#/components/schemas/Note'
|
||||||
|
branch:
|
||||||
|
$ref: '#/components/schemas/Branch'
|
||||||
Attribute:
|
Attribute:
|
||||||
type: object
|
type: object
|
||||||
description: Attribute (Label, Relation) is a key-value record attached to a note.
|
description: Attribute (Label, Relation) is a key-value record attached to a note.
|
||||||
@@ -880,6 +923,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
pattern: '[a-zA-Z0-9_]{4,32}'
|
pattern: '[a-zA-Z0-9_]{4,32}'
|
||||||
example: evnnmvHTCgIn
|
example: evnnmvHTCgIn
|
||||||
|
StringId:
|
||||||
|
type: string
|
||||||
|
pattern: '[a-zA-Z0-9_]{1,32}'
|
||||||
|
example: my_ID
|
||||||
EntityIdList:
|
EntityIdList:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const v = require("./validators");
|
|||||||
const searchService = require("../services/search/services/search");
|
const searchService = require("../services/search/services/search");
|
||||||
const SearchContext = require("../services/search/search_context");
|
const SearchContext = require("../services/search/search_context");
|
||||||
const zipExportService = require("../services/export/zip");
|
const zipExportService = require("../services/export/zip");
|
||||||
|
const zipImportService = require("../services/import/zip");
|
||||||
|
|
||||||
function register(router) {
|
function register(router) {
|
||||||
eu.route(router, 'get', '/etapi/notes', (req, res, next) => {
|
eu.route(router, 'get', '/etapi/notes', (req, res, next) => {
|
||||||
@@ -141,11 +142,21 @@ function register(router) {
|
|||||||
// (e.g. branchIds are not seen in UI), that we export "note export" instead.
|
// (e.g. branchIds are not seen in UI), that we export "note export" instead.
|
||||||
const branch = note.getParentBranches()[0];
|
const branch = note.getParentBranches()[0];
|
||||||
|
|
||||||
console.log(note.getParentBranches());
|
|
||||||
|
|
||||||
zipExportService.exportToZip(taskContext, branch, format, res);
|
zipExportService.exportToZip(taskContext, branch, format, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
eu.route(router, 'post' ,'/etapi/notes/:noteId/import', (req, res, next) => {
|
||||||
|
const note = eu.getAndCheckNote(req.params.noteId);
|
||||||
|
const taskContext = new TaskContext('no-progress-reporting');
|
||||||
|
|
||||||
|
zipImportService.importZip(taskContext, req.body, note).then(importedNote => {
|
||||||
|
res.status(201).json({
|
||||||
|
note: mappers.mapNoteToPojo(importedNote),
|
||||||
|
branch: mappers.mapBranchToPojo(importedNote.getBranches()[0]),
|
||||||
|
});
|
||||||
|
}); // we need better error handling here, async errors won't be properly processed.
|
||||||
|
});
|
||||||
|
|
||||||
eu.route(router, 'post' ,'/etapi/notes/:noteId/note-revision', (req, res, next) => {
|
eu.route(router, 'post' ,'/etapi/notes/:noteId/note-revision', (req, res, next) => {
|
||||||
const note = eu.getAndCheckNote(req.params.noteId);
|
const note = eu.getAndCheckNote(req.params.noteId);
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export default class Entrypoints extends Component {
|
|||||||
const resp = await server.post(`sql/execute/${note.noteId}`);
|
const resp = await server.post(`sql/execute/${note.noteId}`);
|
||||||
|
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
toastService.showError(`Error occurred while executing SQL query: ${resp.message}`);
|
toastService.showError(`Error occurred while executing SQL query: ${resp.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: resp.results});
|
await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: resp.results});
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runActiveNoteCommand(params) {
|
||||||
|
if (this.isNoteContext(params.ntxId)) {
|
||||||
|
// make sure that script is saved before running it #4028
|
||||||
|
await this.spacedUpdate.updateNowIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.parent.triggerCommand('runActiveNote', params);
|
||||||
|
}
|
||||||
|
|
||||||
async printActiveNoteEvent() {
|
async printActiveNoteEvent() {
|
||||||
if (!this.noteContext.isActive()) {
|
if (!this.noteContext.isActive()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ const TPL = `
|
|||||||
|
|
||||||
const MAX_SEARCH_RESULTS_IN_TREE = 100;
|
const MAX_SEARCH_RESULTS_IN_TREE = 100;
|
||||||
|
|
||||||
|
// this has to be hanged on the actual elements to effectively intercept and stop click event
|
||||||
|
const cancelClickPropagation = e => e.stopPropagation();
|
||||||
|
|
||||||
export default class NoteTreeWidget extends NoteContextAwareWidget {
|
export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -559,7 +562,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== 'root';
|
const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== 'root';
|
||||||
|
|
||||||
if (isHoistedNote) {
|
if (isHoistedNote) {
|
||||||
const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>');
|
const $unhoistButton = $('<span class="tree-item-button unhoist-button bx bx-door-open" title="Unhoist"></span>')
|
||||||
|
.on("click", cancelClickPropagation);
|
||||||
|
|
||||||
// unhoist button is prepended since compared to other buttons this is not just convenience
|
// unhoist button is prepended since compared to other buttons this is not just convenience
|
||||||
// on the mobile interface - it's the only way to unhoist
|
// on the mobile interface - it's the only way to unhoist
|
||||||
@@ -567,19 +571,22 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (note.hasLabel('workspace') && !isHoistedNote) {
|
if (note.hasLabel('workspace') && !isHoistedNote) {
|
||||||
const $enterWorkspaceButton = $('<span class="tree-item-button enter-workspace-button bx bx-door-open" title="Hoist this note (workspace)"></span>');
|
const $enterWorkspaceButton = $('<span class="tree-item-button enter-workspace-button bx bx-door-open" title="Hoist this note (workspace)"></span>')
|
||||||
|
.on("click", cancelClickPropagation);
|
||||||
|
|
||||||
$span.append($enterWorkspaceButton);
|
$span.append($enterWorkspaceButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.type === 'search') {
|
if (note.type === 'search') {
|
||||||
const $refreshSearchButton = $('<span class="tree-item-button refresh-search-button bx bx-refresh" title="Refresh saved search results"></span>');
|
const $refreshSearchButton = $('<span class="tree-item-button refresh-search-button bx bx-refresh" title="Refresh saved search results"></span>')
|
||||||
|
.on("click", cancelClickPropagation);
|
||||||
|
|
||||||
$span.append($refreshSearchButton);
|
$span.append($refreshSearchButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!['search', 'launcher'].includes(note.type) && !note.isOptions() && !note.isLaunchBarConfig()) {
|
if (!['search', 'launcher'].includes(note.type) && !note.isOptions() && !note.isLaunchBarConfig()) {
|
||||||
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>');
|
const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>')
|
||||||
|
.on("click", cancelClickPropagation);
|
||||||
|
|
||||||
$span.append($createChildNoteButton);
|
$span.append($createChildNoteButton);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const content = {
|
const content = {
|
||||||
|
type: "excalidraw",
|
||||||
|
version: 2,
|
||||||
_meta: "This note has type `canvas`. It uses excalidraw and stores an exported svg alongside.",
|
_meta: "This note has type `canvas`. It uses excalidraw and stores an exported svg alongside.",
|
||||||
elements, // excalidraw
|
elements, // excalidraw
|
||||||
appState, // excalidraw
|
appState, // excalidraw
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ export default class EtapiOptions extends OptionsWidget {
|
|||||||
message: "Please enter new token's name",
|
message: "Please enter new token's name",
|
||||||
defaultValue: oldName
|
defaultValue: oldName
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(tokenName === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await server.patch(`etapi-tokens/${etapiTokenId}`, {name: tokenName});
|
await server.patch(`etapi-tokens/${etapiTokenId}`, {name: tokenName});
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ function execute(req) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.toLowerCase().startsWith('select')) {
|
if (query.toLowerCase().startsWith('select') || query.toLowerCase().startsWith('with')) {
|
||||||
results.push(sql.getRows(query));
|
results.push(sql.getRows(query));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ const etapiBranchRoutes = require('../etapi/branches');
|
|||||||
const etapiNoteRoutes = require('../etapi/notes');
|
const etapiNoteRoutes = require('../etapi/notes');
|
||||||
const etapiSpecialNoteRoutes = require('../etapi/special_notes');
|
const etapiSpecialNoteRoutes = require('../etapi/special_notes');
|
||||||
const etapiSpecRoute = require('../etapi/spec');
|
const etapiSpecRoute = require('../etapi/spec');
|
||||||
|
const etapiBackupRoute = require('../etapi/backup');
|
||||||
|
|
||||||
const csrfMiddleware = csurf({
|
const csrfMiddleware = csurf({
|
||||||
cookie: true,
|
cookie: true,
|
||||||
@@ -315,6 +316,7 @@ function register(app) {
|
|||||||
etapiNoteRoutes.register(router);
|
etapiNoteRoutes.register(router);
|
||||||
etapiSpecialNoteRoutes.register(router);
|
etapiSpecialNoteRoutes.register(router);
|
||||||
etapiSpecRoute.register(router);
|
etapiSpecRoute.register(router);
|
||||||
|
etapiBackupRoute.register(router);
|
||||||
|
|
||||||
app.use('', router);
|
app.use('', router);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2023-06-08T22:46:52+02:00", buildRevision: "6e69cafe5419e8efcc6f652647f9227dbcfa1e18" };
|
module.exports = { buildDate:"2023-06-15T23:23:37+02:00", buildRevision: "3223e767875e5379c99ff58a562cb9c1a2641bdf" };
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const ws = require('./ws');
|
|||||||
const taskContexts = {};
|
const taskContexts = {};
|
||||||
|
|
||||||
class TaskContext {
|
class TaskContext {
|
||||||
constructor(taskId, taskType = null, data = null) {
|
constructor(taskId, taskType = null, data = {}) {
|
||||||
this.taskId = taskId;
|
this.taskId = taskId;
|
||||||
this.taskType = taskType;
|
this.taskType = taskType;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|||||||
4
test-etapi/create-backup.http
Normal file
4
test-etapi/create-backup.http
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
PUT {{triliumHost}}/etapi/backup/etapi_test
|
||||||
|
Authorization: {{authToken}}
|
||||||
|
|
||||||
|
> {% client.assert(response.status === 201); %}
|
||||||
12
test-etapi/import-zip.http
Normal file
12
test-etapi/import-zip.http
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
POST {{triliumHost}}/etapi/notes/root/import
|
||||||
|
Authorization: {{authToken}}
|
||||||
|
Content-Type: application/octet-stream
|
||||||
|
Content-Transfer-Encoding: binary
|
||||||
|
|
||||||
|
< ../db/demo.zip
|
||||||
|
|
||||||
|
> {%
|
||||||
|
client.assert(response.status === 201);
|
||||||
|
client.assert(response.body.note.title == "Trilium Demo");
|
||||||
|
client.assert(response.body.branch.parentNoteId == "root");
|
||||||
|
%}
|
||||||
Reference in New Issue
Block a user