├── .gitignore ├── CONSTANTS.js ├── LICENSE ├── README.md ├── ele.js ├── model ├── basecommand.js ├── creategroup.js ├── delete.js ├── download.js ├── joingroup.js └── upload.js ├── package.json └── utils ├── s3utils.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /CONSTANTS.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The DEFAULT_PASSWORD in the constant are dummy passwords only for the experimental purpose. 3 | */ 4 | module.exports = { 5 | 6 | ACCESS_KEY: 'access_key', 7 | SECRET_ACCESS_KEY: 'secret_access_key', 8 | BUCKET_NAME : 'bucket_name', 9 | S3_SERVER_ENDPOINT : "s3_server_endpoint", 10 | MAX_FILE_SIZE : 'max_upload_file_size_in_mb', 11 | PUBLIC_SHARED_FOLDER : 'public_shared_folder', 12 | 13 | GROUP_NAME : 'group_name', 14 | GROUP_ID : 'group_id', 15 | GROUP_CONFIG_FILE : 'groupConfigFile', 16 | 17 | 18 | HOME_ELE_DIR : ".ele", 19 | CONFIG_FILE : "config.ini", 20 | CONFIG_FILE_ENCRYPTED : "group-config-encrypted.ini", 21 | ALGORITHM : 'aes-256-ctr', 22 | UTF_8 : 'utf-8', 23 | DEFAULT_PASSWORD : "dummy-password", 24 | DEFAULT_PASSWORD_1 : "ele-default-dummy-password-123", 25 | 26 | CLI_KEY_FILE_NAME : 'fileName', 27 | CLI_USER_ENTERED_FILE_NUMBER:"fileNumber", 28 | CLI_GROUP_CONFIG_FILE : 'groupConfigFile', 29 | CLI_JOIN_GROUP_CONFIG_FILE : 'groupConfigFile', 30 | 31 | FILE_INDEX_FROM_SERVER : 'file_index', 32 | 33 | DEFAULT: 'DEFAULT', 34 | STATUS: 'STATUS', 35 | FAILURE: 'FAILURE', 36 | SUCCESS: 'SUCCESS', 37 | HTTPS: 'https://', 38 | DEBUG: false, 39 | 40 | FIRESSTORE_URL_TO_GET_NUMBER : "https://us-central1-helloele.cloudfunctions.net/mvp_add_file_path", 41 | FIRESSTORE_URL_TO_GET_FILE_DATA : "https://us-central1-helloele.cloudfunctions.net/mvp_get_file_url_by_index", 42 | FIRESSTORE_URL_TO_CREATE_GROUP : "https://us-central1-helloele.cloudfunctions.net/mvp_create_group", 43 | FIRESSTORE_URL_TO_UPDATE_DELETE_STATUS : "https://us-central1-helloele.cloudfunctions.net/mvp_update_delete_status", 44 | 45 | AWS_AMAZON_STORAGE_BUCKET : "s3.amazonaws.com", 46 | DIGITAL_OCEAN_SPACES : "sfo2.digitaloceanspaces.com", 47 | BACK_BLAZE_B2 : "s3.us-west-002.backblazeb2.com" 48 | 49 | } 50 | 51 | //region= us-east-1 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Krishna Pagadala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HelloEle 2 | 3 | A simple file copy tool from command prompt(terminal) using file-index **No more copy pasting or remembering long file urls**. 4 | You can upload download files with commands like. 5 | 6 | You can also find this doc on [HelloEle.com](https://helloele.com/) 7 | 8 | $ ele upload project-abc-version123.txt 9 | This will return a file-number example : 421 . To download this file using file-number 10 | 11 | $ ele download 421 12 | 13 | 14 | ## Requirements 15 | 16 | For development, you will only need Node.js and a node global package, npm installed in your environement. 17 | 18 | ### Usage & Configuration 19 | - #### installation 20 | To install ele run 21 | $ sudo npm install -g helloele 22 | 23 | To uninstall ele run 24 | $ sudo npm uninstall -g helloele 25 | 26 | - #### create-group 27 | To use ele you first need to create a group and provide your AWS S3 storage credentials (these credentails are stored in your computer never sent to helloeele server). 28 | $ ele create-group avengers-group 29 | 30 | This command asks for your AWS S3 compatable storage credentials (Tested with AWS S3, DigitalOcean Spaces, BackBlaze). And it will produces a group file example:- **avengers-group.ini** 31 | 32 | You as group admin share this file with other members in your group with email or other means. Once the memebers received this file, they can join the group as. 33 | 34 | $ ele join avengers-group.ini 35 | 36 | Now you can upload download files between your group by simply using file-indexes. 37 | 38 | To upload a file called "project-xyz-version123.zip" 39 | $ ele upload project-xyz-version123.zip 40 | > this will return a file-number ex:- 101 41 | 42 | Your group memebers can download this file by running 43 | 44 | $ ele download 101 45 | 46 | Thats all, please share your feedback and suggestions. 47 | 48 | 49 | -------------------------------------------------------------------------------- /ele.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const myProgVersion = require("./package.json").version; 4 | const prog = require("caporal"); 5 | 6 | const CONSTANTS = require('./CONSTANTS') 7 | 8 | const { DeleteFile } = require("./model/delete"); 9 | const { CreateGroup } = require("./model/creategroup"); 10 | const { JoinGroup } = require("./model/joingroup"); 11 | const { Upload } = require("./model/upload"); 12 | const { Download } = require("./model/download"); 13 | 14 | // ----------- upload --------------- 15 | prog.version(myProgVersion) 16 | 17 | .command("upload", "Upload a file") //.alias("u") 18 | .argument("<" + CONSTANTS.CLI_KEY_FILE_NAME + ">", "File name to upload") 19 | .option( 20 | "--public", 21 | "Upload file to public directory (anyone with link can download it)" 22 | ) 23 | .action(function (args, options, logger) { 24 | let up = new Upload(args, options); 25 | up.action(); 26 | }); 27 | 28 | // ----------- download ---------- 29 | prog.version(myProgVersion) 30 | .command("download", "Download a file") //.alias("d") 31 | .argument("<" + CONSTANTS.CLI_USER_ENTERED_FILE_NUMBER + ">", "File number to download", prog.INT) 32 | .action(function (args, options, logger) { 33 | let downlo = new Download(args); 34 | downlo.action(); 35 | }); 36 | 37 | // ----------- join-group ---------- 38 | prog.version(myProgVersion) 39 | .command("join", "Join a group").alias("join-group") 40 | .argument("<" + CONSTANTS.CLI_JOIN_GROUP_CONFIG_FILE + ">", "Group information config file") 41 | .action(function (args, options, logger) { 42 | let jg = new JoinGroup(args); 43 | jg.action(); 44 | }); 45 | 46 | // ----------- create-group ---------- 47 | prog.version(myProgVersion) 48 | .command("create-group", "Create a group") 49 | .option( 50 | "--credentials ", 51 | "credentials file (not the aws credentials file)" 52 | ) 53 | .action(function (args, options, logger) { 54 | let cg = new CreateGroup(args, options); 55 | cg.action(); 56 | }); 57 | 58 | // ----------- delete ---------- 59 | prog.version(myProgVersion) 60 | .command("delete", "Delete a file") //.alias("d") 61 | .argument("<" + CONSTANTS.CLI_USER_ENTERED_FILE_NUMBER + ">", "File number to delete", prog.INT) 62 | .action(function (args, options, logger) { 63 | let de = new DeleteFile(args, options); 64 | de.action(); 65 | }); 66 | 67 | 68 | prog.name("ele").parse(process.argv); -------------------------------------------------------------------------------- /model/basecommand.js: -------------------------------------------------------------------------------- 1 | class BaseCommand { 2 | /** 3 | *Creates an instance of BaseCommand. All the CLI commands implements this class. 4 | * @param {*} args - arguments passed for the command 5 | * @param {*} [options=null] - command options 6 | * @memberof BaseCommand 7 | */ 8 | constructor(args, options = null) { 9 | this.args = args; 10 | this.options = options; 11 | } 12 | action() { 13 | console.log('Base class Command::action()'); 14 | } 15 | getArgsValue(key) { 16 | return this.args[key]; 17 | } 18 | } 19 | 20 | module.exports = { 21 | BaseCommand 22 | }; -------------------------------------------------------------------------------- /model/creategroup.js: -------------------------------------------------------------------------------- 1 | const utils = require('../utils/utils') 2 | const CONSTANTS = require('../CONSTANTS') 3 | const { BUCKET_NAME, GROUP_NAME, GROUP_ID, DEBUG } = require('../CONSTANTS') 4 | const { BaseCommand } = require("./basecommand") 5 | 6 | const axios = require('axios') 7 | const ini = require('ini') 8 | const fs = require('fs') 9 | 10 | 11 | 12 | var readlineSync = require('readline-sync') 13 | 14 | class CreateGroup extends BaseCommand { 15 | 16 | /** 17 | * Creates an instance of CreateGroup. 18 | * @param {*} args - arguments passed to the command. 19 | * @param {*} options - options passed to the command. 20 | * @memberof CreateGroup 21 | */ 22 | constructor(args, options) { 23 | super(args, options); 24 | } 25 | 26 | /** 27 | * This method prompts user to enter their s3 credentials. 28 | */ 29 | ask_credentials() { 30 | let s3_storage_type; 31 | let s3_storage_service = readlineSync.question(' \nChoose your storage service provider \n[1] AWS Amazon Storage Bucket \n[2] Digital Ocean Spaces \n[3] BackBlaze B2 \n>'); 32 | if (s3_storage_service == 1) { 33 | s3_storage_type = CONSTANTS.AWS_AMAZON_STORAGE_BUCKET; 34 | } else if (s3_storage_service == 2) { 35 | s3_storage_type = CONSTANTS.DIGITAL_OCEAN_SPACES; 36 | } else if (s3_storage_service == 3) { 37 | s3_storage_type = CONSTANTS.BACK_BLAZE_B2; 38 | } else { 39 | console.log("Please enter the valid option"); 40 | process.exit() 41 | } 42 | let s3_access_key = readlineSync.question(' \nPlease enter s3 access key: '); 43 | let s3_secret_access_key = readlineSync.question(' \nPlease enter s3 secret access key: '); 44 | let s3_bucket_name = readlineSync.question(' \nPlease enter s3 bucket name: '); 45 | let file_data = "[" + CONSTANTS.DEFAULT + "]" + "\n" + CONSTANTS.ACCESS_KEY + "=" + s3_access_key + "\n" + CONSTANTS.SECRET_ACCESS_KEY + "=" + s3_secret_access_key + "\n" + CONSTANTS.BUCKET_NAME + "=" + s3_bucket_name + "\n" + CONSTANTS.S3_SERVER_ENDPOINT + "=" + s3_storage_type + "\n" + CONSTANTS.PUBLIC_SHARED_FOLDER + "=public" + "\n"; 46 | fs.writeFileSync(CONSTANTS.CONFIG_FILE, file_data, CONSTANTS.UTF_8); 47 | return CONSTANTS.CONFIG_FILE; 48 | 49 | } 50 | 51 | /** 52 | * This method prompts user to enter their group name. 53 | */ 54 | ask_group_name() { 55 | var groupName = readlineSync.question(' \nPlease enter group name: '); 56 | return groupName; 57 | } 58 | 59 | /** 60 | * This method stores the group credentials locally and calls the server to create the group. 61 | */ 62 | action() { 63 | let group_config_file_name = this.ask_credentials() 64 | let new_group_name = this.ask_group_name(); 65 | let user_given_config = ini.parse(fs.readFileSync(group_config_file_name, CONSTANTS.UTF_8)) 66 | let user_given_bucket_name = user_given_config[CONSTANTS.DEFAULT][BUCKET_NAME]; 67 | this.call_server_to_create_group(user_given_config, new_group_name, user_given_bucket_name) 68 | } 69 | 70 | /** 71 | * 72 | * This method takes the user configured information and creates the new document in the firebase store. 73 | * @param {*} user_given_config - User S3 credentials. 74 | * @param {*} new_group_name - Name of the group to be created. 75 | * @param {*} user_given_bucket_name - Bucket name where the group need to be created. 76 | * @memberof CreateGroup 77 | */ 78 | call_server_to_create_group(user_given_config, new_group_name, user_given_bucket_name) { 79 | let local_group_data = { 80 | [BUCKET_NAME]: user_given_bucket_name, 81 | [GROUP_NAME]: new_group_name 82 | } 83 | axios.post(CONSTANTS.FIRESSTORE_URL_TO_CREATE_GROUP, local_group_data).then((response) => { 84 | 85 | let new_group_id = response.data[GROUP_ID] 86 | console.log(response.data); 87 | if (response.data[CONSTANTS.STATUS] == CONSTANTS.FAILURE) { 88 | console.log("Unable to create new group", response.data); 89 | } else { 90 | let new_group_file_name = utils.append_group_name_id(user_given_config, new_group_name, new_group_id) 91 | utils.copy_group_config_to_dot_ele_dir(new_group_file_name) 92 | } 93 | }, (err) => { 94 | if (DEBUG) console.log(err); else console.log('Error-213: in creating group'); 95 | }); 96 | } 97 | } 98 | 99 | module.exports = { 100 | CreateGroup 101 | }; -------------------------------------------------------------------------------- /model/delete.js: -------------------------------------------------------------------------------- 1 | const utils = require('../utils/utils') 2 | const CONSTANTS = require('../CONSTANTS') 3 | const { BUCKET_NAME, GROUP_NAME, GROUP_ID, FILE_INDEX_FROM_SERVER, DEBUG, CLI_USER_ENTERED_FILE_NUMBER } = require('../CONSTANTS') 4 | const { BaseCommand } = require("./basecommand") 5 | const axios = require('axios') 6 | const s3utils = require('../utils/s3utils') 7 | 8 | 9 | class DeleteFile extends BaseCommand { 10 | /** 11 | *Creates an instance of DeleteFile. 12 | * @param {*} args - Arguments passed to the command, user entered file number. 13 | * @param {*} options - Options of the command 14 | * @memberof DeleteFile 15 | */ 16 | constructor(args, options) { 17 | super(args, options) 18 | } 19 | action() { 20 | const bucket_name = utils.get_config_value(BUCKET_NAME) 21 | const group_name = utils.get_config_value(GROUP_NAME) 22 | const group_id = utils.get_config_value(GROUP_ID) 23 | 24 | const file_index = this.getArgsValue(CLI_USER_ENTERED_FILE_NUMBER); 25 | const local_group_data = { [BUCKET_NAME]: bucket_name, [GROUP_ID]: group_id, [GROUP_NAME]: group_name, [FILE_INDEX_FROM_SERVER]: file_index } 26 | 27 | axios.post(CONSTANTS.FIRESSTORE_URL_TO_GET_FILE_DATA, local_group_data).then((response) => { 28 | let file_data_from_db = response.data; 29 | if (file_data_from_db["delete_status"] == true) { 30 | console.log("\n File number : " + file_index + " is deleted \n") 31 | } else { 32 | let s3_file_obj_name = file_data_from_db["s3_url"] 33 | s3utils.delete_file_from_s3(bucket_name, s3_file_obj_name, (data) => { 34 | this.update_delete_status_in_firestore(bucket_name, group_id, group_name, file_index) 35 | }) 36 | } 37 | }, (err) => { 38 | if (DEBUG) 39 | console.log(err); 40 | }); 41 | } 42 | /** 43 | *This method updates the delete status in the firebase datastore. 44 | * @param {*} bucket_name - Bucket name from where the file has to be deleted. 45 | * @param {*} group_id - Group ID to which the file index belongs to. 46 | * @param {*} group_name - Group Name to which the file index belongs to. 47 | * @param {*} file_index - file index of the file to be deleted. 48 | * @memberof DeleteFile 49 | */ 50 | update_delete_status_in_firestore(bucket_name, group_id, group_name, file_index) { 51 | const delete_file_status_data = { [BUCKET_NAME]: bucket_name, [GROUP_ID]: group_id, [GROUP_NAME]: group_name, [FILE_INDEX_FROM_SERVER]: file_index } 52 | axios.post(CONSTANTS.FIRESSTORE_URL_TO_UPDATE_DELETE_STATUS, delete_file_status_data).then((response) => { 53 | console.log("\n File Deleted \n"); 54 | }, (err) => { 55 | if (DEBUG) console.log(err); else console.log('Error-45: in deleting file number'); 56 | }); 57 | } 58 | } 59 | 60 | module.exports = { 61 | DeleteFile 62 | }; -------------------------------------------------------------------------------- /model/download.js: -------------------------------------------------------------------------------- 1 | const utils = require('../utils/utils') 2 | const CONSTANTS = require('../CONSTANTS') 3 | const { BUCKET_NAME, GROUP_NAME, GROUP_ID, FILE_INDEX_FROM_SERVER, DEBUG, CLI_USER_ENTERED_FILE_NUMBER } = require('../CONSTANTS') 4 | const s3utils = require('../utils/s3utils') 5 | const { BaseCommand } = require("./basecommand") 6 | 7 | const axios = require('axios') 8 | 9 | class Download extends BaseCommand { 10 | 11 | /** 12 | * Creates an instance of Download. 13 | * @param {*} args - File number to be downloaded. 14 | * @memberof Download 15 | */ 16 | constructor(args) { 17 | super(args); 18 | } 19 | 20 | /** 21 | * This method takes file number to be downloaded and calls server to get corresponding file name and downloads the file locally. 22 | */ 23 | action() { 24 | const bucket_name = utils.get_config_value(BUCKET_NAME) 25 | const group_name = utils.get_config_value(GROUP_NAME) 26 | const group_id = utils.get_config_value(GROUP_ID) 27 | 28 | const file_index = this.getArgsValue(CLI_USER_ENTERED_FILE_NUMBER); 29 | const local_group_data = { [BUCKET_NAME]: bucket_name, [GROUP_ID]: group_id, [GROUP_NAME]: group_name, [FILE_INDEX_FROM_SERVER]: file_index } 30 | 31 | axios.post(CONSTANTS.FIRESSTORE_URL_TO_GET_FILE_DATA, local_group_data).then((response) => { 32 | let file_data_from_db = response.data; 33 | if (file_data_from_db["delete_status"] == true) { 34 | console.log("\n File number : " + file_index + " is deleted \n") 35 | } 36 | else { 37 | 38 | let s3_file_obj_name = file_data_from_db["s3_url"] 39 | let local_file_path = utils.retrive_file_name_only(s3_file_obj_name) 40 | const bucket_name = utils.get_config_value(BUCKET_NAME) 41 | s3utils.download_file_from_s3(bucket_name, s3_file_obj_name, local_file_path) 42 | } 43 | }, (err) => { 44 | if (DEBUG) console.log(err); else console.log('Error-124: in downloading file'); 45 | }); 46 | } 47 | } 48 | 49 | module.exports = { 50 | Download 51 | } -------------------------------------------------------------------------------- /model/joingroup.js: -------------------------------------------------------------------------------- 1 | const utils = require('../utils/utils') 2 | const CONSTANTS = require('../CONSTANTS') 3 | const { BaseCommand } = require("./basecommand") 4 | 5 | class JoinGroup extends BaseCommand { 6 | /** 7 | *Creates an instance of JoinGroup. 8 | * @param {*} args - Arguments passed to the command, group credential file 9 | * @memberof JoinGroup 10 | */ 11 | constructor(args) { 12 | super(args) 13 | } 14 | action() { 15 | var group_config_name = this.args[CONSTANTS.GROUP_CONFIG_FILE] 16 | utils.copy_group_config_to_dot_ele_dir(group_config_name) 17 | } 18 | } 19 | module.exports = { 20 | JoinGroup 21 | }; -------------------------------------------------------------------------------- /model/upload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module contains class that uploads the 3 | */ 4 | const utils = require('../utils/utils') 5 | const CONSTANTS = require('../CONSTANTS') 6 | const { BUCKET_NAME, GROUP_NAME, GROUP_ID, FILE_INDEX_FROM_SERVER, HTTPS, DEBUG, 7 | PUBLIC_SHARED_FOLDER, S3_SERVER_ENDPOINT, CLI_KEY_FILE_NAME } = require('../CONSTANTS') 8 | const s3utils = require('../utils/s3utils') 9 | const { BaseCommand } = require("./basecommand") 10 | 11 | const axios = require('axios') 12 | const chalk = require('chalk') 13 | 14 | class Upload extends BaseCommand { 15 | /** 16 | *Creates an instance of Upload. 17 | * @param {*} args - Arguments passed to the command, name of file to be uploaded. 18 | * @param {*} options - options of the command. 19 | * @memberof Upload 20 | */ 21 | constructor(args, options) { 22 | super(args, options); 23 | } 24 | action() { 25 | 26 | let epoch_time = (new Date()) / 1000; 27 | let local_file_path = utils.check_file_exists(this.getArgsValue(CLI_KEY_FILE_NAME)) 28 | if (local_file_path) { 29 | const bucket_name = utils.get_config_value(BUCKET_NAME) 30 | const group_name = utils.get_config_value(GROUP_NAME) 31 | const group_id = utils.get_config_value(GROUP_ID) 32 | const public_shared_folder = utils.get_config_value(PUBLIC_SHARED_FOLDER) 33 | const s3_server_endpoint = utils.get_config_value(S3_SERVER_ENDPOINT) 34 | 35 | let s3_file_obj_name = group_name + "/" + epoch_time + "/" + local_file_path 36 | let s3_full_public_path = null; 37 | const DOT = "."; 38 | const PUBLIC_FILE_PERMISSION = 'public-read' 39 | let is_public = null; 40 | if (this.options.public && public_shared_folder) { 41 | s3_file_obj_name = public_shared_folder + "/" + s3_file_obj_name; 42 | s3_full_public_path = HTTPS + bucket_name + DOT + s3_server_endpoint + "/" + s3_file_obj_name; 43 | is_public = PUBLIC_FILE_PERMISSION; 44 | } 45 | 46 | 47 | s3utils.upload_to_s3(local_file_path, bucket_name, s3_file_obj_name, is_public, (data) => { 48 | this.store_file_path_in_firestore(bucket_name, group_id, group_name, s3_file_obj_name, s3_full_public_path) 49 | }); 50 | } 51 | } 52 | /** 53 | * This method adds the url to the firebase database and gets the file index number. 54 | * @param {*} bucket_name - Bucket name where file has to be uploaded. 55 | * @param {*} group_id - Group ID of the group 56 | * @param {*} group_name - Group name of the group to which file has to be uploaded. 57 | * @param {*} s3_file_obj_name - S3 object name of the file to be uploaded. 58 | * @param {*} s3_full_public_path - S3 full URL to the file in storage. 59 | * @memberof Upload 60 | */ 61 | store_file_path_in_firestore(bucket_name, group_id, group_name, s3_file_obj_name, s3_full_public_path) { 62 | let upload_file_data = { 63 | "bucket_name": bucket_name, 64 | "group_id": group_id, 65 | "group_name": group_name, 66 | "s3_url": s3_file_obj_name 67 | } 68 | axios.post(CONSTANTS.FIRESSTORE_URL_TO_GET_NUMBER, upload_file_data).then((response) => { 69 | 70 | let pub_url = ""; 71 | if (s3_full_public_path) { 72 | pub_url = " \n\n " + s3_full_public_path; 73 | } 74 | 75 | console.log(" \n "); 76 | console.log("Success! file number: " + chalk.red(response.data[FILE_INDEX_FROM_SERVER])); 77 | console.log("\n" + pub_url, " \n\n "); 78 | }, (err) => { 79 | if (DEBUG) console.log(err); else console.log('Error-96: in fetching file number'); 80 | }); 81 | } 82 | } 83 | 84 | module.exports = { 85 | Upload 86 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloele", 3 | "version": "0.1.5", 4 | "description": "Simple file copy for devs", 5 | "main": "ele.js", 6 | "bin": { 7 | "ele": "ele.js" 8 | }, 9 | "scripts": { 10 | "test": "test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/prakis/helloele.git" 15 | }, 16 | "author": "Kishore", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/prakis/helloele/issues" 20 | }, 21 | "homepage": "https://helloele.com", 22 | "dependencies": { 23 | "aws-sdk": "^2.679.0", 24 | "axios": "^0.19.2", 25 | "caporal": "prakis/Caporal.js", 26 | "chalk": "^4.0.0", 27 | "ini": "^1.3.5", 28 | "mkdirp": "^1.0.4", 29 | "prompts": "^2.3.2", 30 | "readline-sync": "^1.4.10" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /utils/s3utils.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const fs = require('fs'); 3 | 4 | const utils = require('./utils'); 5 | const CONSTANTS = require('../CONSTANTS') 6 | const { HTTPS, DEBUG } = require('../CONSTANTS') 7 | 8 | /** 9 | * This function updates the AWS credentials provided by the user. 10 | * @returns 11 | */ 12 | function init_s3() { 13 | 14 | let endpointUrl = HTTPS + utils.get_config_value(CONSTANTS.S3_SERVER_ENDPOINT); 15 | AWS.config.update({ 16 | accessKeyId: utils.get_config_value(CONSTANTS.ACCESS_KEY), 17 | secretAccessKey: utils.get_config_value(CONSTANTS.SECRET_ACCESS_KEY), 18 | endpoint: endpointUrl 19 | }); 20 | let s3 = new AWS.S3(); 21 | return s3; 22 | } 23 | 24 | /** 25 | * This function downloads the given file number from the S3 store. 26 | * @param {*} bucket_name - Bucket name from where the file has to be downloaded. 27 | * @param {*} s3_file_obj_name - S3 url to download the file. 28 | * @param {*} local_file_path - Path where the file has to be saved. 29 | */ 30 | function download_file_from_s3(bucket_name, s3_file_obj_name, local_file_path) { 31 | let s3 = init_s3(); 32 | 33 | var fileStream = fs.createWriteStream(local_file_path); 34 | var s3Stream = s3.getObject({ Bucket: bucket_name, Key: s3_file_obj_name }).createReadStream(); 35 | 36 | s3Stream.on('error', function (err) { 37 | console.error(err); // NoSuchKey: The specified key does not exist 38 | }); 39 | 40 | s3Stream.pipe(fileStream).on('error', function (err) { 41 | // capture any errors that occur when writing data to the file 42 | if (DEBUG) console.log(err); else console.log('Error-37: Error in downlading file '); 43 | }).on('close', function () { 44 | console.log(' \n\n Download Complete: ', local_file_path, " \n\n "); 45 | }); 46 | } 47 | 48 | /** 49 | * This function upload the given file to the S3 store. 50 | * 51 | * @param {*} local_file_path - Path of the file need to be uploaded. 52 | * @param {*} bucket_name - Bucket name where the file has to be uploaded. 53 | * @param {*} s3_file_obj_name - S3 url to upload the file. 54 | * @param {*} public_permision 55 | * @param {*} callback 56 | */ 57 | function upload_to_s3(local_file_path, bucket_name, s3_file_obj_name, public_permision, callback) { 58 | let s3 = init_s3(); 59 | var params = { 60 | Bucket: bucket_name, 61 | Body: fs.createReadStream(local_file_path), 62 | Key: s3_file_obj_name 63 | }; 64 | 65 | if (public_permision) { 66 | params['ACL'] = 'public-read'; 67 | } 68 | s3.upload(params, function (err, data) { 69 | //handle error 70 | if (err) { 71 | if (DEBUG) console.log(err); else console.log('Error-59: Error in uploading file '); 72 | } 73 | 74 | //success 75 | if (data) { 76 | callback(data); 77 | } 78 | }); 79 | } 80 | 81 | /** 82 | * This function deletes the given file number from the S3 store. 83 | * 84 | * @param {*} bucket_name - Bucket name where the file has to be deleted. 85 | * @param {*} s3_file_path - S3 url to delete the file 86 | * @param {*} callback 87 | */ 88 | function delete_file_from_s3(bucket_name, s3_file_path, callback) { 89 | let s3 = init_s3(); 90 | 91 | var params = { 92 | Bucket: bucket_name, 93 | Delete: { 94 | Objects: [ 95 | { 96 | Key: s3_file_path 97 | } 98 | ], 99 | }, 100 | }; 101 | 102 | s3.deleteObjects(params, function (err, data) { 103 | //handle error 104 | if (err) { 105 | if (DEBUG) console.log(err); else console.log('Error-87: Error in deleting file '); 106 | } 107 | 108 | //success 109 | if (data) { 110 | callback(data); 111 | } 112 | }); 113 | } 114 | 115 | module.exports = { 116 | init_s3, 117 | upload_to_s3, 118 | download_file_from_s3, 119 | delete_file_from_s3 120 | } -------------------------------------------------------------------------------- /utils/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const ini = require('ini') 3 | 4 | const join = require("path").join; 5 | const mkdirp = require('mkdirp') 6 | const crypto = require('crypto') 7 | const path = require('path') 8 | const chalk = require('chalk') 9 | 10 | const CONSTANTS = require('../CONSTANTS') 11 | 12 | var iv = Buffer.from('26ae5cc854e36b6bdfca366848dea6bb', 'hex'); 13 | 14 | 15 | // -------------- 16 | /** 17 | * 18 | * 19 | * @param {*} local_file_path 20 | * @returns 21 | */ 22 | function check_file_exists(local_file_path) { 23 | if (null == local_file_path) { 24 | console.log("Please enter file name") 25 | return null; 26 | } 27 | if (true == is_file_exists(local_file_path)) { 28 | return local_file_path 29 | } else { 30 | console.log("File doesn't exist") 31 | return null; 32 | } 33 | } 34 | /** 35 | * 36 | * 37 | * @param {*} file_path 38 | * @param {*} max_size 39 | * @returns 40 | */ 41 | function is_file_size_in_limits(file_path, max_size) { 42 | if (is_file_exists(file_path)) { 43 | if (get_file_size_in_mb(file_path) < max_size) { 44 | return true; 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | /** 51 | * 52 | * 53 | * @param {*} filename 54 | * @returns 55 | */ 56 | function get_file_size_in_mb(filename) { 57 | var stats = fs.statSync(filename) 58 | var fileSizeInBytes = stats["size"] 59 | var fileSizeInMegabytes = fileSizeInBytes / 1000000.0 60 | return fileSizeInMegabytes 61 | } 62 | 63 | /** 64 | * 65 | * 66 | * @returns 67 | */ 68 | function expand_home_dir() { 69 | let path = "~"; 70 | var homedir = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; 71 | 72 | if (!path) return path; 73 | if (path == '~') return homedir; 74 | if (path.slice(0, 2) != '~/') return path; 75 | return join(homedir, path.slice(2)); 76 | } 77 | 78 | /** 79 | * 80 | * 81 | * @param {*} path 82 | * @returns 83 | */ 84 | function is_file_exists(path) { 85 | return fs.existsSync(path); 86 | } 87 | function get_home_ele_dir() { 88 | var home_ele_dir_path = join(expand_home_dir(), CONSTANTS.HOME_ELE_DIR) 89 | if (!fs.existsSync(home_ele_dir_path)) { 90 | const made = mkdirp.sync(home_ele_dir_path) 91 | } 92 | return home_ele_dir_path 93 | } 94 | /** 95 | * 96 | * 97 | * @param {*} s3_file_obj_name 98 | * @returns 99 | */ 100 | function retrive_file_name_only(s3_file_obj_name) { 101 | return path.parse(s3_file_obj_name).base; 102 | } 103 | 104 | //-------------- 105 | var config = null; 106 | 107 | /** 108 | * 109 | * 110 | * @param {*} key 111 | * @returns 112 | */ 113 | function get_config_value(key) { 114 | return get_config()[CONSTANTS.DEFAULT][key] 115 | } 116 | /** 117 | * 118 | * 119 | * @returns 120 | */ 121 | function get_config() { 122 | if (null == config) { 123 | var conf_file_path = join(get_home_ele_dir(), CONSTANTS.CONFIG_FILE_ENCRYPTED) 124 | if (!is_file_exists(conf_file_path)) { 125 | console.log("Config file doesnt exists"); 126 | } 127 | config = ini.parse(fs.readFileSync(conf_file_path, CONSTANTS.UTF_8)) 128 | } 129 | return config; 130 | } 131 | /** 132 | * 133 | * 134 | * @param {*} file_path 135 | */ 136 | function copy_group_config_to_dot_ele_dir(file_path) { 137 | var home_ele_config_path = join(get_home_ele_dir(), CONSTANTS.CONFIG_FILE_ENCRYPTED) 138 | 139 | try { 140 | fs.copyFileSync(file_path, home_ele_config_path); 141 | } catch (err) { 142 | console.log("Error copying group config file"); 143 | throw err; 144 | } 145 | console.log("\n Success!") 146 | } 147 | 148 | /** 149 | * 150 | * 151 | * @param {*} user_given_config 152 | * @param {*} new_group_name 153 | * @param {*} new_group_id 154 | * @returns 155 | */ 156 | function append_group_name_id(user_given_config, new_group_name, new_group_id) { 157 | user_given_config[CONSTANTS.DEFAULT][CONSTANTS.GROUP_NAME] = new_group_name 158 | user_given_config[CONSTANTS.DEFAULT][CONSTANTS.GROUP_ID] = new_group_id 159 | let new_group_plain_file_name = new_group_name + ".ini" 160 | 161 | fs.writeFileSync(new_group_plain_file_name, ini.stringify(user_given_config)) 162 | 163 | console.log("\n1) Created group setting file in current dir: '", chalk.blue(new_group_plain_file_name) + "'") 164 | console.log("\n2) Share this file with your group members") 165 | console.log("\n3) To join this group run $ ele join " + chalk.blue(new_group_plain_file_name)) 166 | 167 | return new_group_plain_file_name 168 | } 169 | /** 170 | * 171 | * 172 | * @param {*} user_given_config 173 | * @param {*} new_group_name 174 | * @param {*} new_group_id 175 | * @returns 176 | */ 177 | function append_group_name_id_and_encrypt(user_given_config, new_group_name, new_group_id) { 178 | user_given_config[CONSTANTS.DEFAULT][CONSTANTS.GROUP_NAME] = new_group_name 179 | user_given_config[CONSTANTS.DEFAULT][CONSTANTS.GROUP_ID] = new_group_id 180 | let new_group_plain_file_name = new_group_name + ".ini" 181 | 182 | fs.writeFileSync(new_group_plain_file_name, ini.stringify(user_given_config)) 183 | 184 | return _encrypt_group_file(new_group_plain_file_name, new_group_name) 185 | } 186 | /** 187 | * 188 | * 189 | * @param {*} group_config_file_name 190 | * @param {*} new_group_name 191 | * @returns 192 | */ 193 | function _encrypt_group_file(group_config_file_name, new_group_name) { 194 | let new_group_encrypted_file_name = new_group_name + ".encrypted" 195 | try { 196 | _encrpt_file(group_config_file_name, new_group_encrypted_file_name); 197 | } catch (err) { 198 | console.log("Error copying group config file"); 199 | throw err; 200 | } 201 | console.log("\n1) Created group setting file in current dir: '", chalk.blue(new_group_encrypted_file_name) + "'") 202 | console.log("\n2) Share this file with your group members") 203 | console.log("\n3) To join this group run $ ele join " + chalk.blue(new_group_encrypted_file_name)) 204 | 205 | return new_group_encrypted_file_name; 206 | } 207 | 208 | /** 209 | * 210 | * 211 | * @param {*} group_settings_input 212 | * @param {*} group_settings_output 213 | * @param {*} [password=CONSTANTS.DEFAULT_PASSWORD] 214 | */ 215 | function _encrpt_file(group_settings_input, group_settings_output, password = CONSTANTS.DEFAULT_PASSWORD) { 216 | const input_file_content = fs.readFileSync(group_settings_input, CONSTANTS.UTF_8); 217 | var encrpted_data = _encrpt_data(input_file_content, password); 218 | 219 | fs.writeFileSync(group_settings_output, encrpted_data, CONSTANTS.UTF_8); 220 | } 221 | /** 222 | * 223 | * 224 | * @param {*} group_settings_input 225 | * @param {*} [group_settings_output=null] 226 | * @param {*} [password=CONSTANTS.DEFAULT_PASSWORD] 227 | * @returns 228 | */ 229 | function _decrypt_file(group_settings_input, group_settings_output = null, password = CONSTANTS.DEFAULT_PASSWORD) { 230 | try { 231 | const input_file_content = fs.readFileSync(group_settings_input, CONSTANTS.UTF_8); 232 | var decrypted_data = _decrypt_data(input_file_content, password); 233 | 234 | if (null == group_settings_output) { 235 | return decrypted_data; 236 | } else { 237 | fs.writeFileSync(group_settings_output, decrypted_data, CONSTANTS.UTF_8); 238 | } 239 | 240 | } catch (err) { 241 | console.log("Error in copying group config file", err); 242 | } 243 | } 244 | 245 | 246 | /** 247 | * 248 | * 249 | * @param {*} password 250 | * @returns 251 | */ 252 | function _get_hash_digest_key(password) { 253 | const hash = crypto.createHash('sha256'); //'sha256'); 254 | hash.update(password); 255 | return hash.digest(); 256 | } 257 | /** 258 | * 259 | * 260 | * @param {*} data 261 | * @param {*} password 262 | * @returns 263 | */ 264 | function _encrpt_data(data, password) { 265 | 266 | var hash_digest_key = _get_hash_digest_key(password); 267 | var cipher = crypto.createCipheriv(CONSTANTS.ALGORITHM, hash_digest_key, iv) //createCipher(ALGORITHM, password) 268 | var encrypted = cipher.update(data, 'utf8', 'hex') 269 | encrypted += cipher.final('hex'); 270 | return encrypted; 271 | } 272 | /** 273 | * 274 | * 275 | * @param {*} data 276 | * @param {*} password 277 | * @returns 278 | */ 279 | function _decrypt_data(data, password) { 280 | 281 | var hash_digest_key = _get_hash_digest_key(password); 282 | 283 | var decipher = crypto.createDecipheriv(CONSTANTS.ALGORITHM, hash_digest_key, iv) //createDecipher(ALGORITHM, password) 284 | var decrypted = decipher.update(data, 'hex', 'utf8') 285 | decrypted += decipher.final('utf8'); 286 | return decrypted; 287 | } 288 | 289 | module.exports = { 290 | get_home_ele_dir, 291 | get_file_size_in_mb, 292 | is_file_exists, 293 | retrive_file_name_only, 294 | check_file_exists, 295 | 296 | get_config, 297 | get_config_value, 298 | copy_group_config_to_dot_ele_dir, 299 | append_group_name_id_and_encrypt, 300 | append_group_name_id 301 | } --------------------------------------------------------------------------------