Overview

Juju is a cloud-based Content Management System and Database-as-a-Service. The Juju platform allows for fine-grained control over the design and content of web pages served from the infrastructure. In addition, the media CDN and database service provides the ability to add dynamic, database-driven content to any page in both pre and post-rendered models.


ChangeLog

An overview of the changes to the Juju platform over the last 12 months.

July 2014

  • Improved asset pipeline for Javascript compilation.

April 2014

  • Added support for Geographic types to the database.
  • Added support for repeated BLOB types.

February 2014

  • Added support for SEO-friendly media names.

December 2013

  • Added advanced query support with pre-rendered page variables.
  • Added fulltext support with pre-rendered variables.

October 2013

  • Fixed issue with MIME types on components on global CDN.

FAQ

How secure is Juju?

Juju is a highly secure system. All API and dashboard data are encrypted in transit using secure sockets (SSL, or more accurately, TLS). All data is also encrypted at rest, both in our database and in our blob storage systems. Our systems are hardended against DOS attacks, and are constantly monitored to ensure system health.

I'd like to know where Juju stores it's data and how safe it is from failure.

Our data-centers for front-end request serving and database are located in the central US and western US. We also have a pair of data-centers in Europe for customers needing that capability. In addition to our data-centers, we maintain a world-wide network of 35 edge-nodes that comprise our Content Delivery Network.

Is there support for searching for records using a full-text like syntax?

Yes, the Database API fully supports both filtering and searching. See the full-text search section in the documentation.

I have a site with a lot of pages, can I add them all into my Juju site?

Sites do not have a limit on the number of pages.




Pages & Templates

The Juju CMS supports the concept of HTML templates and pages. Templates contain common HTML headers and footers and define "blocks" that can be overwritten at the page level.

Templates

Do create a template, select the "Templates" tab and click "New Template". A template needs a unique name among the other templates on the site.

A template can be as simple as:

<html>
<head>
    <title>Title of the Page</title>
</head>
<body>
    [[ block content ]]
    [[ endblock ]]
</body>
</html>

Including Media

To utilize media in a template, call the media command with the name of the media file you wish to include.

Example:

<img src="(( media('bestfile.jpg') ))">

The appropriate CDN URL of the media file will be inserted into the template.

Including Themes

To utilize a theme in a template, call the theme command with the name of the theme you wish to include.

Example:

<link href="(( theme('Main Theme') ))" rel="stylesheet">

The appropriate CDN URL of the compiled CSS theme will be inserted into the template.

Including Components

To utilize a Javascript compontent in a template, call the js command with the name of the component you wish to include.

Example:

<script src="(( js('My Component') ))"></script>

The appropriate CDN URL of the compiled component Javascript will be inserted into the template.

Pages

A page is the central piece of the CMS system. A page associates a template, a path (or route), and blocks.

Once template exists in the system, it can be assigned to a page.

Blocks

Blocks can be either WYSIWYG or "code". This allows for direct HTML editing or more visually based editing.


Themes

A theme is a collection of files that compile to a single CSS file that is located on a Global CDN.

Creating A Theme

To create a theme, click on the "Themes" tab and click "New Theme". A theme can be SASS, LESS, or plain CSS-based. A theme can consist of any number of files.

Publishing A Theme

To publish a staging copy of a theme, save any file in the theme.

To publish a production compilation of the theme, click the "Publish Site" button at the top of the screen.

Including Media in a Theme

To utilize media in a theme, call the media command with the name of the media file you wish to include.

Example:

<img src="(( media('bestfile.jpg') ))">

The appropriate CDN URL of the media file will be inserted into the theme file.


Components

A theme is a collection of files that compile to a single Javascript file that is located on a Global CDN.

Creating A Component

To create a component, click on the "Components" tab and click "New Component". A component can consist of Javascript and HTML files.

Publishing A Component

To publish a staging copy of a component, click on the component name and click "Deploy Staging".

To publish a production compilation of the component, click the "Publish Site" button at the top of the screen or click "Deploy Production" from the component information page.


Media

All media uploaded to the Juju platform is served from a global CDN for fast access.


Database

Creating a New Database

To create a new database table, select the "Database" tab and click "Add Table".

