2017-12-03 12:41:18 +08:00
|
|
|
"use strict";
|
|
|
|
|
2018-04-01 23:42:12 +08:00
|
|
|
const repository = require('../../services/repository');
|
2018-04-02 09:27:46 +08:00
|
|
|
const labelService = require('../../services/labels');
|
|
|
|
const noteService = require('../../services/notes');
|
2018-02-26 13:07:43 +08:00
|
|
|
const tar = require('tar-stream');
|
|
|
|
const stream = require('stream');
|
|
|
|
const path = require('path');
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
function getFileName(name) {
|
|
|
|
let key;
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (name.endsWith(".dat")) {
|
|
|
|
key = "data";
|
|
|
|
name = name.substr(0, name.length - 4);
|
|
|
|
}
|
|
|
|
else if (name.endsWith((".meta"))) {
|
|
|
|
key = "meta";
|
|
|
|
name = name.substr(0, name.length - 5);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error("Unknown file type in import archive: " + name);
|
2017-12-03 12:41:18 +08:00
|
|
|
}
|
2018-02-26 13:07:43 +08:00
|
|
|
return {name, key};
|
|
|
|
}
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
async function parseImportFile(file) {
|
|
|
|
const fileMap = {};
|
|
|
|
const files = [];
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
const extract = tar.extract();
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
extract.on('entry', function(header, stream, next) {
|
|
|
|
let {name, key} = getFileName(header.name);
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
let file = fileMap[name];
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (!file) {
|
|
|
|
file = fileMap[name] = {
|
|
|
|
children: []
|
|
|
|
};
|
2017-12-03 13:10:43 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
let parentFileName = path.dirname(header.name);
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (parentFileName && parentFileName !== '.') {
|
|
|
|
fileMap[parentFileName].children.push(file);
|
2017-12-03 13:10:43 +08:00
|
|
|
}
|
|
|
|
else {
|
2018-02-26 13:07:43 +08:00
|
|
|
files.push(file);
|
2017-12-03 13:10:43 +08:00
|
|
|
}
|
2017-12-03 12:41:18 +08:00
|
|
|
}
|
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
const chunks = [];
|
|
|
|
|
|
|
|
stream.on("data", function (chunk) {
|
|
|
|
chunks.push(chunk);
|
|
|
|
});
|
|
|
|
|
|
|
|
// header is the tar header
|
|
|
|
// stream is the content body (might be an empty stream)
|
|
|
|
// call next when you are done with this entry
|
2017-12-03 13:10:43 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
stream.on('end', function() {
|
|
|
|
file[key] = Buffer.concat(chunks);
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (key === "meta") {
|
|
|
|
file[key] = JSON.parse(file[key].toString("UTF-8"));
|
|
|
|
}
|
2017-12-11 01:56:59 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
next(); // ready for next entry
|
2017-12-03 12:41:18 +08:00
|
|
|
});
|
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
stream.resume(); // just auto drain the stream
|
|
|
|
});
|
|
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
extract.on('finish', function() {
|
|
|
|
resolve(files);
|
2017-12-03 12:41:18 +08:00
|
|
|
});
|
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
const bufferStream = new stream.PassThrough();
|
|
|
|
bufferStream.end(file.buffer);
|
|
|
|
|
|
|
|
bufferStream.pipe(extract);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-03-31 07:31:22 +08:00
|
|
|
async function importTar(req) {
|
2018-04-02 08:50:58 +08:00
|
|
|
const parentNoteId = req.params.parentNoteId;
|
2018-02-26 13:07:43 +08:00
|
|
|
const file = req.file;
|
|
|
|
|
2018-04-02 08:50:58 +08:00
|
|
|
const parentNote = await repository.getNote(parentNoteId);
|
2018-02-26 13:07:43 +08:00
|
|
|
|
2018-04-01 23:42:12 +08:00
|
|
|
if (!parentNote) {
|
2018-04-02 08:50:58 +08:00
|
|
|
return [404, `Note ${parentNoteId} doesn't exist.`];
|
2018-02-26 13:07:43 +08:00
|
|
|
}
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
const files = await parseImportFile(file);
|
|
|
|
|
2018-04-02 08:50:58 +08:00
|
|
|
await importNotes(files, parentNoteId);
|
2018-03-31 03:34:07 +08:00
|
|
|
}
|
2018-02-26 13:07:43 +08:00
|
|
|
|
2018-03-31 07:41:54 +08:00
|
|
|
async function importNotes(files, parentNoteId) {
|
2018-02-26 13:07:43 +08:00
|
|
|
for (const file of files) {
|
2018-03-03 22:32:21 +08:00
|
|
|
if (file.meta.version !== 1) {
|
|
|
|
throw new Error("Can't read meta data version " + file.meta.version);
|
|
|
|
}
|
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (file.meta.type !== 'file') {
|
|
|
|
file.data = file.data.toString("UTF-8");
|
|
|
|
}
|
|
|
|
|
2018-04-04 10:15:28 +08:00
|
|
|
const {note} = await noteService.createNote(parentNoteId, file.meta.title, file.data, {
|
2018-02-26 13:07:43 +08:00
|
|
|
type: file.meta.type,
|
2018-03-31 07:41:54 +08:00
|
|
|
mime: file.meta.mime
|
2018-02-26 13:07:43 +08:00
|
|
|
});
|
2017-12-03 12:41:18 +08:00
|
|
|
|
2018-04-01 21:59:44 +08:00
|
|
|
for (const label of file.meta.labels) {
|
2018-04-04 10:15:28 +08:00
|
|
|
await labelService.createLabel(note.noteId, label.name, label.value);
|
2018-03-03 22:30:18 +08:00
|
|
|
}
|
|
|
|
|
2018-02-26 13:07:43 +08:00
|
|
|
if (file.children.length > 0) {
|
2018-04-04 10:15:28 +08:00
|
|
|
await importNotes(file.children, note.noteId);
|
2017-12-03 12:41:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-31 03:34:07 +08:00
|
|
|
module.exports = {
|
|
|
|
importTar
|
|
|
|
};
|