mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	Merge branch 'master' into next61
# Conflicts: # package-lock.json # src/routes/api/recent_changes.js
This commit is contained in:
		
							
								
								
									
										2733
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2733
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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: | ||||||
| @@ -883,6 +902,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: | ||||||
|   | |||||||
| @@ -172,7 +172,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}); | ||||||
|   | |||||||
| @@ -139,6 +139,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(); | ||||||
| @@ -539,7 +542,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 | ||||||
| @@ -547,19 +551,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); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -110,6 +110,10 @@ export default class EtapiOptions extends OptionsWidget { | |||||||
|             defaultValue: oldName |             defaultValue: oldName | ||||||
|         }); |         }); | ||||||
|          |          | ||||||
|  |         if(tokenName === null) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         await server.patch(`etapi-tokens/${etapiTokenId}`, {name: tokenName}); |         await server.patch(`etapi-tokens/${etapiTokenId}`, {name: tokenName}); | ||||||
|  |  | ||||||
|         this.refreshTokens(); |         this.refreshTokens(); | ||||||
|   | |||||||
| @@ -27,7 +27,8 @@ function getRecentChanges(req) { | |||||||
|     for (const revisionRow of revisionRows) { |     for (const revisionRow of revisionRows) { | ||||||
|         const note = becca.getNote(revisionRow.noteId); |         const note = becca.getNote(revisionRow.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(revisionRow); |             recentChanges.push(revisionRow); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -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); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -32,7 +32,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 { | ||||||
|   | |||||||
| @@ -69,6 +69,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, | ||||||
| @@ -340,6 +341,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); %} | ||||||
		Reference in New Issue
	
	Block a user