Select a name for the table using uppercase, lowercase, and numeric characters only. A database table must have fields associated with it. Fields can be of many types including:

  • string - for single line strings of unicode characters.
  • text - for multi-line strings of unicode characters.
  • integer - for 64-bit integer.
  • float - for 64-bit floating point numbers.
  • blob - for large binary objects.
  • bool - for true/false values.
  • geo - for geographic coordinates.
  • address - addresses with geographic coordinates.

Adding a Row to the Database

To create a new row for a table first select the table, then click "Create New [Table Name]". You can then enter in the fields for the new database row and click "Add Row".

Searching the Database

To search a database, click into the search box on the selected table and type a query.

The query can be a fulltext query or inequality:

  • Fulltext: "cat AND dog"
  • GT: "age > 30"
  • Field specific: "title: cat AND age > 30"

3rd Party Integrations

Mailchimp

Mailchimp support is integrated via the "MailChimp Form" component.




API Integration

The Juju platform is built oppon a RESTful API for access and manipulation of pages, themes, components, media, and database. API access is granted through the API keys in each project settings tab.

Authorization

To authorize with the API, user either your API Key or Master Key to authenticate requests. User the standard Authorization header in the following way:

Authorization: Juju <key>

Database API

The database API provides access to the Juju DAAS.

/api/v1/db/

POST

Create a new database table with the following payload parameters:

{
    "name":"NameOfTable",
    "fields":[
        {"field_type":"field_name"}
    ]
}
  • name - the name of the new table to be created. Must conform to [A-Za-z0-9]+
  • fields - a list of fields in the table.
  • field_type_ - one of: string, text, int, blob, float, bool, address, datetime or geo
  • field_name_ - the name of the field, must conform to: [A-Za-z0-9]+

GET

List tables in the site.

  • limit - the number of items to return.
  • offset - the offset of the query.

/api/v1/db/TableName

Manipulate a table in place or get details.

POST

Create a new row in the table.

{
    "field_string":"This field.",
    "field_int":1234",
    "field_float_":3.14
}

Creating a new record and return a new auto-increment ID.

GET

Query the table for rows:

  • limit - the number of row to return.
  • offset - the cursor offset for the query.
  • query - the filter construct. Can be a fulltext query or inequality:
    • Fulltext: "cat AND dog"
    • Inequality: "age > 30"
    • Boolean: "is_active:true"
    • Field specific: "title: cat AND age > 30"
    • Geographic: distance(field, geopoint(35.2, 40.5)) < 100
      • Note: distance measures are in meters and "field" must be address or geo

PUT

Update the table details.

/api/v1/db/TableName/ID

Manipulate a table in place or get details.

GET

Get the table row.

PUT

Update the table row.

DELETE

Delete the table row.


Users

Many apps have a unified login that works across the mobile app and other systems. Accessing user accounts through the REST API lets you build this functionality on top of Juju.

In general, users have the same features as other objects, such as the flexible schema via the meta_data field. The differences are that user objects must have a username and password, the password is automatically encrypted and stored securely, and Juju enforces the uniqueness of the username field.

Signing Up

Signing up a new user differs from creating a generic database object in that the username and password fields are required. The password field is handled differently than the others; it is encrypted when stored in the Juju system and never returned to any client request.

To sign up a new user, send a POST request to the users root. You may add any additional fields. For example, to create a user with a specific phone number:

curl -X POST \
  -H "Authorization: Juju ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"username":"skywalker","password":"asdfasdfasd", "email":"jo@funsite.com"}' \
  https://${SITE_ID}.juju.io/api/v1/users

When the creation is successful, the HTTP response is a 201 Created.

The response body is a JSON object containing the id, the created_at timestamp of the newly-created object, and the session_token which can be used to authenticate subsequent requests as this user:

{
  "created_at": "2011-11-07T20:58:34.448Z",
  "username":"skywalker",
  "email":"jo@funsite.com",
  "id": 12341234123412,
  "session_token": "pnktnjyb996sj4p156gjtp4im"
}

Associating Meta-Data with Users

Do associate arbitrary meta-data with a user, create or update the user with a JSON structured called meta_data.

curl -X POST \
  -H "Authorization: Juju ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"username":"skywalker","password":"asdfasdfasd", "email":"jo@funsite.com", "meta_data":{"age":21}}' \
  https://${SITE_ID}.juju.io/api/v1/users

The resulting response will refect the meta-data:

