diff --git a/README.md b/README.md index 8d3f288..cc249b3 100644 --- a/README.md +++ b/README.md @@ -1 +1,90 @@ -# node-nodejs-FileManager \ No newline at end of file +# node-nodejs: File Manager + +### Description +The file manager can do the following: +- Operate using the CLI +- Perform basic file operations (copy, move, delete, rename, etc.) +- Use Streams API +- Get information about the operating system of the host machine +- Perform hash calculations +- Compress and decompress files +### How to run +```bash +npm run start -- --username=your_username +``` +### List of operations and their syntax: +- Navigation & working directory (nwd) + - Go upper from current directory + ```bash + up + ``` + - Go to dedicated folder from current directory (`path_to_directory` can be relative or absolute) + ```bash + cd path_to_directory + ``` + - List all files and folder in current directory and print it to console + ```bash + ls + ``` +- Basic operations with files + - Read file and print it's content in console: + ```bash + cat path_to_file + ``` + - Create empty file in current working directory: + ```bash + add new_file_name + ``` + - Rename file: + ```bash + rn path_to_file new_filename + ``` + - Copy file: + ```bash + cp path_to_file path_to_new_directory + ``` + - Move file (same as copy but initial file is deleted): + ```bash + mv path_to_file path_to_new_directory + ``` + - Delete file: + ```bash + rm path_to_file + ``` +- Operating system info (prints following information in console) + - Get EOL (default system End-Of-Line) + ```bash + os --EOL + ``` + - Get host machine CPUs info (overall amount of CPUS plus model and clock rate (in GHz) for each of them) + ```bash + os --cpus + ``` + - Get home directory: + ```bash + os --homedir + ``` + - Get current *system user name* + ```bash + os --username + ``` + - Get CPU architecture for which Node.js binary has compiled + ```bash + os --architecture + ``` +- Hash calculation + - Calculate hash for file and print it into console + ```bash + hash path_to_file + ``` +- Compress and decompress operations + - Compress file (using Brotli algorithm) + ```bash + compress path_to_file path_to_destination + ``` + - Decompress file (using Brotli algorithm) + ```bash + decompress path_to_file path_to_destination + ``` + _If the second `path_to_destination` parameter is not specified, the operation will be performed in the current directory_ + diff --git a/index.js b/index.js new file mode 100644 index 0000000..63862c8 --- /dev/null +++ b/index.js @@ -0,0 +1,102 @@ +import * as readline from 'node:readline'; +import { getUserName } from './src/cli/getUserName.js'; +import { goUpDirectory,goToDirectory, getListDirectory } from "./src/nav/navigation.js"; +import { readFile } from "./src/files/readFile.js"; +import { createFile } from "./src/files/createFile.js"; +import { renameFile } from "./src/files/renameFile.js"; +import { copyFile } from "./src/files/copyFile.js"; +import { moveFile } from "./src/files/moveFile.js"; +import { osCommandHandler } from "./src/os/os.js"; +import { calcHash } from "./src/hash/calcHash.js"; +import { compressFile } from "./src/zip/compress.js"; +import { decompressFile } from "./src/zip/decompress.js"; +import { deleteFile } from "./src/files/deleteFile.js"; + +const { stdin, stdout, cwd } = process; +const homeDirectory = process.env["HOME"]; +const userName = getUserName(); +const readLineInterface = readline.createInterface({ input: stdin, output: stdout }); + +const printCurrentDir = () => process.stdout.write(`You are currently in \x1b[33m${cwd()}\n\x1b[0m`); + +(function sayHi () { + process.chdir(homeDirectory); + stdout.write(`Welcome to the File Manager, \x1b[35m${userName}!\n\x1b[0m`); + printCurrentDir(); +})(); + + +readLineInterface.on('line', async (line) => { + + const path = line.split(' ').slice(1); + const pathFile = path[0] || false; + const fileName = path[1] || false; + const pathDestination = path[1] || path[0]; + + const command = line.split(' ')[0]; + + switch (command) { + case 'up' : + await goUpDirectory(); + printCurrentDir(); + break; + case 'cd' : + await goToDirectory(pathFile); + printCurrentDir(); + break; + case 'ls' : + await getListDirectory(); + printCurrentDir(); + break; + case 'cat' : + await readFile(pathFile); + printCurrentDir(); + break; + case 'add' : + await createFile(pathFile); + printCurrentDir(); + break; + case 'rn' : + await renameFile(pathFile, fileName); + printCurrentDir(); + break; + case 'cp' : + await copyFile(pathFile, pathDestination); + printCurrentDir(); + break; + case 'mv' : + await moveFile(pathFile, pathDestination); + printCurrentDir(); + break; + case 'rm' : + await deleteFile(pathFile); + printCurrentDir(); + break; + case 'os' : + await osCommandHandler(pathFile); + printCurrentDir(); + break; + case 'hash' : + await calcHash(pathFile); + printCurrentDir(); + break; + case 'compress' : + await compressFile(pathFile, pathDestination); + printCurrentDir(); + break; + case 'decompress' : + await decompressFile(pathFile, pathDestination); + printCurrentDir(); + break; + case '.exit' : + process.exit(); + break; + default: + process.stdout.write('Invalid input\n'); + printCurrentDir(); + } + +}) + +process.on('exit', () => process.stdout.write(`Thank you for using File Manager, ${userName}!`)); +process.on('SIGINT', () => process.exit()); diff --git a/package.json b/package.json new file mode 100644 index 0000000..f8b0444 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "nodejs-filemanager", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Shuvalovrus/node-nodejs-FileManager.git" + }, + "author": "Shuvalov Konstantin", + "license": "ISC", + "bugs": { + "url": "https://github.com/Shuvalovrus/node-nodejs-FileManager/issues" + }, + "homepage": "https://github.com/Shuvalovrus/node-nodejs-FileManager#readme" +} diff --git a/src/cli/getUserName.js b/src/cli/getUserName.js new file mode 100644 index 0000000..e9e67e2 --- /dev/null +++ b/src/cli/getUserName.js @@ -0,0 +1,14 @@ +export const getUserName = () => { + + let result = 'Stranger'; + + process.argv.forEach((item) => { + + const name = item.split('=').pop(); + + if (item.startsWith('--username')) result = name ? result : name; + + }) + + return result; +} diff --git a/src/files/copyFile.js b/src/files/copyFile.js new file mode 100644 index 0000000..8bc16fc --- /dev/null +++ b/src/files/copyFile.js @@ -0,0 +1,21 @@ +import { createWriteStream, createReadStream } from "fs"; +import { pipeline } from 'stream'; +import { isAbsolute, join, parse } from "path"; + +export const copyFile = async (toReadPath,toWritePath) => { + + if (!isAbsolute(toReadPath)) toReadPath = join(process.cwd(), toReadPath); + toWritePath = join( toWritePath, parse(toReadPath).base) ; + + + const readStream = createReadStream(toReadPath); + const writeStream = createWriteStream(toWritePath); + + await pipeline ( + readStream, + writeStream, + (err) => { + if (err) console.error('Operation failed'); + } + ) +} \ No newline at end of file diff --git a/src/files/createFile.js b/src/files/createFile.js new file mode 100644 index 0000000..d809b86 --- /dev/null +++ b/src/files/createFile.js @@ -0,0 +1,15 @@ +import { createWriteStream } from 'fs'; +import { join } from "path"; + +export const createFile = async (file) => { + try { + const path = join( process.cwd(), file ); + + await createWriteStream(path).end(); + + } catch (err) { + console.error('Operation failed'); + } + + +} \ No newline at end of file diff --git a/src/files/deleteFile.js b/src/files/deleteFile.js new file mode 100644 index 0000000..e4eb738 --- /dev/null +++ b/src/files/deleteFile.js @@ -0,0 +1,13 @@ +import { unlink } from "fs/promises"; +import { isAbsolute, join } from "path"; + +export const deleteFile = async (path) => { + if (!isAbsolute(path)) path = join(process.cwd(), path); + + try { + await unlink(path) + + } catch (err) { + console.error('Operation failed'); + } +} \ No newline at end of file diff --git a/src/files/moveFile.js b/src/files/moveFile.js new file mode 100644 index 0000000..f105147 --- /dev/null +++ b/src/files/moveFile.js @@ -0,0 +1,12 @@ +import { copyFile } from "./copyFile.js"; +import { deleteFile } from "./deleteFile.js"; + +export const moveFile = async (toCopyPath, toDestinationPath) => { + try { + await copyFile(toCopyPath, toDestinationPath); + await deleteFile(toCopyPath); + + } catch (err) { + console.error('Operation failed'); + } +} \ No newline at end of file diff --git a/src/files/readFile.js b/src/files/readFile.js new file mode 100644 index 0000000..c0b1ea2 --- /dev/null +++ b/src/files/readFile.js @@ -0,0 +1,18 @@ +import { createReadStream } from 'fs'; +import { finished } from 'stream/promises' +import { join } from "path"; + +export const readFile = async (file) => { + try { + const streamPath = join( process.cwd(), file ); + + const readStream = createReadStream(streamPath); + + readStream.on('data', (chunk) => process.stdout.write(chunk + '\n')); + + await finished(readStream); + + } catch (err) { + console.error('Operation failed'); + } +} \ No newline at end of file diff --git a/src/files/renameFile.js b/src/files/renameFile.js new file mode 100644 index 0000000..3698478 --- /dev/null +++ b/src/files/renameFile.js @@ -0,0 +1,12 @@ +import { rename } from "fs/promises" + +export const renameFile = async (path, fileName) => { + + try { + + await rename(path, fileName); + + } catch (err) { + console.error('Operation failed'); + } +} \ No newline at end of file diff --git a/src/hash/calcHash.js b/src/hash/calcHash.js new file mode 100644 index 0000000..5bc7e2f --- /dev/null +++ b/src/hash/calcHash.js @@ -0,0 +1,28 @@ +import { createReadStream } from "fs"; +import { createHash } from 'crypto' +import { join } from "path"; +import { finished } from "stream/promises"; + +export const calcHash = async (path) => { + + try { + const readPath = join( process.cwd(), path ); + const readStream = createReadStream(readPath); + + readStream.on('data', (chunk) => { + + let hash = createHash('sha256'); + let updateHash = hash.update(chunk); + let hexHash = updateHash.digest('hex'); + + process.stdout.write(hexHash + '\n'); + + }) + + await finished(readStream); + + } catch (err) { + + console.error('Operation failed'); + } +} diff --git a/src/nav/navigation.js b/src/nav/navigation.js new file mode 100644 index 0000000..8367e08 --- /dev/null +++ b/src/nav/navigation.js @@ -0,0 +1,34 @@ +import { join } from 'path'; +import { readdir } from 'fs/promises'; +import { isAbsolute } from 'path'; + +export const goUpDirectory = async () => { + + try { + await process.chdir(join(process.cwd(), '../')); + } catch (err) { + console.error('Operation failed'); + } + +} + +export const goToDirectory = async (path) => { + try { + if (!isAbsolute(path)) path = join(process.cwd(), path); + await process.chdir(path) + } catch (err) { + console.error('Operation failed'); + } + +} + +export const getListDirectory = async () => { + + try { + const result = await readdir(process.cwd()); + console.log(result); + } catch (err) { + console.error('Operation failed'); + } + +} \ No newline at end of file diff --git a/src/os/os.js b/src/os/os.js new file mode 100644 index 0000000..ef785f2 --- /dev/null +++ b/src/os/os.js @@ -0,0 +1,35 @@ +import * as os from "os"; + + +export const osCommandHandler = async (arg) => { + + switch (arg) { + case '--EOL' : + process.stdout.write(JSON.stringify(os.EOL) + '\n'); + break; + + case '--cpus' : + process.stdout.write(`Total: ${ os.cpus().length + '\n' }`); + + os.cpus().forEach((item) => process.stdout.write(`Model: ${ item.model } Speed: ${ item.speed / 1000 } GHz\n`)); + break; + + case '--homedir' : + process.stdout.write(os.homedir() + '\n'); + break; + + case '--username' : + process.stdout.write(os.userInfo().username + '\n'); + break; + + case '--architecture' : + process.stdout.write(os.arch() + '\n'); + break; + + default: + console.error('Operation Failed'); + } +} + + + diff --git a/src/zip/compress.js b/src/zip/compress.js new file mode 100644 index 0000000..a317741 --- /dev/null +++ b/src/zip/compress.js @@ -0,0 +1,32 @@ +import { createReadStream, createWriteStream } from "fs"; +import { createBrotliCompress } from 'zlib' +import { join, parse, dirname, isAbsolute } from "path"; +import { pipeline } from "stream"; + +export const compressFile = async (toReadPath, toWritePath) => { + + + const readPath = !isAbsolute( toReadPath ) ? join(process.cwd(), toReadPath) : toReadPath + + let writePath = !isAbsolute( toWritePath ) ? join(process.cwd(), toWritePath) : toWritePath + + + writePath = readPath === writePath ? + + join( dirname(toWritePath) , parse(toReadPath).base + '.gz') : + join( toWritePath , parse(toReadPath).base + '.gz'); + + + const readStream = createReadStream( readPath ); + const writeStream = createWriteStream( writePath ); + + + await pipeline( + readStream, + createBrotliCompress(), + writeStream, + (err) => { + if (err) console.error('Operation Failed'); + } + ) +} diff --git a/src/zip/decompress.js b/src/zip/decompress.js new file mode 100644 index 0000000..2195544 --- /dev/null +++ b/src/zip/decompress.js @@ -0,0 +1,37 @@ +import { createReadStream, createWriteStream } from "fs"; +import { createBrotliDecompress } from 'zlib' +import {join, dirname, parse, isAbsolute} from "path"; +import { pipeline } from "stream"; + +export const decompressFile = async (toReadPath, toWritePath) => { + + const readPath = !isAbsolute( toReadPath ) ? join(process.cwd(), toReadPath) : toReadPath + + let writePath = !isAbsolute( toWritePath ) ? join(process.cwd(), toWritePath) : toWritePath + + + const unzipFileNameArr = parse(toReadPath).base.split('.') + unzipFileNameArr.length = unzipFileNameArr.length -1; + + const unzipFileName = unzipFileNameArr.join('.'); + + + writePath = writePath === readPath ? + + join( dirname(toWritePath), unzipFileName) : + join( toWritePath, unzipFileName); + + + const readStream = createReadStream( readPath ); + const writeStream = createWriteStream( writePath ); + + + await pipeline( + readStream, + createBrotliDecompress(), + writeStream, + (err) => { + if (err) console.error('Operation Failed'); + } + ) +}