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
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,23 @@ var api = new NeoCities('YOURUSERNAME', 'YOURPASSWORD')

api.upload([
{name: 'derp.js', path: './index.js'}
], function(resp) {
], function(resp) {
console.log(resp)
})
```

### Downloading files from your site

``` javascript
// site path is derp.js, saved on client as index.js

api.download([
{name: 'derp.js', path: 'index.js'}
], function(resp) {
console.log(resp)
})
```

### Deleting files from your site

``` javascript
Expand All @@ -36,6 +48,14 @@ api.delete(['derp.js'], function(resp) {
})
```

### List the files on your site

``` javascript
api.list('dirname', function(resp) {
console.log(resp)
})
```

### Get site info (hits, et cetera)

``` javascript
Expand All @@ -49,3 +69,36 @@ api.info('youpi', function(resp) {
console.log(resp)
})
```

### Use an API Key

The API key is a more secure way to upload data, since it doesn't store or send your username or password. First, Log in normally with a callback for the key option. (This then uses the key once it is acquired instead of your username and password.)

``` javascript
var api = new NeoCities('YOURUSERNAME', 'YOURPASSWORD', {key: function(key) {/* store your key here */}})
```

Then, use the key instead of the username or password the next time you log in.

``` javascript
var api = new NeoCities('YOURAPIKEY')
```

### Pushing a folder

``` javascript
// foo is the local folder, images is what it will be named on your site
// hidden.json is not uploaded, nor any file ending with .conf.json

api.push('foo/', 'images/', ['hidden.json', /\.conf\.json$/])
```

### Pulling a folder

Similar to download, but for folders

``` javascript
// same argument syntax as push

