mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
relation between notes and images
This commit is contained in:
parent
784cd62df1
commit
c0e45a73a8
16
migrations/0064__add_note_id_to_image_table.sql
Normal file
16
migrations/0064__add_note_id_to_image_table.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
DROP TABLE images;
|
||||||
|
|
||||||
|
CREATE TABLE images
|
||||||
|
(
|
||||||
|
image_id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
note_id TEXT NOT NULL,
|
||||||
|
format TEXT NOT NULL,
|
||||||
|
checksum TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
data BLOB,
|
||||||
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
|
date_modified TEXT NOT NULL,
|
||||||
|
date_created TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX images_note_id_index ON images (note_id);
|
26
migrations/0065__notes_image.sql
Normal file
26
migrations/0065__notes_image.sql
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
DROP TABLE images;
|
||||||
|
|
||||||
|
CREATE TABLE images
|
||||||
|
(
|
||||||
|
image_id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
format TEXT NOT NULL,
|
||||||
|
checksum TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
data BLOB,
|
||||||
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
|
date_modified TEXT NOT NULL,
|
||||||
|
date_created TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE notes_image
|
||||||
|
(
|
||||||
|
note_image_id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
note_id TEXT NOT NULL,
|
||||||
|
image_id TEXT NOT NULL,
|
||||||
|
is_deleted INT NOT NULL DEFAULT 0,
|
||||||
|
date_modified TEXT NOT NULL,
|
||||||
|
date_created TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX notes_image_note_id_index ON notes_image (note_id);
|
||||||
|
CREATE INDEX notes_image_note_id_image_id_index ON notes_image (note_id, image_id);
|
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
@ -24,11 +24,16 @@ router.get('/:imageId/:filename', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
res.send(image.data);
|
res.send(image.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/upload', auth.checkApiAuth, multer.single('upload'), async (req, res, next) => {
|
router.post('', auth.checkApiAuth, multer.single('upload'), async (req, res, next) => {
|
||||||
const sourceId = req.headers.source_id;
|
const sourceId = req.headers.source_id;
|
||||||
|
const noteId = req.query.noteId;
|
||||||
const file = req.file;
|
const file = req.file;
|
||||||
|
|
||||||
const imageId = utils.newNoteId();
|
const note = await sql.getFirst("SELECT * FROM notes WHERE note_id = ?", [noteId]);
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return req.status(404).send(`Note ${noteId} doesn't exist.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!file.mimetype.startsWith("image/")) {
|
if (!file.mimetype.startsWith("image/")) {
|
||||||
return req.send("Unknown image type: " + file.mimetype);
|
return req.send("Unknown image type: " + file.mimetype);
|
||||||
@ -39,6 +44,8 @@ router.post('/upload', auth.checkApiAuth, multer.single('upload'), async (req, r
|
|||||||
const resizedImage = await resize(file.buffer);
|
const resizedImage = await resize(file.buffer);
|
||||||
const optimizedImage = await optimize(resizedImage);
|
const optimizedImage = await optimize(resizedImage);
|
||||||
|
|
||||||
|
const imageId = utils.newImageId();
|
||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
await sql.insert("images", {
|
await sql.insert("images", {
|
||||||
image_id: imageId,
|
image_id: imageId,
|
||||||
@ -52,11 +59,24 @@ router.post('/upload', auth.checkApiAuth, multer.single('upload'), async (req, r
|
|||||||
});
|
});
|
||||||
|
|
||||||
await sync_table.addImageSync(imageId, sourceId);
|
await sync_table.addImageSync(imageId, sourceId);
|
||||||
|
|
||||||
|
const noteImageId = utils.newNoteImageId();
|
||||||
|
|
||||||
|
await sql.insert("notes_image", {
|
||||||
|
note_image_id: noteImageId,
|
||||||
|
note_id: noteId,
|
||||||
|
image_id: imageId,
|
||||||
|
is_deleted: 0,
|
||||||
|
date_modified: now,
|
||||||
|
date_created: now
|
||||||
|
});
|
||||||
|
|
||||||
|
await sync_table.addNoteImageSync(noteImageId, sourceId);
|
||||||
});
|
});
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
uploaded: true,
|
uploaded: true,
|
||||||
url: `/api/image/${imageId}/${file.originalname}`
|
url: `/api/images/${imageId}/${file.originalname}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -133,6 +133,12 @@ router.get('/images/:imageId', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
res.send(entity);
|
res.send(entity);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/notes_image/:noteImageId', auth.checkApiAuth, async (req, res, next) => {
|
||||||
|
const noteImageId = req.params.noteImageId;
|
||||||
|
|
||||||
|
res.send(await sql.getFirst("SELECT * FROM notes_image WHERE note_image_id = ?", [noteImageId]));
|
||||||
|
});
|
||||||
|
|
||||||
router.put('/notes', auth.checkApiAuth, async (req, res, next) => {
|
router.put('/notes', auth.checkApiAuth, async (req, res, next) => {
|
||||||
await syncUpdate.updateNote(req.body.entity, req.body.sourceId);
|
await syncUpdate.updateNote(req.body.entity, req.body.sourceId);
|
||||||
|
|
||||||
@ -175,4 +181,10 @@ router.put('/images', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
res.send({});
|
res.send({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put('/notes_image', auth.checkApiAuth, async (req, res, next) => {
|
||||||
|
await syncUpdate.updateNoteImage(req.body.entity, req.body.sourceId);
|
||||||
|
|
||||||
|
res.send({});
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -52,7 +52,7 @@ function register(app) {
|
|||||||
app.use('/api/sql', sqlRoute);
|
app.use('/api/sql', sqlRoute);
|
||||||
app.use('/api/anonymization', anonymizationRoute);
|
app.use('/api/anonymization', anonymizationRoute);
|
||||||
app.use('/api/cleanup', cleanupRoute);
|
app.use('/api/cleanup', cleanupRoute);
|
||||||
app.use('/api/image', imageRoute);
|
app.use('/api/images', imageRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../package');
|
const packageJson = require('../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 63;
|
const APP_DB_VERSION = 65;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
app_version: packageJson.version,
|
app_version: packageJson.version,
|
||||||
|
@ -181,6 +181,8 @@ async function runAllChecks() {
|
|||||||
await runSyncRowChecks("notes_history", "note_history_id", errorList);
|
await runSyncRowChecks("notes_history", "note_history_id", errorList);
|
||||||
await runSyncRowChecks("notes_tree", "note_tree_id", errorList);
|
await runSyncRowChecks("notes_tree", "note_tree_id", errorList);
|
||||||
await runSyncRowChecks("recent_notes", "note_tree_id", errorList);
|
await runSyncRowChecks("recent_notes", "note_tree_id", errorList);
|
||||||
|
await runSyncRowChecks("images", "image_id", errorList);
|
||||||
|
await runSyncRowChecks("notes_image", "note_image_id", errorList);
|
||||||
|
|
||||||
if (errorList.length === 0) {
|
if (errorList.length === 0) {
|
||||||
// we run this only if basic checks passed since this assumes basic data consistency
|
// we run this only if basic checks passed since this assumes basic data consistency
|
||||||
|
@ -224,6 +224,9 @@ async function pushEntity(sync, syncContext) {
|
|||||||
entity.data = entity.data.toString('base64');
|
entity.data = entity.data.toString('base64');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (sync.entity_name === 'notes_image') {
|
||||||
|
entity = await sql.getFirst('SELECT * FROM notes_image WHERE note_image_id = ?', [sync.entity_id]);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error(`Unrecognized entity type ${sync.entity_name} in sync #${sync.id}`);
|
throw new Error(`Unrecognized entity type ${sync.entity_name} in sync #${sync.id}`);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,10 @@ async function addImageSync(imageId, sourceId) {
|
|||||||
await addEntitySync("images", imageId, sourceId);
|
await addEntitySync("images", imageId, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addNoteImageSync(imageId, sourceId) {
|
||||||
|
await addEntitySync("notes_image", imageId, sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
async function addEntitySync(entityName, entityId, sourceId) {
|
async function addEntitySync(entityName, entityId, sourceId) {
|
||||||
await sql.replace("sync", {
|
await sql.replace("sync", {
|
||||||
entity_name: entityName,
|
entity_name: entityName,
|
||||||
@ -83,6 +87,7 @@ async function fillAllSyncRows() {
|
|||||||
await fillSyncRows("notes_history", "note_history_id");
|
await fillSyncRows("notes_history", "note_history_id");
|
||||||
await fillSyncRows("recent_notes", "note_tree_id");
|
await fillSyncRows("recent_notes", "note_tree_id");
|
||||||
await fillSyncRows("images", "image_id");
|
await fillSyncRows("images", "image_id");
|
||||||
|
await fillSyncRows("notes_image", "note_image_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -93,6 +98,7 @@ module.exports = {
|
|||||||
addOptionsSync,
|
addOptionsSync,
|
||||||
addRecentNoteSync,
|
addRecentNoteSync,
|
||||||
addImageSync,
|
addImageSync,
|
||||||
|
addNoteImageSync,
|
||||||
cleanupSyncRowsForMissingEntities,
|
cleanupSyncRowsForMissingEntities,
|
||||||
fillAllSyncRows
|
fillAllSyncRows
|
||||||
};
|
};
|
@ -110,6 +110,20 @@ async function updateImage(entity, sourceId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateNoteImage(entity, sourceId) {
|
||||||
|
const origNoteImage = await sql.getFirst("SELECT * FROM notes_image WHERE note_image_id = ?", [entity.note_image_id]);
|
||||||
|
|
||||||
|
if (!origNoteImage || origNoteImage.date_modified <= entity.date_modified) {
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
await sql.replace("notes_image", entity);
|
||||||
|
|
||||||
|
await sync_table.addNoteImageSync(entity.note_image_id, sourceId);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("Update/sync note image " + entity.note_image_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateNote,
|
updateNote,
|
||||||
updateNoteTree,
|
updateNoteTree,
|
||||||
@ -117,5 +131,6 @@ module.exports = {
|
|||||||
updateNoteReordering,
|
updateNoteReordering,
|
||||||
updateOptions,
|
updateOptions,
|
||||||
updateRecentNotes,
|
updateRecentNotes,
|
||||||
updateImage
|
updateImage,
|
||||||
|
updateNoteImage
|
||||||
};
|
};
|
@ -15,6 +15,14 @@ function newNoteHistoryId() {
|
|||||||
return randomString(12);
|
return randomString(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function newImageId() {
|
||||||
|
return randomString(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newNoteImageId() {
|
||||||
|
return randomString(12);
|
||||||
|
}
|
||||||
|
|
||||||
function randomString(length) {
|
function randomString(length) {
|
||||||
return randtoken.generate(length);
|
return randtoken.generate(length);
|
||||||
}
|
}
|
||||||
@ -96,6 +104,8 @@ module.exports = {
|
|||||||
newNoteId,
|
newNoteId,
|
||||||
newNoteTreeId,
|
newNoteTreeId,
|
||||||
newNoteHistoryId,
|
newNoteHistoryId,
|
||||||
|
newImageId,
|
||||||
|
newNoteImageId,
|
||||||
toBase64,
|
toBase64,
|
||||||
fromBase64,
|
fromBase64,
|
||||||
hmac,
|
hmac,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user