mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 21:11:30 +08:00 
			
		
		
		
	optional cleanup of unused images
This commit is contained in:
		
							parent
							
								
									bde9e825c8
								
							
						
					
					
						commit
						31b4186e17
					
				
							
								
								
									
										
											BIN
										
									
								
								db/image-deleted.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/image-deleted.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.4 KiB | 
| @ -157,6 +157,7 @@ settings.addModule((async function () { | |||||||
|     const fillSyncRowsButton = $("#fill-sync-rows-button"); |     const fillSyncRowsButton = $("#fill-sync-rows-button"); | ||||||
|     const anonymizeButton = $("#anonymize-button"); |     const anonymizeButton = $("#anonymize-button"); | ||||||
|     const cleanupSoftDeletedButton = $("#cleanup-soft-deleted-items-button"); |     const cleanupSoftDeletedButton = $("#cleanup-soft-deleted-items-button"); | ||||||
|  |     const cleanupUnusedImagesButton = $("#cleanup-unused-images-button"); | ||||||
|     const vacuumDatabaseButton = $("#vacuum-database-button"); |     const vacuumDatabaseButton = $("#vacuum-database-button"); | ||||||
| 
 | 
 | ||||||
|     forceFullSyncButton.click(async () => { |     forceFullSyncButton.click(async () => { | ||||||
| @ -186,6 +187,14 @@ settings.addModule((async function () { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     cleanupUnusedImagesButton.click(async () => { | ||||||
|  |         if (confirm("Do you really want to clean up unused images?")) { | ||||||
|  |             await server.post('cleanup/cleanup-unused-images'); | ||||||
|  | 
 | ||||||
|  |             showMessage("Unused images have been cleaned up"); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     vacuumDatabaseButton.click(async () => { |     vacuumDatabaseButton.click(async () => { | ||||||
|         await server.post('cleanup/vacuum-database'); |         await server.post('cleanup/vacuum-database'); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								public/libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -22,6 +22,10 @@ router.post('/cleanup-soft-deleted-items', auth.checkApiAuth, wrap(async (req, r | |||||||
| 
 | 
 | ||||||
|         await sql.execute("DELETE FROM notes_tree WHERE is_deleted = 1"); |         await sql.execute("DELETE FROM notes_tree WHERE is_deleted = 1"); | ||||||
| 
 | 
 | ||||||
|  |         await sql.execute("DELETE FROM notes_image WHERE is_deleted = 1"); | ||||||
|  | 
 | ||||||
|  |         await sql.execute("DELETE FROM images WHERE is_deleted = 1"); | ||||||
|  | 
 | ||||||
|         await sql.execute("DELETE FROM notes WHERE is_deleted = 1"); |         await sql.execute("DELETE FROM notes WHERE is_deleted = 1"); | ||||||
| 
 | 
 | ||||||
|         await sql.execute("DELETE FROM recent_notes"); |         await sql.execute("DELETE FROM recent_notes"); | ||||||
| @ -37,6 +41,33 @@ router.post('/cleanup-soft-deleted-items', auth.checkApiAuth, wrap(async (req, r | |||||||
|     res.send({}); |     res.send({}); | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
|  | router.post('/cleanup-unused-images', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||||
|  |     const sourceId = req.headers.source_id; | ||||||
|  | 
 | ||||||
|  |     await sql.doInTransaction(async () => { | ||||||
|  |         const unusedImageIds = await sql.getFirstColumn(` | ||||||
|  |           SELECT images.image_id  | ||||||
|  |           FROM images  | ||||||
|  |             LEFT JOIN notes_image ON notes_image.image_id = images.image_id AND notes_image.is_deleted = 0 | ||||||
|  |           WHERE | ||||||
|  |             images.is_deleted = 0 | ||||||
|  |             AND notes_image.note_image_id IS NULL`);
 | ||||||
|  | 
 | ||||||
|  |         const now = utils.nowDate(); | ||||||
|  | 
 | ||||||
|  |         for (const imageId of unusedImageIds) { | ||||||
|  |             log.info(`Deleting unused image: ${imageId}`); | ||||||
|  | 
 | ||||||
|  |             await sql.execute("UPDATE images SET is_deleted = 1, data = null, date_modified = ? WHERE image_id = ?", | ||||||
|  |                 [now, imageId]); | ||||||
|  | 
 | ||||||
|  |             await sync_table.addImageSync(imageId, sourceId); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     res.send({}); | ||||||
|  | })); | ||||||
|  | 
 | ||||||
| router.post('/vacuum-database', auth.checkApiAuth, wrap(async (req, res, next) => { | router.post('/vacuum-database', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||||
|     await sql.execute("VACUUM"); |     await sql.execute("VACUUM"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,8 @@ const jimp = require('jimp'); | |||||||
| const imageType = require('image-type'); | const imageType = require('image-type'); | ||||||
| const sanitizeFilename = require('sanitize-filename'); | const sanitizeFilename = require('sanitize-filename'); | ||||||
| const wrap = require('express-promise-wrap').wrap; | const wrap = require('express-promise-wrap').wrap; | ||||||
|  | const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; | ||||||
|  | const fs = require('fs'); | ||||||
| 
 | 
 | ||||||
| router.get('/:imageId/:filename', auth.checkApiAuthOrElectron, wrap(async (req, res, next) => { | router.get('/:imageId/:filename', auth.checkApiAuthOrElectron, wrap(async (req, res, next) => { | ||||||
|     const image = await sql.getFirst("SELECT * FROM images WHERE image_id = ?", [req.params.imageId]); |     const image = await sql.getFirst("SELECT * FROM images WHERE image_id = ?", [req.params.imageId]); | ||||||
| @ -22,6 +24,10 @@ router.get('/:imageId/:filename', auth.checkApiAuthOrElectron, wrap(async (req, | |||||||
|     if (!image) { |     if (!image) { | ||||||
|         return res.status(404).send({}); |         return res.status(404).send({}); | ||||||
|     } |     } | ||||||
|  |     else if (image.data === null) { | ||||||
|  |         res.set('Content-Type', 'image/png'); | ||||||
|  |         return res.send(fs.readFileSync(RESOURCE_DIR + '/db/image-deleted.png')); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     res.set('Content-Type', 'image/' + image.format); |     res.set('Content-Type', 'image/' + image.format); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -248,12 +248,24 @@ | |||||||
|           <p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and metadata) |           <p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and metadata) | ||||||
|             for sharing online for debugging purposes without fear of leaking your personal data.</p> |             for sharing online for debugging purposes without fear of leaking your personal data.</p> | ||||||
| 
 | 
 | ||||||
|           <h4>Cleanup</h4> |           <h4>Image cleanup</h4> | ||||||
| 
 | 
 | ||||||
|           <button id="cleanup-soft-deleted-items-button" class="btn btn-danger btn-sm">Permanently cleanup soft-deleted items</button> (should be executed in all synced instances) |           <p>This will remove all image data of images not used in any current version of note from the database (metadata will remain). | ||||||
| 
 | 
 | ||||||
|           <br/> |             This means that some images can disappear from note history.</p> | ||||||
|           <br/> | 
 | ||||||
|  |           <button id="cleanup-unused-images-button" class="btn btn-warning btn-sm">Permanently cleanup unused images</button> | ||||||
|  | 
 | ||||||
|  |           <h4>Soft-delete cleanup</h4> | ||||||
|  | 
 | ||||||
|  |           <p>This deletes all soft deleted rows from the database. This change isn't synced and should be done manually on all instances. | ||||||
|  |             <strong>Use this only if you really know what you're doing.</strong></p> | ||||||
|  | 
 | ||||||
|  |           <button id="cleanup-soft-deleted-items-button" class="btn btn-danger btn-sm">Permanently cleanup soft-deleted items</button> | ||||||
|  | 
 | ||||||
|  |           <h4>Vacuum database</h4> | ||||||
|  | 
 | ||||||
|  |           <p>This will rebuild database which will typically result in smaller database file. No data will be actually changed.</p> | ||||||
| 
 | 
 | ||||||
|           <button id="vacuum-database-button" class="btn btn-sm">Vacuum database</button> |           <button id="vacuum-database-button" class="btn btn-sm">Vacuum database</button> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 azivner
						azivner