mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 23:31:33 +08:00 
			
		
		
		
	Merge branch 'beta'
This commit is contained in:
		
						commit
						6f16b4caec
					
				
							
								
								
									
										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.1-beta",
 | 
					  "version": "0.60.2-beta",
 | 
				
			||||||
  "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
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -700,7 +700,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:
 | 
				
			||||||
@ -880,6 +899,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:
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,8 @@ function getRecentChanges(req) {
 | 
				
			|||||||
    for (const noteRevisionRow of noteRevisionRows) {
 | 
					    for (const noteRevisionRow of noteRevisionRows) {
 | 
				
			||||||
        const note = becca.getNote(noteRevisionRow.noteId);
 | 
					        const note = becca.getNote(noteRevisionRow.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (note?.hasAncestor(ancestorNoteId)) {
 | 
					        // for deleted notes, the becca note is null, and it's not possible to (easily) determine if it belongs to a subtree
 | 
				
			||||||
 | 
					        if (ancestorNoteId === 'root' || note?.hasAncestor(ancestorNoteId)) {
 | 
				
			||||||
            recentChanges.push(noteRevisionRow);
 | 
					            recentChanges.push(noteRevisionRow);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -43,8 +44,8 @@ function getRecentChanges(req) {
 | 
				
			|||||||
                notes.title AS current_title,
 | 
					                notes.title AS current_title,
 | 
				
			||||||
                notes.isProtected AS current_isProtected,
 | 
					                notes.isProtected AS current_isProtected,
 | 
				
			||||||
                notes.title,
 | 
					                notes.title,
 | 
				
			||||||
                notes.utcDateCreated AS utcDate,
 | 
					                notes.utcDateCreated AS utcDate, -- different from the second SELECT
 | 
				
			||||||
                notes.dateCreated AS date
 | 
					                notes.dateCreated AS date        -- different from the second SELECT
 | 
				
			||||||
            FROM notes
 | 
					            FROM notes
 | 
				
			||||||
        UNION ALL
 | 
					        UNION ALL
 | 
				
			||||||
            SELECT
 | 
					            SELECT
 | 
				
			||||||
@ -54,15 +55,16 @@ function getRecentChanges(req) {
 | 
				
			|||||||
                notes.title AS current_title,
 | 
					                notes.title AS current_title,
 | 
				
			||||||
                notes.isProtected AS current_isProtected,
 | 
					                notes.isProtected AS current_isProtected,
 | 
				
			||||||
                notes.title,
 | 
					                notes.title,
 | 
				
			||||||
                notes.utcDateModified AS utcDate,
 | 
					                notes.utcDateModified AS utcDate, -- different from the first SELECT
 | 
				
			||||||
                notes.dateModified AS date
 | 
					                notes.dateModified AS date        -- different from the first SELECT
 | 
				
			||||||
            FROM notes
 | 
					            FROM notes
 | 
				
			||||||
            WHERE notes.isDeleted = 1`);
 | 
					            WHERE notes.isDeleted = 1`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const noteRow of noteRows) {
 | 
					    for (const noteRow of noteRows) {
 | 
				
			||||||
        const note = becca.getNote(noteRow.noteId);
 | 
					        const note = becca.getNote(noteRow.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (note?.hasAncestor(ancestorNoteId)) {
 | 
					        // for deleted notes, the becca note is null, and it's not possible to (easily) determine if it belongs to a subtree
 | 
				
			||||||
 | 
					        if (ancestorNoteId === 'root' || note?.hasAncestor(ancestorNoteId)) {
 | 
				
			||||||
            recentChanges.push(noteRow);
 | 
					            recentChanges.push(noteRow);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -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-05-26T23:11:53+02:00", buildRevision: "82efc924136c5b215e39f2108f00dd2bf075271c" };
 | 
					module.exports = { buildDate:"2023-06-08T22:46:52+02:00", buildRevision: "6e69cafe5419e8efcc6f652647f9227dbcfa1e18" };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										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); %}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user