├── .gitignore ├── auth.gif ├── nftp.gif ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /auth.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/factoria-org/nftp/HEAD/auth.gif -------------------------------------------------------------------------------- /nftp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/factoria-org/nftp/HEAD/nftp.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nftp", 3 | "version": "0.0.5", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "nftp": "index.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "inquirer": "^8.2.0", 13 | "ipfsio": "^0.0.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFTP 2 | 3 | The simplest way to publish files and folders to IPFS, with one command. 4 | 5 | 100% FREE to upload as much files as you want, powered by [nft.storage](https://nft.storage). 6 | 7 | > **NOTE** 8 | > 9 | > [NFT.STORAGE](https://nft.storage) is an infrastructure service designed for storing NFT data, so please use it only for storing NFT data. 10 | 11 | ![nftp.gif](nftp.gif) 12 | 13 | --- 14 | 15 | # Install 16 | 17 | ``` 18 | npm install -g nftp 19 | ``` 20 | 21 | # Usage 22 | 23 | ``` 24 | nftp 25 | ``` 26 | 27 | Currently NFTP supports two types of uploads: 28 | 29 | 1. **File upload:** specify the file path 30 | 2. **Folder upload:** specify the path to the folder. (NOTE: NFTP currently does NOT support nested folders, only supports a folder made up of files) 31 | 32 | # Authentication 33 | 34 | The first time you run the command it will ask you for the **NFT.STORAGE API KEY**. You can get it over here: https://nft.storage/ 35 | 36 | ![auth.gif](auth.gif) 37 | 38 | --- 39 | 40 | # Examples 41 | 42 | For example if you want to publish all files at current path, simply run: 43 | 44 | ``` 45 | nftp . 46 | ``` 47 | 48 | To publish a specific file named `package.json` 49 | 50 | ``` 51 | nftp package.json 52 | ``` 53 | 54 | Supports absolute paths: 55 | 56 | ``` 57 | nftp /etc 58 | ``` 59 | 60 | And supports relative paths: 61 | 62 | ``` 63 | nftp ../images 64 | ``` 65 | 66 | --- 67 | 68 | # FAQ 69 | 70 | 1. **Is it free?:** Yes it's 100% FREE, powered by [nft.storage](https://nft.storage). NFTP is simply a program that lets you upload easily to IPFS using nft.storage 71 | 2. **Where is my nft.storage API key stored?:** It is stored under `~/.nftstorage` (a hidden file named `.nftstorage` at your home path) 72 | 3. **How do I refresh my nft.storage API key?:** Simply delete the `~/.nftstorage` file and run the `nftp` command again and it will ask you for a new API key. 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | process.removeAllListeners('warning') 3 | const { I } = require('ipfsio') 4 | const inquirer = require('inquirer') 5 | const homedir = require('os').homedir(); 6 | const path = require('path') 7 | const ora = require('ora'); 8 | const fs = require('fs') 9 | const keyPath = homedir + "/.nftstorage"; 10 | const upload = async (key, target) => { 11 | const i = new I(key) 12 | let targetPath = path.resolve(process.cwd(), target) 13 | const stat = await fs.promises.lstat(targetPath) 14 | let cid 15 | let type 16 | if (stat.isDirectory()) { 17 | type = "directory" 18 | cid = await i.folder(targetPath) 19 | } else if (stat.isFile()) { 20 | type = 'file' 21 | cid = await i.file(targetPath) 22 | } else { 23 | console.log("error", stat) 24 | } 25 | return { cid, type } 26 | } 27 | const render = (r) => { 28 | if (r.cid) { 29 | console.log("########################################################################################################") 30 | console.log("#") 31 | console.log("# IPFS CID: " + r.cid) 32 | if (r.type === 'directory') { 33 | console.log("# IPFS URI: ipfs://" + r.cid + "/") 34 | } else if (r.type === 'file') { 35 | console.log("# IPFS URI: ipfs://" + r.cid) 36 | } 37 | console.log("# IPFS Gateway: https://ipfs.io/ipfs/" + r.cid) 38 | console.log("#") 39 | console.log("########################################################################################################") 40 | } 41 | } 42 | if (process.argv.length < 3) { 43 | console.log("please enter the file/folder path to upload.") 44 | process.exit(1) 45 | } 46 | const target = process.argv[2] 47 | if (fs.existsSync(keyPath)) { 48 | let key = fs.readFileSync(keyPath, "utf8") 49 | const throbber = ora('uploading').start(); 50 | console.time("upload duration") 51 | upload(key, target).then((r) => { 52 | throbber.stop(); 53 | render(r) 54 | console.log("") 55 | console.timeEnd("upload duration") 56 | }) 57 | } else { 58 | inquirer.prompt([{ 59 | type: 'input', 60 | name: "key", 61 | message: 'NFT.STORAGE API KEY', 62 | }]).then((answers) => { 63 | fs.writeFileSync(keyPath, answers.key) 64 | const throbber = ora('uploading').start(); 65 | console.time("upload duration") 66 | upload(answers.key, target).then((r) => { 67 | throbber.stop(); 68 | render(r) 69 | console.log("") 70 | console.timeEnd("upload duration") 71 | }) 72 | }) 73 | } 74 | --------------------------------------------------------------------------------