api.pull('foo/', 'images', ['site.png'])
```
236 changes: 202 additions & 34 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,78 @@ var qs = require('querystring')
var FormData = require('form-data')

function NeoCities(user, pass, opts) {
this.user = user
this.pass = pass
this.opts = opts || {}
if (typeof pass == 'object' || pass == undefined) {
this.key = user
}
this.opts = opts || (typeof pass == 'object' ? pass : {})
if (!this.opts.key && !this.key) {
this.user = user
this.pass = pass
}
else {
this.opts.key = this.opts.key || true
}
this.url = url.parse(this.opts.url || 'https://neocities.org')
this.siteurl = this.opts.siteurl ? url.parse(this.ops.siteurl) : null
this.client = this.url.protocol == 'https:' ? https : http
if (this.opts.key && !this.key) {
this.get('key', null, function (res) {
this.key = res.api_key
if (typeof this.opts.key == 'function') this.opts.key(this)
}, user, pass)
}
}

NeoCities.prototype.get = function(method, args, callback) {
var path = '/api/'+method

if(args)
path += '?'+qs.stringify(args)
NeoCities.prototype.download = function(files, callback) {
console.log(files)
var i = 0
var that = this
var returning = false

if (!this.siteurl) {
this.info(function (resp) {
that.siteurl = url.parse(that.url.protocol + '//' + (resp.info.domain || resp.info.sitename + '.' + that.url.hostname))
dwnldFile()
})
}
else dwnldFile()

function dwnldFile() {
if (i < files.length) {
if (files[i].path) {
var file = fs.createWriteStream(files[i].path)
file.on('finish', function() {file.close((dwnldFile))})
that.client.get(that.siteurl.href + files[i].name.replace(/^\/?/, '/'),
function(data) {return data.pipe(file)})
}
// else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think its good practice to include commented code, its better to refer the commented block for discussion. Any thoughts?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured out what that commented block was intended to do. It was the start of some code that allows files to be downloaded to a variable to return without saving them to the system. The use case would be if the file is needed for immediate use, but not for later. If you think that it would be good for this library to have that feature, I can finish adding it. Otherwise, it can just be deleted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any comment on this feature?

// returning = true
// that.client.request(that.siteurl.href + files[i].name.replace(/^\/?/, '/'),
// . function (data) {returnData[files[i].name] = data})
// }
i ++
}
else if (typeof callback == 'function' && !returning)
callback({status:'success'})
}
}

var request = this.client.request({
NeoCities.prototype.get = function(method, args, callback, user, pass) {
var opts = {
method: 'get',
host: this.url.hostname,
port: this.url.port,
path: path,
auth: this.user+':'+this.pass
}, function(res) {
path: '/api/' + method + (args ? '?' + qs.stringify(args) : '')
}

if (!this.key) {
opts.auth = (user || this.user) + ':' + (pass || this.pass)
}
else {
opts.headers = {Authorization: 'Bearer ' + this.key}
}

var request = this.client.request(opts, function(res) {
var body = ''

res.on('data', function (chunk) {
Expand All @@ -43,63 +95,179 @@ NeoCities.prototype.get = function(method, args, callback) {

NeoCities.prototype.info = function(sitename, callback) {
var args = null

if(typeof sitename == 'function')
callback = sitename
else if(typeof sitename == 'string')
args = {sitename: sitename}

this.get('info', args, callback)
}

NeoCities.prototype.list = function(path, callback) {
var args = null

if (typeof path == 'function')
callback = path
else if (typeof path == 'string')
args = {path: path}

this.get('list', args, callback)
}

NeoCities.prototype.post = function(method, args, callback) {
var form = new FormData()
var i

for(i=0;i<args.length;i++)

for(var i = 0; i < args.length; i ++)
form.append(args[i].name, args[i].value)

var request = this.client.request({
var opts = {
method: 'post',
host: this.url.hostname,
port: this.url.port,
path: '/api/'+method,
headers: form.getHeaders(),
auth: this.user+':'+this.pass
}, function(res) {
path: '/api/' + method,
headers: form.getHeaders()
}

if (!this.key) {
opts.auth = this.user + ':' + this.pass
}
else {
opts.headers.Authorization = 'Bearer ' + this.key
}

var request = this.client.request(opts, function(res) {
var body = ''

res.on('data', function (chunk) {
body += chunk
})

res.on('end', function() {
var resObj = JSON.parse(body)
callback(resObj)
})
})

form.pipe(request)
}

NeoCities.prototype.delete = function(filenames, callback) {
var args = []
var i

for(i=0;i<filenames.length;i++)

for(var i = 0; i < filenames.length; i ++)
args.push({name: 'filenames[]', value: filenames[i]})

this.post('delete', args, callback)
}

NeoCities.prototype.upload = function(files, callback) {
var args = []
var i

for(i=0;i<files.length;i++)

for(var i = 0; i < files.length; i ++)
args.push({name: files[i].name, value: fs.createReadStream(files[i].path)})

this.post('upload', args, callback)
}

NeoCities.prototype.push = function(localPath, webPath, excludes, callback) {
if (typeof webPath == 'function' || typeof webPath == 'object') {
callback = excludes
excludes = webPath
webPath = '/'
}
if (typeof excludes == 'function') {
callback = excludes
excludes = []
}
var activePaths = []
var that = this
excludes = excludes.map(function(dir) {return path.resolve(localPath, dir)})

list(localPath)

function list(dir) {
activePaths.push(dir)
fs.readdir(dir, parseFiles)

function parseFiles(err, dirContents) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be cleaner and easier for others to understand if it had better logical separation

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to make this better, but I don't have to worry about the readability of my code very often so I have no clue how well I did.

dirContents = dirContents.map(function(file) {return path.resolve(dir, file)})
var uploadArgs = dirContents.filter(function(file) {
if (excludes.includes(path.relative(localPath, file)) ||
excludes.some(function(exclude) {
return (exclude.constructor == RegExp && file.match(exclude)) ||
(typeof exclude == 'function' && exclude(file))
})) {
return false
}

var fileData = fs.statSync(file)

if (fileData.isDirectory()) {
list(file)
}
else {
return true
}
}).map(function(file) {
return {
path: file, name: webPath.replace(/[/\\]?$/, '/') +
path.relative(path.resolve(localPath), file)
}
})
activePaths.splice(activePaths.indexOf(dir), 1)
that.upload(uploadArgs, activePaths.length ? function() {} : callback)
}
}
}

NeoCities.prototype.pull = function(localPath, webPath, excludes, callback) {
if (typeof webPath == 'function' || typeof webPath == 'object') {
callback = excludes
excludes = webPath
webPath = '/'
}
if (typeof excludes == 'function') {
callback = excludes
excludes = []
}

var that = this

fs.exists(localPath, createIfFailed)

function createIfFailed(pathExists) {
if (!pathExists) {
fs.mkdir(localPath, {}, startDownload)
}
else {
startDownload()
}
}

function startDownload() {
that.list(filterDirs)

function filterDirs(files) {
if (files.result == 'success') {
that.download(files.files.filter(function(file) {
return !file.is_directory &&
(!path.relative(webPath, file.path).match(/^\.\.[/\\]/) || webPath == '/') &&
!excludes.includes(path.relative(webPath, file.path)) &&
!excludes.some(function(exclude) {
return (exclude.prototype == RegExp && file.path.match(exclude)) ||
(typeof exclude == 'function' && exclude(file.path)) ||
(typeof exclude == 'string' && path.relative(exclude, path.relative(webPath, file.path)).match(/^\.\.[/\\]/))
})
}).map(function(file) {
return {
path: localPath.replace(/[/\\]?$/, '/') + path.relative(webPath, file.path),
name: file.path
}
}), callback)
}
}
}
}

module.exports = NeoCities