diff --git a/bin/deardesi.js b/bin/deardesi.js new file mode 100644 index 0000000..e25b296 --- /dev/null +++ b/bin/deardesi.js @@ -0,0 +1,63 @@ +'use strict'; + +var PromiseA = require('bluebird') + , fs = PromiseA.promisifyAll(require('fs')) + , path = require('path') + , cli = require('cli') + ; + +cli.parse({ + blog: ['b', 'Where your blog is, i.e. ~/path/to/blog', 'string', './'] +//, output: ['o', 'name of output directory within ~/path/to/blog', 'string', './compiled'] +}); + +function serve(blogdir) { + var http = require('http') + //, https = require('https') + , app = require('../server').create({ blogdir: blogdir }) + , server + ; + + server = http.createServer(app).listen(65080, function () { + console.log("Listening from " + blogdir); + console.log("Listening on http://local.dear.desi:" + server.address().port); + }); + //secureServer = https.createServer(app).listen(65043); +} + +cli.main(function (args, options) { + var command = args[0] + , blogdir = options.blog + ; + + if (!blogdir) { + blogdir = path.resolve('./'); + } + + if (!fs.existsSync(path.join(options.blog, 'site.yml'))) { + console.error("Usage: deardesi [serve|init|post] -b ~/path/to/blog"); + console.error("(if ~/path/to/blog doesn't yet exist or doesn't have config.yml, site.yml, etc, " + + "try `deardesi init -b ~/path/to/blog'"); + process.exit(1); + return; + } + + if ('init' === command) { + console.error("`init' not yet implemented"); + process.exit(1); + return; + } + else if ('post' === command) { + console.error("`post' not yet implemented"); + process.exit(1); + return; + } + else if ('serve' === command) { + serve(blogdir); + return; + } + else { + console.error("Usage: deardesi [serve|init|post] -b ~/path/to/blog"); + return; + } +}); diff --git a/components/desirae/desirae.js b/components/desirae/desirae.js index a71b9cc..93bd762 100644 --- a/components/desirae/desirae.js +++ b/components/desirae/desirae.js @@ -1,13 +1,22 @@ angular.module('myApp.services', []). - factory('Desirae', ['$q', function($q) { - var Desi = window.Desi || require('./deardesi').Desi - , desi = {} - , fsapi = window.fsapi + factory('Desirae', ['$q', '$http', function ($q, $http) { + var Desi = window.Desi || require('./deardesi').Desi + , desi = {/*TODO api_base: '/api'*/} + , fsapi = window.fsapi ; + function getBlogdir () { + return $http.get('/api/fs/rootdir').then(function (resp) { + desi.blogdir = resp.data; + return resp.data; + }); + } + getBlogdir(); + return { reset: function () { desi = {}; + return getBlogdir(); } , toDesiDate: Desi.toLocaleDate , meta: function () { diff --git a/desirae b/desirae index 3235b6c..4b71063 160000 --- a/desirae +++ b/desirae @@ -1 +1 @@ -Subproject commit 3235b6c182a6cff73fc3a2482b347979ad126b01 +Subproject commit 4b7106363794014edd7575a6892fae98c5f83d70 diff --git a/desirae-http-api.js b/desirae-http-api.js new file mode 100644 index 0000000..df9fc9b --- /dev/null +++ b/desirae-http-api.js @@ -0,0 +1,134 @@ +'use strict'; + + var fsapi = require('./desirae/lib/fsapi').fsapi + , path = require('path') + ; + +module.exports.create = function (options) { + var restful = {} + ; + + // + // Required for desirae + // + restful.walk = function (req, res, next) { + if (!(/^GET$/i.test(req.method) || /^GET$/i.test(req.query._method))) { + next(); + return; + } + + var opts = {} + , dirnames = req.query.dir && [req.query.dir] || (req.query.dirs && req.query.dirs.split(/,/g)) || req.body.dirs + ; + + if (!dirnames || !dirnames.length) { + res.json({ error: "please specify GET w/ req.query.dir or POST w/ _method=GET&dirs=path/to/thing,..." }); + return; + } + + if (!dirnames.every(function (dirname) { + return 'string' === typeof dirname; + })) { + res.json({ error: "malformed request: " + JSON.stringify(dirnames) }); + return; + } + + /* + if (req.query.excludes) { + opts.excludes = req.query.excludes.split(','); + } + */ + + if (req.query.extensions) { + opts.extensions = req.query.extensions.split(/,/g); + } + + if ('true' === req.query.dotfiles) { + opts.dotfiles = true; + } + if ('false' === req.query.sha1sum) { + opts.sha1sum = false; + } + if ('true' === req.query.contents) { + opts.contents = true; + } + + // TODO opts.contents? + fsapi.walk.walkDirs(options.blogdir, dirnames, opts).then(function (stats) { + if (!req.body.dirs && !req.query.dirs) { + res.json(stats[dirnames[0]]); + } else { + res.json(stats); + } + }); + }; + + restful.getFiles = function (req, res, next) { + if (!(/^GET$/i.test(req.method) || /^GET$/i.test(req.query._method))) { + next(); + return; + } + + var filepaths = req.query.path && [req.query.path] || (req.query.paths && req.query.paths.split(/,/g)) || req.body.paths + ; + + if (!filepaths || !filepaths.length) { + res.json({ error: "please specify GET w/ req.query.path or POST _method=GET&paths=path/to/thing,..." }); + return; + } + + return fsapi.getfs(options.blogdir, filepaths).then(function (files) { + if (!req.body.paths && !req.query.paths) { + res.json(files[0]); + } else { + res.send(files); + } + }); + }; + + restful.putFiles = function (req, res, next) { + if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) { + next(); + return; + } + + var opts = {} + , files = req.body.files + ; + + if (!files || !files.length) { + res.json({ error: "please specify POST w/ req.body.files" }); + return; + } + + opts.tmpdir = options.tmpdir; + return fsapi.putfs(options.blogdir, files, opts).then(function (results) { + res.json(results); + }); + }; + + restful.copy = function (req, res, next) { + if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) { + next(); + return; + } + + var opts = {} + , files = req.body.files + ; + + if ('object' !== typeof files || !Object.keys(files).length) { + res.json({ error: "please specify POST w/ req.body.files" }); + return; + } + + return fsapi.copyfs(options.blogdir, files, opts).then(function (results) { + res.json(results); + }); + }; + // + // end Desirae API + // + + return restful; +}; diff --git a/package.json b/package.json index 220a12f..a7772a4 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, + "bin": { + "deardesi": "bin/deardesi.js" + }, "repository": { "type": "git", "url": "https://github.com/DearDesi/deardesi.git" @@ -35,6 +38,7 @@ "dependencies": { "bluebird": "^2.6.4", "body-parser": "^1.10.1", + "cli": "^0.6.5", "compression": "^1.3.0", "connect": "^3.3.4", "connect-query": "^0.2.0", diff --git a/server.js b/server.js index 46ff0ad..62506b7 100644 --- a/server.js +++ b/server.js @@ -1,173 +1,66 @@ 'use strict'; -require('require-yaml'); +module.exports.create = function (options) { + var connect = require('connect') + , query = require('connect-query') + , bodyParser = require('body-parser') + , serveStatic = require('serve-static') + , send = require('connect-send-json') -var connect = require('connect') - //, PromiseA = require('bluebird').Promise - , query = require('connect-query') - , bodyParser = require('body-parser') - , serveStatic = require('serve-static') - , send = require('connect-send-json') + , path = require('path') - , app = connect() - , fsapi = require('./desirae/lib/fsapi') - , walk = require('./desirae/lib/fsapi').walk - , getfs = require('./desirae/lib/fsapi').getfs - , putfs = require('./desirae/lib/fsapi').putfs + , app = connect() + , restful = require('./desirae-http-api').create(options) + ; - , blogdir = process.argv[2] - , port = process.argv[3] || '65080' + app + .use(send.json()) + .use(query()) + .use(bodyParser.json({ limit: 10 * 1024 * 1024 })) // 10mb + .use(require('compression')()) + ; - , path = require('path') - //, config = require(path.join('./', blogdir, 'config.yml')) - ; + // + // Keeping the API *required* by desirae super minimal + // so that it can be implemented easily in any language + // + app + .use('/api/fs/static', serveStatic(options.blogdir)) + .use('/api/fs/walk', restful.walk) + .use('/api/fs/files', restful.getFiles) + .use('/api/fs/files', restful.putFiles) + .use('/api/fs/copy', restful.copy) + ; + // end Desirae API -if (!blogdir) { - console.error("Usage: deardesi ~/path/to/blog"); - process.exit(1); - return; -} + if (options.tmpdir) { + app.use(serveStatic(options.tmpdir)); + } -app - .use(send.json()) - .use(query()) - .use(bodyParser.json({ limit: 10 * 1024 * 1024 })) // 10mb - .use(require('compression')()) - .use('/api/fs/walk', function (req, res, next) { - if (!(/^GET$/i.test(req.method) || /^GET$/i.test(req.query._method))) { - next(); - return; - } - - var opts = {} - , dirnames = req.query.dir && [req.query.dir] || (req.query.dirs && req.query.dirs.split(/,/g)) || req.body.dirs - ; - - if (!dirnames || !dirnames.length) { - res.json({ error: "please specify GET w/ req.query.dir or POST w/ _method=GET&dirs=path/to/thing,..." }); - return; - } - - if (!dirnames.every(function (dirname) { - return 'string' === typeof dirname; - })) { - res.json({ error: "malformed request: " + JSON.stringify(dirnames) }); - return; - } - - /* - if (req.query.excludes) { - opts.excludes = req.query.excludes.split(','); - } - */ - - if (req.query.extensions) { - opts.extensions = req.query.extensions.split(/,/g); - } - - if ('true' === req.query.dotfiles) { - opts.dotfiles = true; - } - if ('false' === req.query.sha1sum) { - opts.sha1sum = false; - } - if ('true' === req.query.contents) { - opts.contents = true; - } - - // TODO opts.contents? - walk.walkDirs(blogdir, dirnames, opts).then(function (stats) { - if (!req.body.dirs && !req.query.dirs) { - res.json(stats[dirnames[0]]); - } else { - res.json(stats); - } - }); - }) - .use('/api/fs/files', function (req, res, next) { - if (!(/^GET$/i.test(req.method) || /^GET$/i.test(req.query._method))) { - next(); - return; - } - - var filepaths = req.query.path && [req.query.path] || (req.query.paths && req.query.paths.split(/,/g)) || req.body.paths - ; - - if (!filepaths || !filepaths.length) { - res.json({ error: "please specify GET w/ req.query.path or POST _method=GET&paths=path/to/thing,..." }); - return; - } - - return getfs(blogdir, filepaths).then(function (files) { - if (!req.body.paths && !req.query.paths) { - res.json(files[0]); - } else { - res.send(files); - } - }); - }) - .use('/api/fs/files', function (req, res, next) { - if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) { - next(); - return; - } - - var opts = {} - , files = req.body.files - ; - - if (!files || !files.length) { - res.json({ error: "please specify POST w/ req.body.files" }); - return; - } - - return putfs(blogdir, files, opts).then(function (results) { - res.json(results); - }); - }) - .use('/api/fs/copy', function (req, res, next) { - if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) { - next(); - return; - } - - var opts = {} - , files = req.body.files - ; - - if ('object' !== typeof files || !Object.keys(files).length) { - res.json({ error: "please specify POST w/ req.body.files" }); - return; - } - - return fsapi.copyfs(blogdir, files, opts).then(function (results) { - res.json(results); - }); - }) - - .use('/api/fs', function (req, res) { - var pathname = path.resolve(blogdir) + // this is used by DearDesi, but not required for desirae + app + .use('/api/fs/rootdir', function (req, res) { + var pathname = path.resolve(options.blogdir) ; res.json({ path: pathname , name: path.basename(pathname) , relativePath: path.dirname(pathname) - //, cwd: path.resolve() - //, patharg: blogdir }); return; }) - .use('/api/fs/static', serveStatic(blogdir)) + ; - .use(serveStatic('./')) - .use('/compiled_dev', serveStatic(path.join(blogdir, '/compiled_dev'))) - // TODO - //.use(serveStatic(tmpdir)) - ; + app + // the AngularJS App + .use(serveStatic(__dirname)) + // TODO change file requests to '/blog' + //.use(serveStatic(options.blogdir)) + .use('/blog', serveStatic(options.blogdir)) + .use('/compiled_dev', serveStatic(path.join(options.blogdir, '/compiled_dev'))) + //.use('/compiled', serveStatic(path.join(options.blogdir, '/compiled'))) + ; -module.exports = app; - -require('http').createServer().on('request', app).listen(port, function () { - console.log('listening from ' + blogdir + ' http://local.dear.desi:' + port); -}); + return app; +}; diff --git a/views/post/post.html b/views/post/post.html index cb11ccc..d4a4824 100644 --- a/views/post/post.html +++ b/views/post/post.html @@ -3,7 +3,7 @@
@@ -105,6 +105,22 @@ +
+ +
+ +
+
+
diff --git a/views/post/post.js b/views/post/post.js index b200168..5220045 100644 --- a/views/post/post.js +++ b/views/post/post.js @@ -88,6 +88,8 @@ angular.module('myApp.post', ['ngRoute']) if (!/\.html?$/.test(selected.path)) { selected.path = window.path.join(selected.path, 'index.html'); } + + selected.url = window.path.join(scope.site.base_url + window.path.join(scope.site.base_path, post.yml.permalink)); selected.abspath = window.path.join(scope.blogdir, selected.path); }; scope.onFrontmatterChange = function () { @@ -107,10 +109,13 @@ angular.module('myApp.post', ['ngRoute']) scope.selected.post.yml = data; post = scope.selected.post; + scope.selected.path = window.path.join((scope.selected.collection || 'posts'), window.path.basename(post.yml.permalink)); - if (!/\.html?$/.test(scope.selected.path)) { - scope.selected.path = window.path.join(scope.selected.path, 'index.html'); + if (!/\.html?$/.test(window.path.basename(post.yml.permalink))) { + scope.selected.path = window.path.join(scope.selected.path.replace(/\.w+$/, ''), 'index.html'); } + scope.selected.url = window.path.join(scope.site.base_url + window.path.join(scope.site.base_path, post.yml.permalink)); + scope.selected.abspath = window.path.join(scope.blogdir, scope.selected.path); } catch(e) { console.error(e); console.error('ignoring update that created parse error');