{
  "created_at": "2011-11-07T20:58:34.448Z",
  "username":"skywalker",
  "email":"jo@funsite.com",
  "meta_data": {
      "age":21
  },
  "id": 12341234123412
}

Users can be queried by meta-data fields.

Logging In

After you allow users to sign up, you need to let them log in to their account with a username and password in the future. To do this, send a POST request to the /api/v1/login endpoint with username and password as URL-encoded parameters:

curl -X GET \
  -H "Authorization: Juju ${API_KEY}" \
  -G \
  --data-urlencode 'username=skywalker' \
  --data-urlencode 'password=asdfasdfasd' \
  https://${SITE_ID}.juju.io/api/v1/login

The response body is a JSON object containing all the user-provided fields except password. It also contains the created_at, updated_at, id, and session_token fields:

{
  "username": "skywalker",
  "created_at": "2011-11-07T20:58:34.448Z",
  "updated_at": "2011-11-07T20:58:34.448Z",
  "id": 12341234123412,
  "session_token": "pnktnjyb996sj4p156gjtp4im"
}

The session_token is valid for 30 days.

Facebook and Twitter

Juju allows you to link your users with services like Twitter and Facebook, enabling your users to sign up or log into your application using their existing identities. This is accomplished through the sign-up and update REST endpoints by providing authentication data for the service you wish to link to a user in the authData field. Once your user is associated with a service, the auth_data for the service will be stored with the user and is retrievable by logging in.

auth_data is a JSON object with keys for each linked service containing the data below. In each case, you are responsible for completing the authentication flow (e.g. OAuth 1.0a/2.0) to obtain the information the the service requires for linking.

Facebook auth_data contents:

{
  "facebook": {
    "id": "user's Facebook id number as a string",
    "access_token": "an authorized Facebook access token for the user",
    "expiration_date": "token expiration date of the format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
  }
}

Twitter auth_data contents:

{
  "twitter": {
    "id": "user's Twitter id number as a string",
    "screen_name": "user's Twitter screen name",
    "consumer_key": "your application's consumer key",
    "consumer_secret": "your application's consumer secret",
    "auth_token": "an authorized Twitter token for the user with your application",
    "auth_token_secret": "the secret associated with the auth_token"
  }
}

Signing Up and Logging In

Signing a user up with a linked service and logging them in with that service uses the same POST request, in which the auth_data for the user is specified. For example, to sign up or log in with a user's Twitter account:

curl -X POST \
  -H "Authorization: Juju ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
        "authData": {
          "twitter": {
            "id": "12345678",
            "screen_name": "DuckMan123",
            "consumer_key": "SaMpLeId3X7eLjjLgWEw",
            "consumer_secret": "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
            "auth_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
            "auth_token_secret": "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
          }
        }
      }' \
    https://${SITE_ID}.juju.io/api/v1/login

Juju then verifies that the provided auth_data is valid and checks to see if a user is already associated with this data. If so, it returns a status code of 200 OK and the details (including a session_token for the user):

Status: 200 OK With a response body like:

{
  "username": null,
  "created_at": "2012-02-28T23:49:36.353Z",
  "updated_at": "2012-02-28T23:49:36.353Z",
  "id": 1234123412,
  "session_token": "samplei3l83eerhnln0ecxgy5",
  "authData": {
    "twitter": {
      "id": "12345678",
      "screen_name": "DuckMan123",
      "consumer_key": "SaMpLeId3X7eLjjLgWEw",
      "consumer_secret": "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
      "auth_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
      "auth_token_secret": "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
    }
  }
}

Logging Out

Post to the /api/v1/logout URL:

curl -X POST \
  -H "Authorization: Juju ${API_KEY}" \
  -H "X-Juju-Session-Key: ${SESSION_KEY}" \
  -H "Content-Type: application/json" \
  https://${SITE_ID}.juju.io/api/v1/logout

Status will be 200 OK

The session key will now be deactivated.

Reset Password

Post to the /api/v1/forgot URL with either a valid username or email.

Note: the user's password will be reset to a random value and will be sent to them via email. If the user has no email defined the call will result in a 409 error.

curl -X POST \
  -H "Authorization: Juju ${API_KEY}" \
  -H "Content-Type: application/json" \
  --data-urlencode 'username=skywalker' \
  https://${SITE_ID}.juju.io/api/v1/forgot

Status will be 200 OK if successful.