Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 129 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,129 @@
# nodejs-file-manager
# NODEJS-FILE-MANAGER

A command-line file manager application built with Node.js, leveraging the filesystem and streams for efficient file manipulation. It offers functionalities such as file operations, compression and decompression, system information retrieval, and hash calculations.

## Features

- Basic file operations: **copy**, **move**, **delete**, **rename**
- **Compression** and **decompression** using the Brotli algorithm
- Retrieval of **system information** (CPU details, home directory, etc.)
- **Hash calculations** for files
- Built entirely with Node.js's built-in modules, **no external dependencies**

## Getting Started

### Prerequisites

- Node.js (version 20 LTS or newer)

### Installation

1. Clone the repository:
```bash
git clone <repository-url>
```
2. Navigate to the project directory:
```bash
cd node-js-file-manager
```

### Running the Application

Start the application with a username of your choice:
```bash
npm start -- --username=<your_username>
```

## Usage

### Basic Commands

- **Navigate Up**
```
up
```
- **Change Directory**
```
cd <directoryPath>
```
- **List Directory Contents**
```
ls
```
- **Read File**
```
cat <filename>
```
- **Create File**
```
add <newFileName>
```
- **Rename File**
```
rn <oldFileName> <newFileName>
```
- **Copy File**
```
cp <sourcePath> <destinationPath>
```
- **Move File**
```
mv <sourcePath> <destinationPath>
```
- **Delete File**
```
rm <filename>
```

### Advanced Commands

- **Calculate File Hash**
```
hash <path_to_file>
```
- **Compress File**
```
compress <path_to_file> <path_to_destination>
```
- **Decompress File**
```
decompress <path_to_compressed_file> <path_to_destination>
```

### System Information Commands

- **Display the System's End-of-Line Marker**
```
os --EOL
```
- **Display Information About the CPU(s)**
```
os --cpus
```
- **Display the Current User's Home Directory**
```
os --homedir
```
- **Display the Current System User Name**
```
os --username
```
- **Display the CPU Architecture**
```
os --architecture
```

## Exiting the Application

To exit the File Manager, enter:
```
.exit
```

## Contributing

Contributions are welcome! Feel free to submit pull requests or create issues for bugs and feature requests.

## License

This project is open source and available under the [MIT License](LICENSE).
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "nodejs-file-manager",
"version": "1.0.0",
"description": "file manager built using Node.js",
"type": "module",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node src"
},
"author": "David Gogua",
"license": "MIT"
}
125 changes: 125 additions & 0 deletions src/app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import readline from 'readline';
import os from 'os';
import fs from 'fs';
import path from 'path';
import { stdin as input, stdout as output } from 'process';

// Navigation and File Operations
import { navigateUp } from './navigation/up.js';
import { changeDirectory } from './navigation/cd.js';
import { listDirectory } from './navigation/list.js';
import { read } from './file-operations/read.js';
import { create } from './file-operations/create.js';
import { rename } from './file-operations/rename.js';
import { copy } from './file-operations/copy.js';
import { move } from './file-operations/move.js';
import { deleteFile } from './file-operations/delete.js';
import { hashFile } from './hash/hash.js';

import { printArchitecture } from './os-info/architecture.js';
import { printCPUs } from './os-info/cpus.js';
import { printEOL } from './os-info/eol.js';
import { printHomeDir } from './os-info/homedir.js';
import { printUsername } from './os-info/username.js';

import { compress } from './compression/compress.js'
import { decompress } from './compression/decompress.js'

