Skip to content
Browse files

Reorganized code into separate servers for landing page, plunk server…

… and api
  • Loading branch information...
1 parent fa99c79 commit ae35225cb333f54abb4f2e15f8035a5ff2373aac @ggoodman committed Feb 14, 2012
View
1 .gitignore
@@ -1,3 +1,4 @@
node_modules
+npm-debug.log
/public/lib/plunker.js
/backup.json
View
14 api/plunks.coffee
@@ -28,19 +28,25 @@ module.exports = (store) ->
_.each plunks, (plunk) ->
delete plunk.token
- res.json(plunks)
+ return res.json(plunks)
create: (req, res, next) ->
plunks.create req.body, (err, plunk) ->
return next(err) if err
-
- res.json(plunk, 201)
+ return res.json(plunk, 201)
show: (req, res, next) ->
delete req.plunk.token unless req.authorized
- res.json(req.plunk)
+ return res.json(req.plunk)
+
+ update: (req, res, next) ->
+ return next({number: 404, message: "Not authorized"}) unless req.authorized
+
+ plunks.update req.plunk, req.body, (err, plunk) ->
+ return next(err) if err
+ return res.json(plunk, 200)
destroy: (req, res, next) ->
return next({number: 404, message: "Not found"}) unless req.authorized
View
107 app.coffee
@@ -1,112 +1,25 @@
+coffee = require("coffee-script")
express = require("express")
gzippo = require("gzippo")
-resource = require("express-resource")
-_ = require("underscore")._
-Gisty = require("gisty")
-
-config = _.defaults require("./config"),
- store: "memory"
- ttl: 60 * 60 * 24 * 2 # 2 days
- server: ""
app = module.exports = express.createServer()
-
+
app.configure ->
- app.set "views", "#{__dirname}/views"
- app.set "view engine", "jade"
- app.set "view options", layout: false
-
- app.use express.logger()
- app.use express.methodOverride()
- app.use express.bodyParser()
+ #app.use express.logger()
app.use express.compiler
src: "#{__dirname}/assets"
dest: "#{__dirname}/public"
enable: ["coffeescript"]
app.use gzippo.staticGzip("#{__dirname}/public")
app.use gzippo.compress()
+ app.use express.errorHandler({ dumpExceptions: true, showStack: true })
-{Store} = require("./lib/stores/#{config.store}")
-store = new Store(config)
-
-app.all "/api/*", (req, res, next) ->
- res.header("Access-Control-Allow-Origin", "*")
-
- #if req.method == "OPTIONS"
- res.header("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers")) # I hear an echo. Do you?
- res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE")
- res.header("Access-Control-Max-Age", 60 * 60 * 24 * 2) # 2 days
-
- #return res.send() if req.method == "OPTIONS"
-
- next()
-
-# Expose the public api for plunks
-app.resource "api/v1/plunks", require("./api/plunks")(store)
-
-
-app.get "/", (req, res, next) ->
- res.render("index")
-
-
-
-plunks = require("./lib/plunks")(store)
-gists = {}
-
-app.get "/gist/:id", (req, res, next) ->
- return res.redirect(gists[req.params.id]) if gists[req.params.id]
-
- gisty = new Gisty
- gisty.fetch req.params.id, (err, gist) ->
- return res.render("error/gist", {id: req.params.id, error: err}) if err
-
- files = {}
- _.map gist.files, (file, filename) ->
- files[filename] =
- content: file.content
- mime: file.type
-
- json =
- description: gist.description
- files: files
-
- if req.query.index then json.index = req.query.index
-
- plunks.create json, (err, plunk) ->
- return next(err) if err
-
- gists[gist.id] = plunk.url
-
- res.redirect(plunk.url)
-
-# Serve up a plunk
-app.get "/:id/", (req, res, next) ->
- store.fetch req.params.id, (err, plunk) ->
- return res.send(500) if err
- return res.send(404) unless plunk
-
- file = plunk.files[plunk.index]
-
- return res.send(404) unless file
- return res.send(file.content, {"Content-Type": file.mime})
-app.get "/:id", (req, res) -> res.redirect("/#{req.params.id}/", 301)
-# Serve a specific file in a plunk
-app.get "/:id/*", (req, res, next) ->
- store.fetch req.params.id, (err, plunk) ->
- return res.send(500) if err
- return res.send(404) unless plunk
-
- file = plunk.files[req.params[0]]
-
- return res.send(404) unless file
- return res.send(file.content, {"Content-Type": file.mime})
+app.use "/api", require("./servers/api")
+app.use require("./servers/landing")
+app.use require("./servers/plunks")
-app.error (err, req, res, next) ->
- body = _.extend({}, err)
- if err.message then body.message = err.message
- if err.errors then body.errors = err.errors
- if err.stack then body.stack = err.stack
-
- res.json body, err.number or 400
+if require.main == module
+ app.listen process.env.PORT || 8080
+ console.log "Listening on port %d", process.env.PORT || 8080
View
39 assets/lib/plunker.coffee
@@ -17,31 +17,31 @@ addThumbnail = (plunk) ->
.attr("title", plunk.description or "Untitled")
.addClass("description")
.appendTo($caption)
-
+
$about = $("<p>by&nbsp;</p>")
.addClass("about")
.appendTo($caption)
-
+
if plunk.author
$author = $("<a></a>")
.text(plunk.author.name)
.attr("href", plunk.author.url)
.attr("target", "_blank")
else
$author = $("<span>Anonymous</span>")
-
+
$author.addClass("creator").appendTo($about)
$about.append(" ")
-
+
$when = $("<abbr>#{new Cromag(plunk.created_at).toLocaleString()}</abbr>")
.addClass("timeago")
.addClass("created_at")
.attr("title", plunk.created_at)
.appendTo($about)
.timeago()
-
+
$about.append("<br />")
-
+
if plunk.source
$about.append("from ")
$source = $("<a>#{plunk.source.name}</a>")
@@ -53,9 +53,9 @@ addThumbnail = (plunk) ->
$a.on "click", ->
showPreview(plunk)
false
-
+
$li.prependTo("#recent")
-
+
showPreview = (plunk) ->
$modal = $("<div></div>").addClass("modal")
$header = $("<div><a class=\"close\" data-dismiss=\"modal\">×</a><h3>#{plunk.description or 'Untitled'}</h3></div>")
@@ -76,7 +76,7 @@ showPreview = (plunk) ->
.addClass("btn")
.addClass("btn-primary")
.appendTo($footer)
-
+
$modal.modal().modal("show").on "hidden", ->
$modal.remove()
@@ -87,7 +87,7 @@ loadGist = (id) ->
# Return Promise
createPlunk = (json) ->
- jQuery.ajax "//#{location.hostname}/api/v1/plunks",
+ jQuery.ajax "/api/v1/plunks",
type: "post",
dataType: "json"
contentType: "application/json"
@@ -105,14 +105,14 @@ showMessage = (title, message, additionalClass) ->
.addClass("alert-heading")
.appendTo($msg)
$body = $("<p>#{message}</p>").appendTo($msg)
-
+
$msg.insertAfter("form.gist-import")
showError = (xhr, err) ->
showMessage("Import failed", err.toString(), "alert-error")
$("form.gist-import input, form.gist-import button").prop("disabled", false)
-
+
$ ->
$("form.gist-import").on "submit", ->
$("form.gist-import input, form.gist-import button").prop("disabled", true)
@@ -128,27 +128,26 @@ $ ->
url: "https://github.com/#{gist.user.login}"
avatar_url: gist.user.avatar_url
files: {}
-
+
json.files[filename] = file.content for filename, file of gist.files
-
+
createPlunk(json).fail(showError).done (data) ->
showPreview(data)
addThumbnail(data)
showMessage("Import successful", "The gist was successfully imported into Plunker", "alert-success")
$("form.gist-import input").val("").prop("disabled", false)
$("form.gist-import button").prop("disabled", false)
-
+
false
-
-
- jQuery.ajax "//#{location.hostname}/api/v1/plunks",
+
+
+ jQuery.ajax "/api/v1/plunks",
dataType: "json"
cache: false
success: (json) ->
for plunk in json then do (plunk) ->
addThumbnail(plunk)
-
+
$("img.lazy").lazyload
threshold: 200
effect: "fadeIn"
-
View
5 config.coffee
@@ -1,4 +1 @@
-module.exports =
- server: "http://plunker.no.de"
- ttl: 60 * 60 * 24 * 2
- store: "memory"
+module.exports = {}
View
4 config.memory.coffee
@@ -1,4 +0,0 @@
-module.exports =
- server: "http://hostname.com"
- ttl: 60 * 60 * 24 * 2
- store: "memory"
View
8 config.redis.coffee
@@ -1,8 +0,0 @@
-module.exports =
- server: "http://hostname.com"
- ttl: 60 * 60 * 24 * 2
- store: "redis"
- redis:
- host: "redis.hostname.com"
- port: 9724
- pass: "secret redis password" # Optional, of course
View
155 lib/plunker/index.coffee
@@ -0,0 +1,155 @@
+mime = require("mime")
+validator = require("./validate")
+Cromag = require("cromag")
+_ = require("underscore")._
+
+# From connect/utils.js
+uid = (len = 16) ->
+ keyspace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ key = ""
+
+ while len-- > 0
+ key += keyspace.charAt(Math.floor(Math.random() * keyspace.length))
+
+ key
+
+class Creater
+ constructor: (@store, @config) ->
+
+ validate: (json, next) ->
+ {@valid, @errors} = validator.validate(json, require("./schema/create"))
+
+ unless @valid then return next
+ message: "Validation failed"
+ errors: @errors
+
+ next null, json
+
+ mapFiles: (json, next) ->
+ _.each json.files, (file, filename) ->
+ if _.isString(file) then file = { content: file }
+ file.filename = filename
+ file.mime ||= mime.lookup(file.filename)
+
+ json.files[filename] = _.clone(file)
+
+ json.index ||= do ->
+ filenames = _.keys(json.files)
+
+ if "index.html" in filenames then "index.html"
+ else
+ html = _.filter filenames, (filename) -> /.html?$/.test(filename)
+
+ if html.length then html[0]
+ else filenames[0]
+
+ unless json.files[json.index] then return next
+ message: "Validation failed"
+ errors: [
+ message: "No file defined for index"
+ property: "index"
+ ]
+
+ next null, json
+
+ addFields: (json, next) ->
+ self = @
+ @store.reserveId (err, id) ->
+ if err then return next(err)
+
+ now = new Cromag()
+
+ _.extend json,
+ id: id
+ token: uid()
+ created_at: now.toISOString()
+ expires: now.addSeconds(self.config.ttl)
+ url: "#{self.config.url}/#{id}/"
+
+ file.url = "#{json.url}/#{filename}" for filename, file of json.files
+
+ self.store.create(json, next)
+
+ create: (json, cb) ->
+ self = @
+
+ self.validate json, (err, json) ->
+ if err then cb(err)
+ else self.mapFiles json, (err, json) ->
+ if err then cb(err)
+ else self.addFields json, cb
+
+
+class Updater
+ constructor: (@store, @config) ->
+
+ validate: (json, next) =>
+ {@valid, @errors} = validator.validate(json, require("./schema/update"))
+
+ unless @valid then return next
+ message: "Validation failed"
+ errors: @errors
+
+ next null, json
+
+ handleFileChanges: (plunk, json, next) ->
+ old_files = _.clone(plunk.files)
+ new_files = {}
+
+ errors = []
+
+ if json.files
+ for filename, new_file of json.files
+ old_file = old_files[filename]
+
+ if _.isString(new_file) then new_file =
+ content: file
+ filename: filename
+ mime: mime.lookup(filename)
+
+ # This is a modification to an existing file
+ if old_file
+ new_file = _.defaults new_file, old_file
+
+ # Change the name of an existing file
+ new_files[new_file.filename] = new_file
+
+ # Delete the old file from old_files hash
+ delete old_files[filename]
+
+
+ update: (plunk, json, cb) ->
+ @validate json, (err, json) ->
+ if err then return cb(err)
+
+ @
+ async.waterfall [@store.fetch()]
+
+class Interface
+ constructor: (config = {}) ->
+ @config = _.defaults config,
+ ttl: 60 * 60 * 24 * 2
+ url: ""
+ store: "memory"
+
+ @store = require("./stores/#{config.store}").createStore(config)
+
+ @creater = new Creater(@store, config)
+ @updater = new Updater(@store, config)
+
+ index: (cb) -> @store.list(cb)
+ create: (json, cb) -> @creater.create(json, cb)
+ read: (id, cb) -> @store.fetch(id, cb)
+ update: (id, json, cb) -> @updater.update(json, cb)
+ delete: (id, cb) -> @store.delete(id, cb)
+
+module.exports = do ->
+ createInterface: (config) -> new Interface(config)
+
+ middleware: (config) ->
+ interface = new Interface(config)
+
+ (req, res, next) ->
+ interface.config.url ||= req.headers.host
+ req.plunker = interface
+ next()
View
0 lib/schema/create.coffee → lib/plunker/schema/create.coffee
File renamed without changes.
View
30 lib/plunker/schema/update.coffee
@@ -0,0 +1,30 @@
+module.exports =
+ type: "object"
+ additionalProperties: false
+ minProperties: 1
+ properties:
+ description:
+ type: "string"
+ index:
+ type: "string"
+ files:
+ type: "object"
+ minProperties: 1
+ additionalProperties:
+ type: [
+ type: "null"
+ ,
+ type: "string"
+ ,
+ type: "object"
+ additionalProperties: false
+ properties:
+ new_filename:
+ type: "string"
+ content:
+ type: "string"
+ mime:
+ type: "string"
+ encoding:
+ type: "string"
+ ]
View
61 lib/stores/memory.coffee → lib/plunker/stores/memory.coffee
@@ -7,10 +7,10 @@ _ = require("underscore")._
uid = (len = 6) ->
keyspace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
key = ""
-
+
while len-- > 0
key += keyspace.charAt(Math.floor(Math.random() * keyspace.length))
-
+
key
deepClone = (obj) ->
@@ -25,77 +25,78 @@ deepClone = (obj) ->
clone
-class exports.Store
+class Store
constructor: ({@ttl, @server, @queueSize} = {ttl: 60 * 60 * 24 * 2, server: "", queueSize: 12})->
@plunks = {}
@timeouts = {}
@destructors = {}
@queue = []
-
+
self = @
+
+ console.log "Attempting to restore data"
+
fs.readFile "./backup.json", "utf8", (err, data) ->
if err then console.log "Failed to restore data"
else
self._add(plunk) for id, plunk of JSON.parse(data)
console.log "Restore completed"
-
+
setInterval @backup, 1000 * 30 # Every 30s
-
+
backup: =>
fs.writeFile "./backup.json", JSON.stringify(@plunks), (err) ->
if err then console.log "Backup failed"
else console.log "Backup completed"
-
-
+
+
createDestructor: (id) ->
self = @
@destructors[id] = ->
clearTimeout(self.timeouts[id])
-
+
self.queue = _.without(self.queue, id)
-
+
delete self.plunks[id]
delete self.timeouts[id]
-
+
exists: (uid) -> !!@plunks[uid]
_add: (json) ->
@plunks[json.id] = json
@timeouts[json.id] = setTimeout(@createDestructor(json.id), json.ttl * 1000)
@queue.unshift json.id
@queue = _.first(@queue, 12)
+
create: (json, cb) ->
- json.id = uid(6)
- json.token = uid(16)
- json.created_at = new cromag().toISOString()
- json.ttl = @ttl
- json.expires = new cromag(cromag.now() + json.ttl * 1000).toISOString()
- json.id = uid(6) while @exists(json.id)
- json.url = "#{@server}/#{json.id}/"
- file.url = json.url + file.filename for filename, file of json.files
-
@_add json
-
+
cb(null, json)
-
+
+ reserveId: (cb) -> cb(null, uid(6))
+
list: (options, cb) ->
if _.isFunction(options) then [cb, options] = [options, {}]
-
+
options = _.defaults options,
start: 0
end: 12
-
+
self = @
-
+
cb null, _.map self.queue, (id) -> self.plunks[id]
-
+
fetch: (id, cb) ->
if plunk = @plunks[id]
plunk = deepClone(plunk)
- plunk.ttl = Math.floor((cromag.parse(plunk.expires) - cromag.now()) / 1000)
-
+
return cb(null, plunk)
-
+
destroy: (id, cb) ->
if destructor = @destructors[id]
destructor()
- cb(null)
+ cb(null)
+
+store = null
+
+exports.createStore = (config) ->
+ store ||= new Store(config)
View
0 lib/stores/redis.coffee → lib/plunker/stores/redis.coffee
File renamed without changes.
View
0 lib/validate.js → lib/plunker/validate.js
File renamed without changes.
View
42 lib/plunks.coffee
@@ -1,42 +0,0 @@
-schema = require("./validate")
-mime = require("mime")
-_ = require("underscore")._
-
-module.exports = (store) ->
- create: (json, cb) ->
- # Validate the json against the json-schema
- {valid, errors} = schema.validate(json, require("../lib/schema/create"))
-
- # Trigger an appropriate error if validation fails
- return cb({number: 422, message: "Validation failed", errors: errors }) unless valid
-
- # Files can be provided as a hash of filename => contents or filename => file descriptor
- # This code normalizes them to the latter format
- _.each json.files, (file, filename) ->
- if _.isString(file) then file = { content: file }
- file.filename = filename
- file.mime ||= mime.lookup(file.filename)
- file.encoding ||= mime.charsets.lookup(file.mime)
-
- json.files[filename] = _.clone(file)
-
- json.index ||= do ->
- filenames = _.keys(json.files)
-
- console.log "Filenames", filenames
-
- if "index.html" in filenames then "index.html"
- else
- html = _.filter filenames, (filename) -> /.html?$/.test(filename)
-
- if html.length then html[0]
- else filenames[0]
-
- # Check to see that the index points to a legitimate file
- return cb({number: 422, message: "Validation failed", errors: [{property: "index", message: "No file defined for index"}]}) unless json.files[json.index]
-
- store.create(json, cb)
-
- read: (id, cb) -> store.fetch(id, cb)
- destroy: (id, cb) -> store.destroy(id, cb)
- list: (cb) -> store.list(cb)
View
3 package.json
@@ -1,12 +1,13 @@
{
"name": "plunker",
"version": "0.0.2",
+ "private": true,
"dependencies": {
"coffee-script": "1.2.x",
"cromag": "0.1.x",
"express": "2.5.7",
"gzippo": "0.1.x",
- "jade": "0.20.x",
+ "jade": "0.20.0",
"gisty": "0.0.x",
"markdown": "0.3.x",
"lingo": "0.0.x",
View
4 server.js
@@ -1,2 +1,4 @@
require("coffee-script");
-require("./app").listen(process.env.PORT);
+require("./app").listen(process.env.PORT || 8080);
+
+console.log("Listening on port %d", process.env.PORT || 8080)
View
12 servers/api/index.coffee
@@ -0,0 +1,12 @@
+express = require("express")
+app = module.exports = express.createServer()
+
+app.use express.logger()
+
+
+app.use "/v1", require("./v1")
+
+
+if require.main == module
+ app.listen process.env.PORT || 8080
+ console.log "API listening on port %d", process.env.PORT || 8080
View
109 servers/api/v1/index.coffee
@@ -0,0 +1,109 @@
+express = require("express")
+_ = require("underscore")._
+
+
+app = module.exports = express.createServer()
+
+config = _.defaults require("../../../config"),
+ store: "memory"
+ ttl: 60 * 60 * 24 * 2 # 2 days
+
+app.configure ->
+ app.use require("../../../lib/plunker").middleware(config)
+
+ app.use express.methodOverride()
+ app.use express.bodyParser()
+ app.use express.cookieParser()
+
+
+
+fetchPlunk = (req, res, next) ->
+ req.plunker.read req.params.id, (err, plunk) ->
+ if err then return apiError(err)
+ else
+ req.plunk = plunk
+ next()
+
+apiError = (res, err) ->
+ body = _.extend({}, err)
+ if err.message then body.message = err.message
+ if err.errors then body.errors = err.errors
+ if err.stack then body.stack = err.stack
+
+ res.json(statusCode, err.statusCode or 500)
+
+checkToken = (req, res, next) ->
+ req.token =
+ if req.query.token? then req.query.token
+ else if auth = req.header("Authorization")
+ [token] = auth.match(/^token (\S+)$/i)
+ token
+ else if req.cookies[req.params.id]? then req.cookies[req.params.id]
+ req.authorized = (req.token == req.plunk.token)
+
+ next()
+
+app.error (err, req, res, next) -> apiError(res, err)
+
+# CORS Headers
+app.all "*", (req, res, next) ->
+ res.header("Access-Control-Allow-Origin", "*")
+
+ #if req.method == "OPTIONS"
+ res.header("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers")) # I hear an echo. Do you?
+ res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE")
+ res.header("Access-Control-Max-Age", 60 * 60 * 24 * 2) # 2 days
+
+ next()
+
+# Index
+app.get "/plunks", (req, res) ->
+ req.plunker.index (err, plunks) ->
+ if err then return apiError(res, err)
+ else
+ for plunk in plunks
+ unless req.cookies[plunk.id]? and req.cookies[plunk.id] == plunk.token
+ delete plunk.token
+ res.json(plunks, 200)
+
+# Create
+app.post "/plunks", (req, res) ->
+ req.plunker.create req.body, (err, plunk) ->
+ if err then return apiError(res, err)
+ else
+ res.cookie plunk.id, plunk.token, { expires: new Date(plunk.expires), httpOnly: true, path: "/api/v1/plunks" }
+ res.json(plunk, 201) # Created
+
+# Read
+app.get "/plunks/:id", fetchPlunk, checkToken, (req, res) ->
+ req.plunker.read req.params.id, (err, plunk) ->
+ if err then return apiError(res, err)
+ else
+ if req.token isnt plunk.token then delete plunk.token
+ res.json(plunk, 200)
+
+# Update
+app.post "/plunks/:id", fetchPlunk, checkToken, (req, res) ->
+ unless req.authorized then return apiError res,
+ statusCode: 403 # Forbidden
+ message: "Unauthorized"
+ else
+ req.plunker.update req.params.id, req.body, (err, plunk) ->
+ if err then return apiError(res, err)
+ else res.json(plunk, 200)
+
+# Delete
+app.delete "/plunks/:id", fetchPlunk, checkToken, (req, res) ->
+ unless req.authorized then return apiError
+ statusCode: 403 # Forbidden
+ message: "Unauthorized"
+ else
+ req.plunker.delete req.params.id, (err) ->
+ if err then return apiError(res, err)
+ else
+ res.clearCookie req.params.id, { path: "/api/v1/plunks" }
+ res.send(204) # No content
+
+if require.main == module
+ app.listen process.env.PORT || 8080
+ console.log "API listening on port %d", process.env.PORT || 8080
View
16 servers/landing.coffee
@@ -0,0 +1,16 @@
+express = require("express")
+app = module.exports = express.createServer()
+
+app.configure ->
+ app.set "views", "#{__dirname}/../views"
+ app.set "view engine", "jade"
+ app.set "view options", layout: false
+
+
+app.get "/", (req, res, next) ->
+ res.render("index")
+
+
+if require.main == module
+ app.listen process.env.PORT || 8080
+ console.log "Landing page listening on port %d", process.env.PORT || 8080
View
48 servers/plunks.coffee
@@ -0,0 +1,48 @@
+express = require("express")
+_ = require("underscore")._
+
+app = module.exports = express.createServer()
+
+config = _.defaults require("../config"),
+ store: "memory"
+ ttl: 60 * 60 * 24 * 2 # 2 days
+
+
+app.configure ->
+ app.use require("../lib/plunker").middleware(config)
+
+ app.use express.logger()
+ app.use express.errorHandler({ dumpExceptions: true, showStack: true })
+
+
+
+app.param "plunk", (req, res, next, id) ->
+ req.plunker.read id, (err, plunk) ->
+ if err then next(err)
+ else
+ req.plunk = plunk
+ next()
+
+# Serve up a plunk
+app.get "/:plunk/", (req, res, next) ->
+ return res.send(404) unless plunk = req.plunk
+
+ file = plunk.files[plunk.index]
+
+ return res.send(404) unless file
+ return res.send(file.content, {"Content-Type": file.mime})
+app.get "/:id", (req, res) -> res.redirect("/#{req.params.id}/", 301)
+
+# Serve a specific file in a plunk
+app.get "/:plunk/*", (req, res, next) ->
+ return res.send(404) unless plunk = req.plunk
+
+ file = plunk.files[req.params[0]]
+
+ return res.send(404) unless file
+ return res.send(file.content, {"Content-Type": file.mime})
+
+
+if require.main == module
+ app.listen process.env.PORT || 8080
+ console.log "Plunks app listening on port %d", process.env.PORT || 8080

0 comments on commit ae35225

Please sign in to comment.
Something went wrong with that request. Please try again.