git ssb

0+

Rômulo Alves / sync



Commit 3f257b5c381aa9abc81ea117537a756a2b877e84

Initial commit

Rômulo Alves committed on 4/5/2018, 1:48:39 AM

Files changed

.gitignoreadded
index.jsadded
package.jsonadded
utils.jsadded
yarn.lockadded
.gitignoreView
@@ -1,0 +1,2 @@
1 +node_modules/
2 +test.js
index.jsView
@@ -1,0 +1,50 @@
1 +const utils = require('./utils');
2 +
3 +module.exports = function sync(source, target, crypt, decrypt) {
4 + return new Promise(async (resolve, reject) => {
5 + try {
6 +
7 + // Get stats of source and target
8 + const [ statSource, statTarget ] = await Promise.all([ utils.getStats(source), utils.getStats(target) ]);
9 +
10 + // Source does not exists and target exists, delete target
11 + if (statSource === null && statTarget !== null) {
12 + await utils.delete(target);
13 +
14 + return resolve();
15 + }
16 +
17 + // Dost not exists in the target, copy
18 + if (statSource !== null && statTarget === null) {
19 + await utils.copy(source, target, crypt, decrypt);
20 +
21 + return resolve();
22 + }
23 +
24 + if (statSource.isDirectory() && statTarget.isDirectory()) {
25 +
26 + // Verify if both are directories
27 + const nextPaths = await utils.readRecursive(source, target);
28 +
29 + for (let index = 0; index < nextPaths.length; index++) {
30 + const [ nextSource, nextTarget ] = nextPaths[index];
31 +
32 + // Start to sync next paths
33 + await sync(nextSource, nextTarget, crypt, decrypt);
34 + }
35 + } else if (statSource.isFile() && statTarget.isFile()) {
36 +
37 + // Verify if both are files
38 + if (statSource.mtime <= statTarget.mtime) {
39 +
40 + // Source is newer, copy the new version to the target
41 + await utils.copy(source, target, crypt, decrypt);
42 + }
43 + }
44 +
45 + return resolve();
46 + } catch (err) {
47 + return reject(err);
48 + }
49 + });
50 +};
package.jsonView
@@ -1,0 +1,14 @@
1 +{
2 + "name": "sync",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "echo \"Error: no test specified\" && exit 1"
8 + },
9 + "author": "",
10 + "license": "ISC",
11 + "dependencies": {
12 + "fs-extra": "^5.0.0"
13 + }
14 +}
utils.jsView
@@ -1,0 +1,137 @@
1 +const { PassThrough } = require('stream');
2 +const { stat, exists } = require('fs');
3 +const fs = require('fs-extra');
4 +const { createReadStream, createWriteStream, utimes } = require('fs');
5 +const { createCipher, createDecipher } = require('crypto');
6 +const { join } = require('path');
7 +const { createGzip, createGunzip } = require('zlib');
8 +
9 +/**
10 + * @function getStats
11 + * @description Returns the directory/file stat
12 + * @param {String} path Path to verify
13 + * @return {Promies<Object>} Null if an error happens or the object of stat
14 + */
15 +const getStats = path =>
16 + new Promise(resolve => {
17 + return stat(path, (err, stat) => {
18 + if (err) {
19 + // File does not exist
20 + return resolve(null);
21 + }
22 +
23 + // Return stat
24 + return resolve(stat);
25 + });
26 + });
27 +
28 +module.exports.getStats = getStats;
29 +
30 +/**
31 + * @function exists
32 + * @description Returns if a path exists
33 + * @param {String} path Path to verify
34 + * @return {Promise<Boolean>} Returns a boolean to indicate if path exists
35 + */
36 +module.exports.exists = path =>
37 + new Promise(resolve => {
38 + return exists(path, exists => resolve(exists));
39 + });
40 +
41 +/**
42 + * @function delete
43 + * @description Deletes the path
44 + * @param {String} path Path to delete
45 + * @return {Promise} Resolves after deleted
46 + */
47 +module.exports.delete = path =>
48 + new Promise(resolve => {
49 + return fs.remove(path, () => resolve());
50 + });
51 +
52 +/**
53 + * @function copy
54 + * @description Copies the file from the source to the target path
55 + * @param {String} source File path to copy
56 + * @param {String} target Path to put the file
57 + * @param {String} crypt The cipher algorithm and key
58 + * @param {String} decrypt The decipher algorithm and key
59 + * @return {Promise<Object>} Resolves if was OK or rejects with the error object
60 + */
61 +module.exports.copy = (source, target, crypt, decrypt) =>
62 + new Promise(async (resolve, reject) => {
63 + const [ statSource, statTarget ] = await Promise.all([ getStats(source), getStats(target) ]);
64 +
65 + if (!statSource) {
66 + return reject(new Error('Source does not exists.'));
67 + }
68 +
69 + if (!statSource.isFile() || (statTarget && !statTarget.isFile())) {
70 + return reject(new Error(`It's possible just to copy files.`));
71 + }
72 +
73 + let cryptOrDecryptFn = null;
74 + let zipFn = null;
75 + let unzipFn = null;
76 +
77 + if (crypt) {
78 +
79 + // Set function to crypt
80 + cryptOrDecryptFn = createCipher(crypt.algorithm, crypt.password);
81 + zipFn = createGzip();
82 + unzipFn = new PassThrough();
83 + } else if (decrypt) {
84 +
85 + // Set function to decrypt
86 + cryptOrDecryptFn = createDecipher(decrypt.algorithm, decrypt.password);
87 + zipFn = new PassThrough();
88 + unzipFn = createGunzip();
89 + } else {
90 +
91 + // Set empty stream just to pipe
92 + cryptOrDecryptFn = new PassThrough();
93 + zipFn = new PassThrough();
94 + unzipFn = new PassThrough();
95 + }
96 +
97 + const destStream = createWriteStream(target);
98 + destStream.on('finish', () => {
99 +
100 + // Change target modificate time
101 + return utimes(target, statSource.atime, statSource.mtime, () => {
102 + return resolve();
103 + });
104 + });
105 + destStream.on('error', err => reject(err));
106 +
107 + // Read the file > crypt/decrypt > write to the target > resolve the promise
108 + return createReadStream(source)
109 + .pipe(zipFn)
110 + .pipe(cryptOrDecryptFn)
111 + .pipe(unzipFn)
112 + .pipe(destStream);
113 + });
114 +
115 +/**
116 + * @function readRecursive
117 + * @description Reads recursivelly a path
118 + * @param {String} source Path to copy
119 + * @param {String} target Target path
120 + * @return {Promise<Array<Array<String>>>} Resolves with the paths if was OK or rejects with the error object
121 + */
122 +module.exports.readRecursive = (source, target) =>
123 + new Promise((resolve, reject) => {
124 +
125 + // Read things inside dir
126 + return fs.readdir(source, (err, files) => {
127 + if (err) {
128 + return reject(err);
129 + }
130 +
131 + // Return an array with new source and target
132 + const pathsParsed = files.map(f => [
133 + join(source, f),
134 + join(target, f)
135 + ]);
136 + });
137 + });
yarn.lockView
@@ -1,0 +1,25 @@
1 +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 +# yarn lockfile v1
3 +
4 +
5 +fs-extra@^5.0.0:
6 + version "5.0.0"
7 + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd"
8 + dependencies:
9 + graceful-fs "^4.1.2"
10 + jsonfile "^4.0.0"
11 + universalify "^0.1.0"
12 +
13 +graceful-fs@^4.1.2, graceful-fs@^4.1.6:
14 + version "4.1.11"
15 + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
16 +
17 +jsonfile@^4.0.0:
18 + version "4.0.0"
19 + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
20 + optionalDependencies:
21 + graceful-fs "^4.1.6"
22 +
23 +universalify@^0.1.0:
24 + version "0.1.1"
25 + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"

Built with git-ssb-web