class App {
constructor(username) {
this.username = username;
this.cwd = os.homedir();
this.rl = readline.createInterface({ input, output });
}

start() {
console.log(`Welcome to the File Manager, ${this.username}! Current directory: ${this.cwd}`);
this.rl.on('line', (line) => this.handleCommand(line.trim().split(/\s+/))).on('close', () => this.shutdown());
}

async handleCommand([cmd, ...args]) {
try {
switch (cmd) {
case 'os':
this.handleOSCommands(args[0]);
break;
case 'up':
this.cwd = navigateUp(this.cwd);
break;
case 'cd':
this.cwd = await changeDirectory(this.cwd, args[0]);
break;
case 'ls':
await listDirectory(this.cwd);
break;
case 'cat':
await read(path.join(this.cwd, args[0]));
break;
case 'add':
await create(path.join(this.cwd, args[0]));
break;
case 'rm':
await deleteFile(path.join(this.cwd, args[0]));
break;
case 'rn':
await rename(path.join(this.cwd, args[0]), path.join(this.cwd, args[1]));
break;
case 'cp':
await copy(path.join(this.cwd, args[0]), path.join(this.cwd, args[1]));
break;
case 'mv':
await move(path.join(this.cwd, args[0]), path.join(this.cwd, args[1]));
break;
case 'hash':
await hashFile(path.join(this.cwd, args[0]));
break;
case 'compress':
await compress(path.join(this.cwd, args[0]), path.join(this.cwd, args[1]));
break;
case 'decompress':
await decompress(path.join(this.cwd, args[0]), path.join(this.cwd, args[1]));
break;
case '.exit':
this.rl.close();
break;
default:
console.log('Invalid command');
break;
}
} catch (error) {
console.error(`Error: ${error.message}`);
} finally {
console.log(`Current directory: ${this.cwd}`);
}
}

handleOSCommands(option) {
switch (option) {
case '--EOL':
printEOL();
break;
case '--cpus':
printCPUs();
break;
case '--homedir':
printHomeDir();
break;
case '--username':
printUsername();
break;
case '--architecture':
printArchitecture();
break;
default:
console.log('Invalid OS command');
break;
}
}

shutdown() {
console.log(`Thank you for using File Manager, ${this.username}. Goodbye!`);
process.exit();
}
}

export default App;
41 changes: 41 additions & 0 deletions src/app/compression/compress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import fs from 'fs';
import path from 'path';
import { createBrotliCompress } from 'zlib';
import { pipeline } from 'stream';
import { promisify } from 'util';

const pipe = promisify(pipeline);

export async function compress(sourcePath, destinationDir, cwd = process.cwd()) {
try {
const fullSourcePath = path.resolve(cwd, sourcePath);
const sourceFileName = path.basename(fullSourcePath);
const compressedFileName = sourceFileName + '.br';

let finalDestinationPath;
if (await isDirectory(destinationDir)) {
finalDestinationPath = path.join(destinationDir, compressedFileName);
} else {
finalDestinationPath = destinationDir;
}

await pipe(
fs.createReadStream(fullSourcePath),
createBrotliCompress(),
fs.createWriteStream(finalDestinationPath)
);

console.log(`File compressed successfully and saved to ${finalDestinationPath}`);
} catch (error) {
console.error(`Compression failed: ${error.message}`);
}
}

async function isDirectory(path) {
try {
const stat = await fs.promises.stat(path);
return stat.isDirectory();
} catch {
return false;
}
}
26 changes: 26 additions & 0 deletions src/app/compression/decompress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import fs from 'fs';
import path from 'path';
import { createBrotliDecompress } from 'zlib';
import { pipeline } from 'stream';
import { promisify } from 'util';

const pipe = promisify(pipeline);

export async function decompress(filePath, destinationDir) {
try {
const fileName = path.basename(filePath, '.br');
const destinationPath = path.join(destinationDir, fileName);

if (!fs.existsSync(destinationDir)) {
fs.mkdirSync(destinationDir, { recursive: true });
}
await pipe(
fs.createReadStream(filePath),
createBrotliDecompress(),
fs.createWriteStream(destinationPath)
);
console.log(`Decompression completed: ${destinationPath}`);
} catch (error) {
console.error(`Decompression failed: ${error.message}`);
}
}
25 changes: 25 additions & 0 deletions src/app/file-operations/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from 'fs';

export function copy(sourcePath, destinationPath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(sourcePath);
const writeStream = fs.createWriteStream(destinationPath);

readStream.pipe(writeStream)
.on('error', reject)
.on('close', resolve);

readStream.on('error', (err) => {
console.error(`Error reading file: ${err.message}`);
reject(err);
});
writeStream.on('error', (err) => {
console.error(`Error writing file: ${err.message}`);
reject(err);
});
})
.then(() => console.log(`File copied from ${sourcePath} to ${destinationPath}`))
.catch(err => console.error(`Error copying file: ${err.message}`));
}

// exp: cp file_name.txt /folder_name/file_name.txt
11 changes: 11 additions & 0 deletions src/app/file-operations/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import fs from 'fs';

export function create(filePath) {
fs.writeFile(filePath, '', 'utf-8', (err) => {
if (err) {
throw new Error(`Error creating file: ${error.message}`);
} else {
console.log(`File created: ${filePath}`);
}
});
}
Loading