From 09bae6fac315423934d633822ed826980627b949 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Sun, 9 Feb 2020 18:25:47 -0700 Subject: [PATCH] Implement a deploy method --- lib/client.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/client.js b/lib/client.js index 8ff003e..5764d5d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -3,10 +3,13 @@ const fetch = require('node-fetch') const { URL } = require('url') const qs = require('qs') const os = require('os') +const path = require('path') const { createReadStream } = require('fs') const FormData = require('form-data') const { handleResponse } = require('fetch-errors') +const afw = require('async-folder-walker') const pkg = require('../package.json') +const { neocitiesLocalDiff } = require('./folder-diff') const defaultURL = 'https://neocities.org' @@ -18,10 +21,16 @@ class NeocitiesAPIClient { assert(typeof password, 'password arg must be a string') opts = Object.assign({ - url: defaultURL + baseURL: defaultURL }, opts) - return fetch() + const baseURL = opts.baseURL + delete opts.baseURL + + const url = new URL('/api/key', baseURL) + url.username = sitename + url.password = password + return fetch(url, opts) } constructor (apiKey, opts) { @@ -44,6 +53,9 @@ class NeocitiesAPIClient { } } + /** + * Generic get request to neocities + */ get (endpoint, quieries, opts) { assert(endpoint, 'must pass endpoint as first argument') opts = Object.assign({ @@ -58,6 +70,9 @@ class NeocitiesAPIClient { return fetch(url, opts) } + /** + * Generic post request to neocities + */ post (endpoint, formEntries, opts) { assert(endpoint, 'must pass endpoint as first argument') const form = new FormData() @@ -80,6 +95,9 @@ class NeocitiesAPIClient { return fetch(url, opts) } + /** + * Upload files to neocities + */ upload (files) { const formEntries = files.map(({ name, path }) => { return { @@ -91,6 +109,9 @@ class NeocitiesAPIClient { return this.post('upload', formEntries).then(handleResponse) } + /** + * delete files from your website + */ delete (filenames) { assert(filenames, 'filenames is a required first argument') assert(Array.isArray(filenames), 'filenames argument must be an array of file paths in your website') @@ -109,7 +130,8 @@ class NeocitiesAPIClient { } /** - * @param {Object} args Querystring arguments to include + * info returns info on your site, or optionally on a sitename querystrign + * @param {Object} args Querystring arguments to include (e.g. sitename) * @return {Promise} Fetch request promise */ info (queries) { @@ -117,8 +139,34 @@ class NeocitiesAPIClient { return this.get('info', queries).then(handleResponse) } - deploy (folder) { - throw new Error('NOT IMPLEMENTED') + /** + * Deploy a folder to neocities, skipping already uploaded files and optionally cleaning orphaned files. + * @param {String} folder The path of the folder to deploy. + * @param {Object} opts Options object. + * @param {Boolean} opts.cleanup Boolean to delete orphaned files nor not. Defaults to false. + * @param {Boolean} opts.statsCb Get access to stat info before uploading is complete. + * @return {Promise} Promise containing stats about the deploy + */ + async deploy (folder, opts) { + opts = { + cleanup: false, // delete remote orphaned files + statsCb: () => {}, + ...opts + } + + const [localFiles, remoteFiles] = Promise.all([ + afw.allFiles(path.join(folder), { shaper: f => f }), + this.list() + ]) + + const { filesToUpload, filesToDelete, filesSkipped } = await neocitiesLocalDiff(remoteFiles, localFiles) + opts.statsCb({ filesToUpload, filesToDelete, filesSkipped }) + const work = [this.upload(filesToUpload)] + if (opts.cleanup) work.push(this.delete(filesToDelete)) + + await work + + return { filesToUpload, filesToDelete, filesSkipped } } } module.exports = { NeocitiesAPIClient }