*/
class DataSourceController extends FileBackedController
name : 'datasources'
- dataDir : null
-
mapping :
all : 'allData'
-> super ...
- toFile: @::toFileRecursive
/**
fs = require 'fs'
path = require 'path'
-{existsSync:exists} = path
+exists = fs.existsSync or path.existsSync
-_ = require 'underscore'
Seq = require 'seq'
+glob = require 'glob'
yaml = require 'js-yaml'
-{mkdirp, mkdirpAsync} = require '../mkdirp'
-{ readJSONFilesAsync,
+{ _, op,
+} = require '../../util'
+{ mkdirp, mkdirpAsync,
+} = require '../mkdirp'
+{ readFilesAsync, readJSONFilesAsync,
} = require '../files'
-Controller = require '../controller'
+Controller = require '../controller'
+FileBackedController = require '../file-controller'
/**
* @class Resource controller for graph requests.
*/
-class GraphController extends Controller
+class GraphController extends FileBackedController
PROTECTED_GRAPH_IDS : <[
unique_visitors
pageviews
]>
PROTECT_GRAPHS : true
- name : 'graphs'
- dataDir : null
+ name : 'graphs'
+ -> super ...
- ->
- super ...
- limnOpts = @app.set 'limn options'
- @dataDir = "#{limnOpts.dataDir}/graphs"
-
-
- toFile: (id) -> "#{@dataDir}/#id.json"
-
- /**
- * Auto-load :id for related requests.
- */
- autoload: (id, cb) ->
- file = @toFile id
- parser = JSON.parse
-
- yamlFile = file.replace /\.json$/i, '.yaml'
- if exists yamlFile
- file = yamlFile
- parser = yaml.load
-
- err, data <- fs.readFile file, 'utf8'
-
- if 'ENOENT' is err?.code
- return cb null, {}
- if err
- console.error "GraphController.autoload(#id, #{typeof cb}) -->\nerr"
- return cb err
- try
- cb null, parser data
- catch err
- console.error "GraphController.autoload(#id, #{typeof cb}) -->\nerr"
- cb err
+ toFileSimple: (id) ->
+ "#{@dataDir}/#id.json"
# GET /graphs.:format?
index: (req, res) ->
# POST /graphs
create: (req, res) ->
return unless data = @processBody req, res
- file = @toFile data.id
- if exists file
- return res.send { result:"error", message:"Graph '#{data.id}' already exists!" }, 409 # 409: Resource Conflict
- else
- fs.writeFile file, JSON.stringify(data), "utf8", @errorHandler(res, "Error writing graph!")
+ Seq()
+ .seq @findFile, data.id, Seq
+ .seq (file) ->
+ res.send { result:"error", message:"Graph '#{data.id}' already exists!" }, 409 # 409: Resource Conflict
+ .catch (err) ->
+ fs.writeFile @toFileSimple(data.id), JSON.stringify(data), "utf8", @errorHandler(res, "Error writing graph!")
# PUT /graphs/:graph
update: (req, res) ->
return unless data = @processBody req, res
if @PROTECT_GRAPHS and _ @PROTECTED_GRAPH_IDS .contains data.id
return res.send {result:"error", message:"Graph '#{data.id}' is read-only."}, 403 # 403: Forbidden
- fs.writeFile @toFile(data.id), JSON.stringify(data), "utf8", @errorHandler(res, "Error writing graph!")
+ Seq()
+ .seq @findFile, data.id, (err, file) ->
+ @ok if err then @toFileSimple(data.id) else file
+ .seq (file) ->
+ fs.writeFile file, JSON.stringify(data), "utf8", @errorHandler(res, "Error writing graph!")
# DELETE /graphs/:graph
destroy: (req, res) ->
id = req.param.graph
if @PROTECT_GRAPHS and _ @PROTECTED_GRAPH_IDS .contains id
return res.send {result:"error", message:"Graph '#{id}' is read-only."}, 403 # 403: Forbidden
- fs.unlink @toFile(id), @errorHandler(res, "Graph '#{id}' does not exist!", 410) # 410: Resource Gone
+ Seq()
+ .seq @findFile, data.id, Seq
+ .seq (file) -> fs.unlink file, @errorHandler(res, "Error destroying Graph '#id'!", 500)
+ .catch (err) -> res.send {result:"error", message:"Graph '#id' does not exist!"}, 410 # 410: Resource Gone
### Helpers
_ = require 'underscore'
Seq = require 'seq'
+glob = require 'glob'
yaml = require 'js-yaml'
{ mkdirp, mkdirpAsync,
@noun ?= @name.charAt(0).toUpperCase() + @name.slice(1)
-
/**
- * Look up the resource in the dataDir.
- *
* @param {String} id ID of this resource.
- * @returns {String} Path to file for this resource.
- * @see toFileRecursive
+ * @returns {String} Glob path to file for this resource.
*/
- toFileFlat: (id) ->
- return j if exists f = "#{@dataDir}/#id.json"
- return y if exists y = "#{@dataDir}/#id.yaml"
+ toFile: (id) ->
+ "#{@dataDir}/**/#id.@(yaml|json)"
/**
- * Look up the resource by recursively searching the dataDir.
+ * Finds the reified filepath for the resource `id`.
*
* @param {String} id ID of this resource.
- * @returns {String} Path to file for this resource.
- * @see toFileFlat
+ * @param {Function} cb Callback `(err, filepath)`.
*/
- toFileRecursive: (id, cb) ->
- files = glob "#{@dataDir}/**/#id.@(yaml|json)", {+nocase, +nosort, +sync}
- files[0]
+ findFile: (id, cb) ->
+ glob @toFile(id), {+nocase, +nosort}, (err, files) ->
+ return cb err if err
+ return cb 'ENOENT' unless files.length
+ cb null, files[0]
- /**
- * Function for looking up the filepath to a resource. Defaults to a recursive lookup;
- * use `toFileFlat()` to only check the top-level directory.
- */
- toFile: @::toFileRecursive
/**
* @param {Function} cb Callback to invoke with the loaded object.
*/
autoload: (id, cb) ->
- parser = JSON.parse
- file = @toFile id
-
- if _.endsWith file, "#id.yaml"
- parser = yaml.load
-
- err, data <- fs.readFile file, 'utf8'
-
- if 'ENOENT' is err?.code
- return cb null, {}
- if err
- console.error "#{this}.autoload(#id, #{typeof cb}) --> Error reading data!\n", err
- return cb err
- try
- cb null, parser data
- catch err
- console.error "#{this}.autoload(#id, #{typeof cb}) --> Error parsing data!\n", err
- cb err
+ Seq()
+ .seq readJSONFilesAsync, @toFile(id), {+yaml, -appendExt}, Seq
+ .seq (data) -> cb null, _.values(data)[0]
+ .catch (err) -> cb err
function limn(options)
app = express.createServer()
app = _.extend app, application
- app.init()
+ app.init options
app
* @constructor
*/
init : (opts={}) ->
+ app = this
@REV = REV
@BASE = BASE
opx.enabled = true if opx.enabled is false and (opx.whitelist or opx.blacklist)
if opx.enabled
opx.whitelist or= [ /.*/ ]
+ opx.whitelist = [opx.whitelist] unless _.isArray opx.whitelist
opx.blacklist or= []
+ opx.blacklist = [opx.blacklist] unless _.isArray opx.blacklist
@set 'limn options', opts
+ # create data directory
+ mkdirp opts.dataDir
+
### apply configuration
# serve static files
@configure ->
- {staticMaxAge} = @set 'limn options'
+ {varDir, staticMaxAge} = @set 'limn options'
maxAge = staticMaxAge[NODE_ENV] ? staticMaxAge.default
@use express.static varDir, {maxAge}
YAML_EXT_PAT = /\.ya?ml$/i
@get '/datasources/all', (req, res, next) ->
- opts = {varDir, dataDir} = @set 'limn options'
+ opts = {varDir, dataDir} = app.set 'limn options'
data = {}
Seq()
*/
app = exports = module.exports = express.createServer()
+
+/**
+ * Load Limn middleware
+ */
+app.use limn = app.limn = LimnMiddleware do
+ varDir : './var'
+ dataDir : './var/data'
+ proxy :
+ enabled : true
+ whitelist : /.*/
+
+
/**
* Handle webhook notification to pull from origin.
*/
app.all '/webhook/post-update', (req, res) ->
-
- # exec the pull async...
cmd = 'git pull origin master'
console.log "[/webhook/post-update] $ #cmd"
+
+ # exec the pull async
child = exec cmd, (err, stdout, stderr) ->
res.contentType '.txt'
console.log '[/webhook/post-update] ', stdout
else
res.send "$ #cmd\n\n#stdout\n\n#stderr", 200
-/**
- * Load Limn middleware
- */
-app.use limn = app.limn = LimnMiddleware()
-
mainfile = path.basename require.main?.filename
throw err if err
REV = stdout.trim! unless REV
- console.log s = "Limn Server (port=#PORT, env=#NODE_ENV, rev=#REV, base_dir=#{limn.BASE})"
+ console.log s = "Limn Server (port=#PORT, env=#NODE_ENV, rev=#REV)"
console.log '=' * s.length
app.listen PORT
+++ /dev/null
-../data
\ No newline at end of file
+++ /dev/null
-../docs
\ No newline at end of file