Skip to content

Commit

Permalink
Memory-based store to simplify things
Browse files Browse the repository at this point in the history
  • Loading branch information
ggoodman committed Feb 1, 2012
1 parent 03c38f1 commit f17598d
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 163 deletions.
50 changes: 50 additions & 0 deletions api/plunks.coffee
@@ -0,0 +1,50 @@
schema = require("json-schema")
mime = require("mime")
_ = require("underscore")._

module.exports = (store) ->
load: (req, id, cb) ->
# Fetch the plunk (async)
store.fetch id, (err, plunk) ->
return cb(err) if err

# Although not necessarily the 'appropriate' place to put this check for
# a token passed in the query string or in the Authorization header.
if req.query.token and req.query.token == plunk.token
req.authorized = true
else if auth = req.header("Authorization")
[token] = auth.match(/^token (\S+)$/i)

if token and token == plunk.token
req.authorized = true

cb(null, plunk)


create: (req, res, next) ->
json = _.clone(req.body)

# Validate the json against the json-schema
{valid, errors} = schema.validate(json, require("../lib/schema/create"))

# Trigger an appropriate error if validation fails
unless valid then return next({number: 422, message: "Validation failed", errors: errors })

# 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)

store.create json, (err, plunk) ->
if err then next(err)
else res.json(plunk)

show: (req, res, next) ->
delete req.plunk.token unless req.authorized

res.json(req.plunk)
100 changes: 41 additions & 59 deletions app.coffee
@@ -1,67 +1,49 @@
express = require("express")
resource = require("express-resource")

config = require("./config")

app = module.exports = express.createServer()

app.configure ->
app.use express.logger()
app.use express.methodOverride()
app.use express.bodyParser()


{Store} = require("./lib/stores/redis")
{Plunks, Plunk} = require("./lib/plunks")

store = new Store(config.redis)

Plunk::sync = store.sync
Plunks::sync = store.sync

plunks = new Plunks


app.post "/api/v1/plunks", (req, res) ->
plunks.create req.body,
success: (model) -> res.json model.toJSON()
error: (model, err) -> res.send err, 400

app.get "/api/v1/plunks/:id", (req, res) ->
plunk = new Plunk(id: req.param("id"))
plunk.fetch
success: (model) -> res.json model.toJSON()
error: (model, err) -> res.send err, 400




app.get "/:id/", (req, res) ->
plunk = new Plunk(id: req.param("id"))
plunk.fetch
success: (model) ->
filename = model.get("index")
file = model.files.get(filename)

if file then res.send file.get("content"), { "Content-Type": file.get("mime") }
else res.send "#{filename} not found in plunk", 404
error: (err) ->
res.send "No such plunk", 404

app.get "/:id", (req, res) ->
res.redirect(req.url + "/", 301)

app.get "/:id/:filename", (req, res) ->
plunk = new Plunk(id: req.param("id"))
plunk.fetch
success: (model) ->
filename = req.param("filename")
file = model.files.get(req.param("filename"))

if file then res.send file.get("content"), { "Content-Type": file.get("mime") }
else res.send "#{filename} not found in plunk", 404
error: (err) ->
res.send "No such plunk", 404



app.get "/", (req, res) ->
res.send "Welcome to Plunk"
app.use express.bodyParser()

{Store} = require("./lib/stores/memory")
store = new Store(config)

# Expose the public api for plunks
app.resource "api/v1/plunks", require("./api/plunks")(store)


# 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})

# Handle errors
app.error (err, req, res, next) ->
body = {}
if err.message then body.message = err.message
if err.errors then body.errors = err.errors

res.json body, err.number or 400
67 changes: 0 additions & 67 deletions lib/plunks.coffee

This file was deleted.

32 changes: 32 additions & 0 deletions lib/schema/create.coffee
@@ -0,0 +1,32 @@
module.exports =
type: "object"
properties:
description:
type: "string"
index:
type: "string"
default: "index.html"
files:
required: true
type: [
type: "array"
minItems: 1
items:
type: "object"
properties:
filename:
type: "string"
required: true
content:
type: "string"
required: true
mime:
type: "string"
encoding:
type: "string"
,
type: "object"
minProperties: 1
items:
type: "string"
]
58 changes: 58 additions & 0 deletions lib/stores/memory.coffee
@@ -0,0 +1,58 @@
cromag = require("cromag")
_ = require("underscore")._


# From connect/utils.js
uid = (len = 6) ->
keyspace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
key = ""

while len-- > 0
key += keyspace.charAt(Math.floor(Math.random() * keyspace.length))

key

deepClone = (obj) ->
if _.isArray(obj)
clone = _.map obj, (elem) -> deepClone(elem)
else if typeof obj == 'object'
clone = {}

_.each obj, (val, key) -> clone[key] = deepClone(val)
else
clone = obj

clone

class exports.Store
constructor: ({@ttl, @server} = {ttl: 60 * 60 * 24 * 2, server: ""})->
@plunks = {}
@timeouts = {}

createDestructor: (id) ->
self = @
->
delete self.plunks[id]
delete self.timeouts[id]

exists: (uid) -> !!@plunks[uid]
create: (json, cb) ->
json.id = uid(6)
json.token = uid(16)
json.ttl = @ttl
json.expires = new cromag(cromag.now() + json.ttl * 1000).toISOString()
json.url = "#{@server}/#{json.id}/"

json.id = uid(6) while @exists(json.id)

@plunks[json.id] = json
@timeouts[json.id] = setTimeout(@createDestructor(json.id), json.ttl * 1000)

cb(null, json)

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)

0 comments on commit f17598d

Please sign in to comment.