Merge branch 'master' of github.com:deployd/deployd

This commit is contained in:
Dallon Feldner
2012-04-03 09:07:54 -07:00
7 changed files with 57 additions and 500 deletions

154
README.md
View File

@@ -1,58 +1,32 @@
# deployd
Extensible, distributed, resource server.
modern, distributed, resource server.
## features
[documentation](http://deployd.github.com/deployd)
## Features
- Streaming, Any-Size File Storage
- Queryable JSON Collections
- Validation
- Authentication
- Proxy and Redirection
- Extensible via Middleware
- Events
## Installation
$ [sudo] npm install deployd -g
## Starting / Stopping
## Start
You can start and stop the server with the `dpd` CLI. For more commands see `dpd -h`.
You can start the server with the `dpd` command line interface. For more commands see `dpd -h`.
$ dpd listen
or the node module
var dpd = require('deployd')
.use('http://localhost:3333')
// optionally specify which storage resource to use
// currently only mongodb is supported
.storage('mongodb://localhost/my-dpd-storage')
// tell deployd to listen
.listen()
;
## Client
The deployd api is entirely available over http. A basic http client is bundled.
var client = require('deployd').client.use('http://localhost:2304')
, resources = client.use('/resources')
, types = client.use('/types');
## Node.js Module
The HTTP Client and node module api are the same.
var dpd = require('deployd').use('http://localhost:2304')
, resources = dpd.use('/resources')
, types = dpd.use('/types');
## Remote Administration
For security reasons deployd servers do not rely on human created passwords, instead deployd can be administered over http using a randomly generated auth key.
Deployd servers do not rely on human created passwords, instead deployd can be administered over http using a randomly generated auth key.
To generate a key and store it in the servers storage, use the cli.
Use the CLI to generate a unique key for remote administration.
$ dpd key
@@ -60,9 +34,7 @@ To generate a key and store it in the servers storage, use the cli.
{_id: "...", ...}
If this key is present in the 'x-dssh-key' header of a request, it will be used to authenticate.
It should never be passed as plain text and never stored in an insecure location. Use the `dpd` cli
to regenerate and remove old keys as needed.
Requests to low level APIs such as /types and /resources will require a `x-dssh-header` containing a key generated with `dpd`.
Keys can contain meta data for identifying their owner. This is useful in the case where access should
be granted and revoked on a key by key basis.
@@ -70,109 +42,7 @@ be granted and revoked on a key by key basis.
$ dpd addkey '{"user":"joe"}'
added key: {user: 'joe', _id: '...', ...}
## Types
`types.get([query], [callback])` or `/types` will return a description of the available resource types.
## Collections
### Create a collection
Give the collection a URL and some validation and documents will only be inserted if they pass validation.
function done(err, collection) { ... }
resources.post({
path: '/todos',
type: 'Collection',
settings: {
title: {
description: 'the title of the todo',
type: 'string',
required: true
},
completed: {
description: 'the state of the todo',
type: 'boolean',
default: false
}
}
}, done);
### Add a document to the collection
## Questions
client.use('/todos').post({title: 'feed the dog'}, function(err, todo) {
console.info(err || 'it worked! saved with _id', todo._id);
});
## Users
### Creating Users
Register a user by `POST`ing a valid user object to `/users`.
var user = {
email: 'foo@bar.com',
password: 'foobar'
};
client.use('/users').post(user, function(err, user) {
console.info(err || 'registered user id: ' + user._id);
});
### Login
Login a user by `POST`ing a valid credentials object to `/users/login` over HTTPS.
Once a user is logged in (has created a session) all requests as the user must be
made over HTTPS. Resources that do not require a user can still be made over HTTP.
var credentials = {email: 'foo@bar.com', password: 'foobar'};
client.use('/users/login').post(credentials, function(err, session) {
console.info(err || 'logged in user id: ' + session.user._id);
});
### Logout
Logout a user by making any request to `/users/logout`.
client.use('/users/logout').get(function(err) {
console.info(err || 'logged out current user');
});
### Deleting Users
Remove a user by sending a `DELETE` request to `/users?_id=<user._id>`.
client.use('/users').get({_id: user._id}).del(function(err) {
console.info(err || 'deleted user id: ' + user._id);
});
## Static Resources (Files)
POST, PUT, DELETE, and GET files over http. Only root authenticated or local requests (using the node module)
are allowed to POST, PUT, or DELETE. All files are otherwise public.
First create a static resource collection:
resources.post({
path: '/my-files',
type: 'Static'
}, ...);
Then (with an auth key) post files:
client
.addHeader('x-dssh-key', myKey)
.use('/my-files/file.jpg')
.post(fs.createReadStream('./file.jpg'), function(err) {
console.info(err || 'Streamed file.jpg to the server!');
})
;
The file will then be available to anyone over http:
client.pipe(fs.createWriteStream('./download.jpg')).get('/my-files/file.jpg', function(err) {
console.info(err || 'Downloaded the file!');
});
Consult the [documentation](http://deployd.github.com/deployd) or contact `ritchie at deployd com`.

View File

@@ -13,7 +13,7 @@
<a href="index.html"><img src="img/logo-text.png" alt="Deployd"></a>
</span>
<ul class="nav">
<li><a href="collection.html">Collection</a></li>
<li class="active"><a href="collection.html">Collection</a></li>
<li><a href="usercollection.html">User Collection</a></li>
<li><a href="static.html">Static</a></li>
</ul>
@@ -21,265 +21,7 @@
</div>
</div>
<div class="container"><h1>Collection Resource</h1>
<p>A Collection resource allows your app to save and load data in a simple schema.</p>
<h2>Setting up a collection</h2>
<p>After creating a Collection resource in the dashboard, you can set up the schema by dragging properties into the database and naming them. </p>
<p>The grid view below the property list allows you to edit the Collection manually.</p>
<h2>Property types</h2>
<p>You can currently use the following property types:</p>
<ul>
<li><strong>String</strong> - Arbritrary text data</li>
<li><strong>Number</strong> - Numeric value, supports floating points</li>
<li><strong>Boolean</strong> - True or false</li>
<li><strong>Date</strong> - A specific point in time</li>
</ul>
<h2>Formats</h2>
<p>You must format the request body as a JSON string and pass the header "Content-Type: application/json".</p>
<h2>Saving data</h2>
<p>To save data, send a POST request to the root of the Collection:</p>
<pre><code>POST /people
Content-Type: application/json
{
"age": 23,
"firstName": "Joe",
"lastName": "Smith"
}
</code></pre>
<p>The server will respond with the object, which will have a new "_id" property. </p>
<pre><code>200 OK
{
"_id": "4f71fc7c2ba744786f000001",
"age": 23,
"firstName": "Joe",
"lastName": "Smith"
}
</code></pre>
<p>This _id is used to find the object's URL (i.e. /people/4f71fc7c2ba744786f000001)</p>
<h2>Listing data</h2>
<p>A GET request to the root of the Collection will return an array of objects in the collection:</p>
<pre><code>GET /people
200 OK
[
{
"_id": "4f71fc7c2ba744786f000001",
"age": 23,
"firstName": "Joe",
"lastName": "Smith"
},
{
"_id": "4f71fe8b2ba744786f000002",
"age": 36,
"firstName": "John",
"lastName": "Doe"
}
]
</code></pre>
<h2>Retrieving a specific object</h2>
<p>A GET request at an object's URL will return the properties of that object:</p>
<pre><code>GET /people/4f71fc7c2ba744786f000001
200 OK
{
"_id": "4f71fc7c2ba744786f000001",
"age": 23,
"firstName": "Joe",
"lastName": "Smith"
}
</code></pre>
<h2>Updating an object</h2>
<p>A PUT request at an object's URL will update the object. You must include all properties except for "_id".</p>
<pre><code>PUT /people/4f71fc7c2ba744786f000001
Content-Type: application/json
{
"age": 24,
"firstName": "Fred",
"lastName": "Smith"
}
</code></pre>
<p>The server will respond with the entire object:</p>
<pre><code>200 OK
{
"_id": "4f71fc7c2ba744786f000001",
"age": 24,
"firstName": "Fred",
"lastName": "Smith"
}
</code></pre>
<h2>Deleting an object</h2>
<p>A DELETE request at an object's URL will permanently remove that object from the collection:</p>
<pre><code>DELETE /people/4f71fc7c2ba744786f000001
204 No Content
</code></pre>
<h2>Filtering results</h2>
<p>You can add querystring parameters to a GET request at the root to filter the results by properties specified:</p>
<pre><code>GET /people?firstName=Joe&amp;lastName=Smith
</code></pre>
<p><strong>NOTE</strong>: This currently only works for String properties.</p>
<h2>Advanced querying</h2>
<p>If you need to query additional types of properties, pass a JSON object as the "q" parameter with the properties you wish to filter:</p>
<pre><code>GET /people?q={
"age": 23
}
</code></pre>
<p>The "q" parameter supports <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries">MongoDB's query language</a> for particularly advanced queries. Note that Collections do not currently support embedded documents or arrays.</p>
<pre><code>GET /people?q={
"$orderby": { "age": 1 },
"name": {
"$regex": "^j"
"$options": "i",
}
}
</code></pre>
<h1>Collection Event Handlers</h1>
<p>You can attach micro-scripts to events to add logic and validation to your objects. Collections currently support the following events:</p>
<ul>
<li><strong>On Get</strong> - called when data is read</li>
<li><strong>On Post</strong> - called when data is created</li>
<li><strong>On Put</strong> - called when data is updated</li>
<li><strong>On Delete</strong> - called when data is destroyed</li>
</ul>
<h2>Reading and setting properties</h2>
<p>In an event micro-script, the "this" object refers to the current object, and has all of the properties of the object.</p>
<p>You can set values on the "this" object during an On Post or On Put event. These changes will be saved to the database.</p>
<pre><code>// On Post:
this.dateCreated = new Date();
// On Put:
this.totalScore = this.level1Points + this.level2Points;
</code></pre>
<h2>Accessing the current user</h2>
<p>If the request is coming from a logged in User, you can use the "me" object to access their properties.</p>
<pre><code>// On Post:
this.creator = me._id;
</code></pre>
<h2>Cancelling an event</h2>
<p>You can stop any event by calling the cancel(message, [code]) method.</p>
<pre><code>//On Delete:
if (this.protected) {
cancel('This post is protected and cannot be deleted');
}
DELETE /posts/123456
400 Bad Request
{
"message": "This post is protected and cannot be deleted"
}
</code></pre>
<p>You can pass an integer to the cancel() method as the second parameter to set the HTTP status code. For example, 401 means "Unauthorized".</p>
<pre><code>//On Put
if (this.creator !== me._id) {
cancel("You cannot view this post because it is not yours!", 401);
}
PUT /posts/13456
Content-Type: application/json
{ ... }
401 Unauthorized
{
"message": "You cannot view this post because it is not yours!"
}
</code></pre>
<h2>Validation</h2>
<p>Use the error(name, message) function to add a validation error.</p>
<pre><code>//On Post
if (this.age &lt; 18) {
error('age', 'must be older than 18')
}
POST /people
{
"firstName": "Joe",
"lastName": "Smith",
"age": 12
}
400 Bad Request
{
errors: {
"age": "must be older than 18"
}
}
</code></pre>
<h2>Hiding properties</h2>
<p>If you wish to hide certain properties from a user, use the hide(propertyName) function.</p>
<pre><code>//On Get
if (this.creator !== me._id) {
hide('lastName');
hide('age');
}
</code></pre>
<h2>Protecting properties from modification</h2>
<p>Use the protect(propertyName) function to protect specified properties during a POST or PUT.</p>
<pre><code>//On Put
protect('createdDate');
</code></pre>
<div class="container">/bin/sh: markdown: command not found
</div>
</body>

View File

@@ -13,7 +13,7 @@
<a href="index.html"><img src="img/logo-text.png" alt="Deployd"></a>
</span>
<ul class="nav">
<li><a href="collection.html">Collection</a></li>
<li class="active"><a href="collection.html">Collection</a></li>
<li><a href="usercollection.html">User Collection</a></li>
<li><a href="static.html">Static</a></li>
</ul>
@@ -21,66 +21,7 @@
</div>
</div>
<div class="container"><div id="index">
<div class="hero-unit">
<h1>Deployd</h1>
<p>A modern web server for front-end developers.</p>
</div>
</div>
<h2>Basics</h2>
<p>Deployd is a web server built on resources, in the style of REST. In the dashboard, you can build your app by creating resources and configuring them to work the way the want.</p>
<h2>Routing</h2>
<p>When Deployd receives an HTTP request, it checks the first part of the URL to see which resource should handle the request:</p>
<ul>
<li><strong>/todos</strong>/12345 - handled by the <strong>/todos</strong> resource</li>
<li><strong>/admin</strong>/users/12345 - handled by the <strong>/admin</strong> resource, if it exists (you cannot create multi-part resource names)</li>
<li><strong>/img</strong>/bg.jpg - handled by the <strong>/img</strong> resource</li>
<li><strong>/</strong>index.html - handled by the <strong>/</strong> resource</li>
</ul>
<h2>Reserved resource names</h2>
<p>Certain resource paths are used internally by Deployd. You should not create resources with these names:</p>
<ul>
<li>/keys</li>
<li>/types</li>
<li>/resources</li>
<li>/sessions</li>
<li>/property-types</li>
<li>/__dashboard</li>
</ul>
<h2>REST</h2>
<p>REST is a web service design pattern that conforms closely to HTTP itself. In Deployd, HTTP methods or verbs have meaning:</p>
<ul>
<li><strong>GET</strong> - Load a resource without modifying it (this is a browser's default method)</li>
<li><strong>POST</strong> - Create a resource, or send data to a special that doesn't fit within these methods</li>
<li><strong>PUT</strong> - Update an existing resource</li>
<li><strong>DELETE</strong> - Destroy an existing resource</li>
</ul>
<p>In Deployd, HTTP response codes are also important:</p>
<ul>
<li><strong>200</strong> OK - The request succeeded</li>
<li><strong>204</strong> No Content - The request succeeded, but there is no content to return (for example, after a deletion, or requesting an empty list)</li>
<li><strong>400</strong> Bad Request - The request did not pass validation. Change the parameters and try again.</li>
<li><strong>401</strong> Unauthorized - The request's session does not have permission to access that resource. </li>
<li><strong>404</strong> Not Found - That URL does not reference an existing resource</li>
<li><strong>500</strong> Internal Server Error - Deployd has failed to process the request due to an unexpected error.</li>
</ul>
<h2>Cross-Origin AJAX</h2>
<p>Deployd is configured so that you can easily develop a web app locally on your computer. It will send Access-Control-Allow-Origin HTTP headers if a request is coming from localhost or your filesystem, which will allow modern web browsers to use AJAX normally. It will not send these headers for any other domain.</p>
<div class="container">/bin/sh: markdown: command not found
</div>
</body>

View File

@@ -13,7 +13,7 @@
<a href="index.html"><img src="img/logo-text.png" alt="Deployd"></a>
</span>
<ul class="nav">
<li><a href="collection.html">Collection</a></li>
<li class="active"><a href="collection.html">Collection</a></li>
<li><a href="usercollection.html">User Collection</a></li>
<li><a href="static.html">Static</a></li>
</ul>

View File

@@ -13,7 +13,7 @@
<a href="index.html"><img src="img/logo-text.png" alt="Deployd"></a>
</span>
<ul class="nav">
<li><a href="collection.html">Collection</a></li>
<li class="active"><a href="collection.html">Collection</a></li>
<li><a href="usercollection.html">User Collection</a></li>
<li><a href="static.html">Static</a></li>
</ul>
@@ -21,26 +21,7 @@
</div>
</div>
<div class="container"><h1>Static Resource</h1>
<p>The Static Resource allows you host static files from your app, such as HTML, browser JavaScript, CSS, images, and videos. </p>
<h2>Accessing files</h2>
<p>Send a GET request with the filename to load the raw file. This is how browsers request pages and files by default.</p>
<pre><code>GET /files/bg.jpg
</code></pre>
<h2>Folders</h2>
<p>If you prefer to have seperate folders for Javascript, CSS, and images, create multiple Static Resources at the paths you want to store the files.</p>
<p>You can also give a Static Resource an empty path ("/"). This will assign it to the root of your app.</p>
<h2>Home page</h2>
<p>If a Static Resource receives a request without a filename, it will automatically redirect to "index.html" if available.</p>
<div class="container">/bin/sh: markdown: command not found
</div>
</body>

View File

@@ -13,7 +13,7 @@
<a href="index.html"><img src="img/logo-text.png" alt="Deployd"></a>
</span>
<ul class="nav">
<li><a href="collection.html">Collection</a></li>
<li class="active"><a href="collection.html">Collection</a></li>
<li><a href="usercollection.html">User Collection</a></li>
<li><a href="static.html">Static</a></li>
</ul>
@@ -21,20 +21,7 @@
</div>
</div>
<div class="container"><h1>User Collection Resource</h1>
<p>A User Collection resource behaves much like the standard Collection resource, but adds the ability to authenticate with a username and password.</p>
<h2>Special properties</h2>
<p>The User Collection contains two special properties:</p>
<ul>
<li><strong>email</strong> - For security, hidden by default on all users except the current user.</li>
<li><strong>password</strong> - Never readable under any circumstances. Can only be set when the user is logged in, when creating a new user, or from the Dashboard.</li>
</ul>
<h2>Authenticating a user</h2>
<div class="container">/bin/sh: markdown: command not found
</div>
</body>

View File

@@ -13,3 +13,39 @@ The User Collection contains two special properties:
Authenticating a user
---------------------
First create a user by POSTing it to the root of the collection.
For this example our collection will be called `/users`.
POST /users/login
Content-Type: application/json
{
"email": "foo@bar.com",
"password": "barfoo"
}
To login a user, send a POST request to `/<collection name>/login`:
POST /users/login
Content-Type: application/json
{
"email": "foo@bar.com",
"password": "barfoo"
}
The server will respond with the user, without the password.
200 OK
{
"_id": "4f71fc7c2ba744786f000001",
"email": "foo@bar.com"
}
To logout a user send a DELETE request to `/<collection name>/logout`:
204 No Content
The currently logged in user is available when GETing `/users/me`.