mirror of https://github.com/lukechilds/docs.git
Moxiegirl
5 years ago
committed by
GitHub
17 changed files with 846 additions and 48 deletions
@ -0,0 +1,101 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Collaborate with groups |
|||
{:.no_toc} |
|||
|
|||
A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same among most applications. Radiks supplies interfaces for collaboration, making it easy to build private, collaborative apps. |
|||
|
|||
You use the <a href="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts" target="_blank"><code>UserGroup</code></a> class to build a collaborative group with Radiks. In this section, you learn about this class. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
## Understand the UserGroup workflow |
|||
|
|||
The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can subclass `UserGroup` with different attributes as needed. |
|||
|
|||
The general workflow for creating a collaborative group that can share and edit encrypted models is as follows: |
|||
|
|||
1. The admin of the group creates a new `UserGroup`. |
|||
This group acts as the 'hub' and controls the logic around inviting and removing users. |
|||
2. The admin invites one or more other users to a group: |
|||
* The admin specifies the username of the user they want to invite |
|||
* Radiks looks up the user's public key |
|||
* Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup` |
|||
3. When the invited user 'activates' an invitation, they create a `GroupMembership`. |
|||
They use this membership instance to reference information (such as private keys and signing keys) related to the group. |
|||
|
|||
As they participate in a group, the group's members can create and update models that are related to the group. These models **must** contain a `userGroupId` attribute used to reference the group. This allows Radiks to know which keys to use for encryption and signing. |
|||
|
|||
When needed, the group admin can remove a user from a group. To remove a user from the group, the admin creates a new private key for signing and encryption. Then, the admin updates the `GroupMembership` of all users _except_ the user they just removed. This update-and-remove action is also known as rotating the key. |
|||
|
|||
After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key. |
|||
|
|||
## Work with a UserGroup |
|||
|
|||
This section details the methods on the <a href="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts" target="_blank"><code>UserGroup</code> class you can use to create, add members to, and query a group. |
|||
|
|||
### Create a UserGroup |
|||
|
|||
To create a `UserGroup`, you must import the class into your application from `radiks`: |
|||
|
|||
```javascript |
|||
import { UserGroup } from 'radiks'; |
|||
|
|||
// ... |
|||
``` |
|||
|
|||
Calling `create` on a new `UserGroup` will create the group and activate an invitation for the group's creator. |
|||
|
|||
|
|||
```javascript |
|||
const group = new UserGroup({ name: 'My Group Name' }); |
|||
await group.create(); |
|||
``` |
|||
|
|||
A group's creator is also the group's admin. |
|||
|
|||
|
|||
### Invite users to become members |
|||
|
|||
Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the user's `username`. |
|||
|
|||
```javascript |
|||
import { UserGroup } from 'radiks'; |
|||
|
|||
const group = await UserGroup.findById(myGroupId); |
|||
const usernameToInvite = 'hankstoever.id'; |
|||
const invitation = await group.makeGroupMembership(usernameToInvite); |
|||
console.log(invitation._id); // the ID used to later activate an invitation |
|||
``` |
|||
|
|||
### Accept an invitation |
|||
|
|||
Use the `activate` method on a `GroupInvitation` instance to activate an invitation on behalf of a user: |
|||
|
|||
```javascript |
|||
import { GroupInvitation } from 'radiks'; |
|||
|
|||
const invitation = await GroupInvitation.findById(myInvitationID); |
|||
await invitation.activate(); |
|||
``` |
|||
|
|||
## View all activated UserGroups for the current user |
|||
|
|||
Call `UserGroup.myGroups` to fetch all groups that the current user is a member of: |
|||
|
|||
```javascript |
|||
import { UserGroup } from 'radiks'; |
|||
|
|||
const groups = await UserGroup.myGroups(); |
|||
``` |
|||
|
|||
## Find a UserGroup |
|||
|
|||
Use the method `UserGroup.find(id)` when fetching a specific UserGroup. This method has extra boilerplate to handle decrypting the model, because the private keys may need to be fetched from different models. |
|||
|
|||
```javascript |
|||
const group = await UserGroup.find('my-id-here'); |
|||
``` |
@ -0,0 +1,69 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Radiks the data indexer |
|||
|
|||
The Blockstack Radiks feature enables Blockstack decentralized applications (DApps) to index and store across data belonging to multiple users. Radiks works with Blockstack's Gaia Storage System. Using Radiks, you can build multi-player DApps that: |
|||
|
|||
- index, store, and query application data |
|||
- query a user's publicly saved data |
|||
- display real-time updates that reflect in progress changes |
|||
- support collaboration among sets of users |
|||
|
|||
|
|||
|
|||
## Why use Radiks? |
|||
|
|||
Many applications server data that users create to share publicly with others, Facebook, Twitter, and Instagram are examples of such applications. Decentralized applications that want to create comparable multi-user experiences must ensure that anything a user creates for public sharing is likewise still under control of the creator in the user's Gaia storage. |
|||
|
|||
For example, if Twitter were a decentralized application where many different users creating their own tweets and those tweets are stored in each user's own Gaia storage. In such a situation,a developer needs a way to track of everyone's tweets, display tweets in user timelines, and perform searches across tweets. Radiks exists to support these kinds of scenarios in that it allows applications to query across multiple user data, using complicated queries like text search, joins, and filters. |
|||
|
|||
Radiks allows applications to query data in a performant and flexible way. Each application that wishes to index and query in this way requires its own Radiks server. |
|||
|
|||
## How Radiks works with application data |
|||
|
|||
Radiks consists of a database, a pre-built server, and a client. Each application adds Radiks client library to their application. With this library a developer models their application data. The model defines an application data schema for the Radiks server. Then, you can use calls to this model to write and query data that use that schema. Whenever an application saves or updates data on behalf of a user, Radiks follows this flow: |
|||
|
|||
1. Encrypts private user data on the client-side. |
|||
2. Saves a raw JSON of this encrypted data in the user's Gaia storage. |
|||
3. Stores the encrypted data on the Radiks server. |
|||
|
|||
Radiks stores public data and sensitive, non-private data. Radiks encrypts sensitive data by default before the data leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. This means that the Radiks server is only able to return queries for unencrypted data. |
|||
|
|||
## How Radiks authorizes writes |
|||
|
|||
Radiks must ensure that user's own any data they are writing. To ensure this, Radiks creates and manages *signing keys*. These keys sign all writes that a user performs. Radiks-server validates all signatures before performing a write. This guarantees that a user is not able to overwrite another user's data. |
|||
|
|||
Radiks-server also is built to support writes in a collaborative but private situation. For example, consider a collaborative document editing application, where users can create organizations and invite users to that organization. All users in that organization have read and write privileges to the organization data. These organizations have a single shared key that is used to sign and encrypt data. |
|||
|
|||
When an organization administrator needs to remove a user from the group, they'll revoke a previous key and create a new one. Radiks is aware of these relationships, and will only support writes that are signed with the currently active key related to an organization. |
|||
|
|||
## Is Radiks decentralized |
|||
|
|||
Although Radiks applications rely on a centrally-hosted database, an application using Radiks remains fundamentally decentralized. A DApp that uses Radiks has these characteristics. |
|||
|
|||
<table class="uk-table"> |
|||
<tr> |
|||
<td>Built on decentralized authentication</td> |
|||
<td> Radiks is deeply tied to Blockstack authentication, which uses a blockchain and Gaia to give you full control over your user data. |
|||
</tr> |
|||
<tr> |
|||
<td>No data lock-in</td> |
|||
<td><p>All user data is first stored in Gaia before encrypted with the user's keys and stored in Radiks. This process means the user still controls their data for as long as they need to. If the application's Radiks server shuts down, the user can still access their data. And, without application to the user's signing keys, the application cannot decrypt the data. Users may also backup or migrate their application data from Gaia. |
|||
</p></td> |
|||
</tr> |
|||
<tr> |
|||
<td>Censorship resistance</td> |
|||
<td><p>All data is also stored in Gaia; no third-party can revoke access to this data. |
|||
</p></td> |
|||
</tr> |
|||
<tr> |
|||
<td>Maximum privacy</td> |
|||
<td><p>All data is encrypted on the client-side before being stored anywhere using Blockstack authorization. The application host cannot inspect, sell, or use user data in any way that a user doesn't explicitly authorize. |
|||
</p></td> |
|||
</tr> |
|||
</table> |
|||
|
|||
If you are not familiar with Gaia or Blockstack authentication, see |
|||
[read the Gaia documentation](({{site.baseurl}}/storage/overview.html) and [start with the overview of Blockstack auth](overview_auth.html). |
@ -0,0 +1,313 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Create and use models |
|||
{:.no_toc} |
|||
|
|||
Radiks allows you to model your client data. You can then query this data and display it for a user in multi-player applications. A social application where users want to see the comments of other users is an example of a multi-player application. This page explains how to create a model in your distributed application using Radiks. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
## Overview of Model class extension |
|||
|
|||
Blockstack provides a `Model` class you should extend to easily create, save, and fetch models. To create a model class, import the `Model` class from `radiks` into your application. |
|||
|
|||
```javascript |
|||
import { Model, User } from 'radiks'; |
|||
``` |
|||
|
|||
Then, create a class that extends this model, and provide a schema. Refer to <a href="https://github.com/blockstack/radiks/blob/master/src/model.ts" target="_blank">the <code>Model</code> class</a> in the `radiks` repo to get an overview of the class functionality. |
|||
|
|||
Your new class must define a static `className` property. This property is used when storing and querying information. If you fail to add a `className`, Radiks defaults to the actual model's class name (`foobar.ts`) and your application will behave unpredictably. |
|||
|
|||
The example class code extends `Model` to create a class named `Todo`: |
|||
|
|||
```javascript |
|||
import { Model, User } from 'radiks'; |
|||
|
|||
class Todo extends Model { |
|||
static className = 'Todo'; |
|||
static schema = { // all fields are encrypted by default |
|||
title: String, |
|||
completed: Boolean, |
|||
} |
|||
}; |
|||
|
|||
// after authentication: |
|||
const todo = new Todo({ title: 'Use Radiks in an app' }); |
|||
await todo.save(); |
|||
todo.update({ |
|||
completed: true, |
|||
}); |
|||
await todo.save(); |
|||
|
|||
const incompleteTodos = await Todo.fetchOwnList({ // fetch todos that this user created |
|||
completed: false |
|||
}); |
|||
console.log(incompleteTodos.length); // 0 |
|||
``` |
|||
|
|||
## How to create your own Model |
|||
|
|||
The following sections guide you through the steps in defining your own class. |
|||
|
|||
### Define a class schema |
|||
|
|||
Every class must have a static `schema` property which defines the attributes of a model using field/value pairs, for example: |
|||
|
|||
```javascript |
|||
class Todo extends Model { |
|||
static className = 'Todo'; |
|||
static schema = { // all fields are encrypted by default |
|||
title: String, |
|||
completed: Boolean, |
|||
} |
|||
}; |
|||
``` |
|||
|
|||
The `key` in this object is the field name and the value, for example, `String`, `Boolean`, or `Number`. In this case, the `title` is a `String` field. Alternatively, you can pass options instead of a type. |
|||
|
|||
To define options, pass an object, with a mandatory `type` field. The only supported option right now is `decrypted`. This defaults to `false`, meaning the field is encrypted before the data is stored publicly. If you specify `true`, then the field is not encrypted. |
|||
|
|||
Storing unencrypted fields is useful if you want to be able to query the field when fetching data. A good use-case for storing decrypted fields is to store a `foreignId` that references a different model, for a "belongs-to" type of relationship. |
|||
|
|||
**Never add the `decrypted` option to fields that contain sensitive user data.** Blockstack data is stored in a decentralized Gaia storage and anyone can read the user's data. That's why encrypting it is so important. If you want to filter sensitive data, then you should do it on the client-side, after decrypting it. |
|||
|
|||
### Include defaults |
|||
|
|||
You may want to include an optional `defaults` static property for some field values. For example, in the class below, the `likesDogs` field is a `Boolean`, and the default is `true`. |
|||
|
|||
```javascript |
|||
import { Model } from 'radiks'; |
|||
|
|||
class Person extends Model { |
|||
static className = 'Person'; |
|||
|
|||
static schema = { |
|||
name: String, |
|||
age: Number, |
|||
isHuman: Boolean, |
|||
likesDogs: { |
|||
type: Boolean, |
|||
decrypted: true // all users will know if this record likes dogs! |
|||
} |
|||
} |
|||
|
|||
static defaults = { |
|||
likesDogs: true |
|||
} |
|||
} |
|||
``` |
|||
|
|||
If you wanted to add a default for `isHuman`, you would simply add it to the `defaults` as well. Separate each field with a comma. |
|||
|
|||
### Extend the User model |
|||
|
|||
Radiks also supplies <a href="https://github.com/blockstack/radiks/blob/master/src/models/user.ts" target="_blank">a default <code>User</code> model</a>. You can also extend this model to add your own attributes. |
|||
|
|||
```javascript |
|||
import { User } from 'radiks'; |
|||
|
|||
// For example I want to add a public name on my user model |
|||
class MyAppUserModel extends User { |
|||
static schema = { |
|||
...User.schema, |
|||
displayName: { |
|||
type: String, |
|||
decrypted: true, |
|||
}, |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
The default `User` model defines a `username`, but you can add a `displayName` to allow the user to set unique name in your app. |
|||
|
|||
## Use a model you have defined |
|||
|
|||
In this section, you learn how to use a model you have defined. |
|||
|
|||
### About the _id attribute |
|||
|
|||
All model instances have an `_id` attribute. An `_id` is used as a primary key when storing data and is used for fetching a model. Radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. |
|||
|
|||
If, when constructing a model's instance, you don't pass an `_id`, Radiks creates an `_id` for you automatically. This automatically created id uses the [`uuid/v4`](https://github.com/kelektiv/node-uuid) format. This automatic `_id` is returned by the constructor. |
|||
|
|||
|
|||
### Construct a model instance |
|||
|
|||
To create an instance, pass some attributes to the constructor of that class: |
|||
|
|||
```javascript |
|||
const person = new Person({ |
|||
name: 'Hank', |
|||
isHuman: false, |
|||
likesDogs: false // just an example, I love dogs! |
|||
}) |
|||
``` |
|||
|
|||
### Fetch an instance |
|||
|
|||
To fetch an existing instance of an instance, you need the instance's `id` property. Then, call the `findById()` method or the `fetch()` method, which returns a promise. |
|||
|
|||
```javascript |
|||
const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44'); |
|||
``` |
|||
|
|||
|
|||
After calling these methods, Radiks automatically decrypts all encrypted fields. |
|||
|
|||
### Access attributes |
|||
|
|||
Other than `id`, all attributes are stored in an `attrs` property on the instance. |
|||
|
|||
```javascript |
|||
const { name, likesDogs } = person.attrs; |
|||
console.log(`Does ${name} like dogs?`, likesDogs); |
|||
``` |
|||
|
|||
### Update attributes |
|||
|
|||
To quickly update multiple attributes of an instance, pass those attributes to the `update` method. |
|||
|
|||
```javascript |
|||
const newAttributes = { |
|||
likesDogs: false, |
|||
age: 30 |
|||
} |
|||
person.update(newAttributes) |
|||
``` |
|||
|
|||
Important, calling `update` does **not** save the instance. |
|||
|
|||
### Save changes |
|||
|
|||
To save an instance to Gaia and MongoDB, call the `save()` method, which returns a promise. This method encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in the MongoDB. |
|||
|
|||
```javascript |
|||
await person.save(); |
|||
``` |
|||
|
|||
### Delete an instance |
|||
|
|||
To delete an instance, just call the `destroy` method on it. |
|||
|
|||
```javascript |
|||
await person.destroy(); |
|||
``` |
|||
|
|||
## Query a model |
|||
|
|||
To fetch multiple records that match a certain query, use the class's `fetchList()` function. This method creates an HTTP query to Radiks-server, which then queries the underlying database. Radiks-server uses the `query-to-mongo` package to turn an HTTP query into a MongoDB query. |
|||
|
|||
Here are some examples: |
|||
|
|||
```javascript |
|||
const dogHaters = await Person.fetchList({ likesDogs: false }); |
|||
``` |
|||
|
|||
Or, imagine a `Task` model with a `name`, a boolean for `completed`, and an `order` attribute. |
|||
|
|||
```javascript |
|||
class Task extends Model { |
|||
static className = 'Task'; |
|||
|
|||
static schema = { |
|||
name: String, |
|||
completed: { |
|||
type: Boolean, |
|||
decrypted: true, |
|||
}, |
|||
order: { |
|||
type: Number, |
|||
decrypted: true, |
|||
} |
|||
} |
|||
} |
|||
|
|||
const tasks = await Task.fetchList({ |
|||
completed: false, |
|||
sort: '-order' |
|||
}) |
|||
``` |
|||
|
|||
You can read the [`query-to-mongo`](https://github.com/pbatey/query-to-mongo) package documentation to learn how to do complex querying, sorting, limiting, and so forth. |
|||
|
|||
## Count models |
|||
|
|||
You can also get a model's `count` record directly. |
|||
|
|||
```javascript |
|||
const dogHaters = await Person.count({ likesDogs: false }); |
|||
// dogHaters is the count number |
|||
``` |
|||
|
|||
## Fetch models created by the current user |
|||
|
|||
Use the `fetchOwnList` method to find instances that were created by the current user. By using this method, you can preserve privacy, because Radiks uses a `signingKey` that only the current user knows. |
|||
|
|||
```javascript |
|||
const tasks = await Task.fetchOwnList({ |
|||
completed: false |
|||
}); |
|||
``` |
|||
|
|||
## Manage relational data |
|||
|
|||
It is common for applications to have multiple different models, where some reference another. For example, imagine a task-tracking application where a user has multiple projects, and each project has multiple tasks. Here's what those models might look like: |
|||
|
|||
```javascript |
|||
class Project extends Model { |
|||
static className = 'Project'; |
|||
static schema = { name: String } |
|||
} |
|||
|
|||
class Task extends Model { |
|||
static className = 'Task'; |
|||
static schema = { |
|||
name: String, |
|||
projectId: { |
|||
type: String, |
|||
decrypted: true, |
|||
} |
|||
completed: Boolean |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Whenever you save a task, you should save a reference to the project it's in: |
|||
|
|||
```javascript |
|||
const task = new Task({ |
|||
name: 'Improve radiks documentation', |
|||
projectId: project._id |
|||
}) |
|||
await task.save(); |
|||
``` |
|||
|
|||
Then, later you'll want to fetch all tasks for a certain project: |
|||
|
|||
```javascript |
|||
const tasks = await Task.fetchList({ |
|||
projectId: project._id, |
|||
}) |
|||
``` |
|||
|
|||
Radiks lets you define an `afterFetch` method. Use this method to automatically fetch child records when you fetch the parent instance. |
|||
|
|||
```javascript |
|||
class Project extends Model { |
|||
static className = 'Project'; |
|||
static schema = { name: String } |
|||
|
|||
async afterFetch() { |
|||
this.tasks = await Task.fetchList({ |
|||
projectId: this.id, |
|||
}) |
|||
} |
|||
} |
|||
|
|||
const project = await Project.findById('some-id-here'); |
|||
console.log(project.tasks); // will already have fetched and decrypted all related tasks |
|||
``` |
@ -0,0 +1,119 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Radiks server tips and tricks |
|||
|
|||
In this section, you'll find some tips and tricks you can use to work with a Radiks server. |
|||
|
|||
|
|||
## Access the MongoDB collection |
|||
|
|||
Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection from inside your application. |
|||
|
|||
```js |
|||
const { getDB } = require('radiks-server'); |
|||
|
|||
const mongo = await getDB(MONGODB_URL); |
|||
``` |
|||
|
|||
[See the MongoDB Collection reference](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html) for documentation about how you can interact with this collection. |
|||
|
|||
|
|||
## Run a custom Radiks-server |
|||
|
|||
If you're using an [express.js](https://expressjs.com/) server to run your application, it's probably easiest to use the Radiks-server middleware. This way, you won't have to run a separate application server and Radiks server. |
|||
|
|||
Radiks-server includes an easy-to-use middleware that you can include in your application: |
|||
|
|||
```javascript |
|||
const express = require('express'); |
|||
|
|||
const { setup } = require('radiks-server'); |
|||
|
|||
const app = express(); |
|||
|
|||
setup().then(RadiksController => { |
|||
app.use('/radiks', RadiksController); |
|||
}); |
|||
``` |
|||
|
|||
The `setup` method returns a promise, and that promise resolves to the actual middleware that your server can use. This is because it first connects to MongoDB, and then sets up the middleware with that database connection. |
|||
|
|||
The `setup` function accepts an `options` object as the first argument. If you aren't using environment variables, you can explicitly pass in a MongoDB URL here: |
|||
|
|||
```javascript |
|||
setup({ |
|||
mongoDBUrl: 'mongodb://localhost:27017/my-custom-database', |
|||
}).then(RadiksController => { |
|||
app.use('/radiks', RadiksController); |
|||
}); |
|||
``` |
|||
|
|||
Currently, only the `mongoDBUrl` option is supported. |
|||
|
|||
|
|||
## Migrate from Firebase (or anywhere else) |
|||
|
|||
Migrating data from Firebase to Radiks-server is simple and painless. You can create a script file to fetch all the Firebase data using their API. Then, you can use your `MONGOD_URI` config to use the `mongodb` npm package. |
|||
|
|||
```js |
|||
// Script for transfering users from Firebase to Radiks-server |
|||
|
|||
const { getDB } = require('radiks-server'); |
|||
const { mongoURI } = require('......'); // How you import/require your mongoURI is up to you |
|||
|
|||
const migrate = async () => { |
|||
// `mongo` is a reference to the MongoDB collection that radiks-server uses. |
|||
// You can add or edit or update data as necessary. |
|||
const mongo = await getDB(mongoURI); |
|||
|
|||
/** |
|||
* Call code to get your users from firebase |
|||
* const users = await getUsersFromFirebase(); |
|||
* OR grab the Firebase JSON file and set users to that value |
|||
* How you saved your user data will probably be different than the example below |
|||
*/ |
|||
|
|||
const users = { |
|||
'-LV1HAQToANRvhysSClr': { |
|||
blockstackId: '1N1DzKgizU4rCEaxAU21EgMaHGB5hprcBM', |
|||
username: 'kkomaz.id', |
|||
}, |
|||
}; |
|||
|
|||
const usersToInsert = Object.values(users).map(user => { |
|||
const { username } = user; |
|||
const doc = { |
|||
username, |
|||
_id: username, |
|||
radiksType: 'BlockstackUser', |
|||
}; |
|||
const op = { |
|||
updateOne: { |
|||
filter: { |
|||
_id: username, |
|||
}, |
|||
update: { |
|||
$setOnInsert: doc, |
|||
}, |
|||
upsert: true, |
|||
}, |
|||
}; |
|||
return op; |
|||
}); |
|||
|
|||
await mongo.bulkWrite(usersToInsert); |
|||
}; |
|||
|
|||
migrate() |
|||
.then(() => { |
|||
console.log('Done!'); |
|||
process.exit(); |
|||
}) |
|||
.catch(error => { |
|||
console.error(error); |
|||
process.exit(); |
|||
}); |
|||
``` |
|||
|
@ -0,0 +1,212 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Set-up Radiks for your DApp |
|||
{:.no_toc} |
|||
|
|||
Using Radiks with your application requires a Radiks server and a client application constructed to use the server. In this article, you learn how to install, setup, and run a pre-packaged Radiks server that connects to MongoDB. You also learn how to establish your DApp application as a client for that server. |
|||
|
|||
* TOC |
|||
{:toc} |
|||
|
|||
## Task 1. Set up your Radiks server |
|||
|
|||
Radiks-server is a `node.js` application that uses [MongoDB](https://www.mongodb.com/) as an underlying database. |
|||
|
|||
### Install and configure MongoDB |
|||
|
|||
In the future, Radiks-server will support various different databases, but right now, only MongoDB 3.6 or higher is supported. MongoDB 3.6 and higher contains fixes required for naming patterns in keys. |
|||
|
|||
{% include note.html content="The steps assume you want to install and run the MongoDB software locally on your workstation for testing and development. If you are deploying for a production application, you will install MongoDB on your application server or on a server connected to it. " %} |
|||
|
|||
1. <a href="https://docs.mongodb.com/manual/administration/install-community/" target="_blank">Download and install MongoDB 3.6 or higher</a> on your workstation. |
|||
|
|||
You can also install MongoDB using your favorite package manager; for example, Homebrew is recommended for macOS. If you are testing on a local workstation, you can use a `docker` image instead of installing locally. |
|||
|
|||
2. Start the MongoDB service and verify it is running. |
|||
|
|||
3. On your MongoDB instance, create a database for your application data. |
|||
|
|||
You can use the <a href="https://docs.mongodb.com/manual/mongo/" target="_blank">Mongo shell</a> to do this, or you can <a href="https://www.mongodb.com/download-center/compass" target="_blank">install the MongoDB Compass</a> software to explore and work with MongoDB data. |
|||
|
|||
4. Create a username/password combination with `root` privileges on your new database. |
|||
|
|||
|
|||
### Install and start the Radiks server |
|||
|
|||
The easiest way to run `radiks-server` is to use the pre-packaged `node.js` server. |
|||
|
|||
1. Install the `radiks-server` on a workstation or server. |
|||
|
|||
```bash |
|||
npm install -g radiks-server |
|||
``` |
|||
Or, if you prefer `yarn`: |
|||
|
|||
```bash |
|||
yarn global add radiks-server |
|||
``` |
|||
The default port for Mongodb is `27017`; your instance may be configured differently. By default, Radiks-server will use `'MongoDB://localhost:27017/radiks-server'` as the `MongoDB_URI` value. This is suitable for local testing, but in production, you'll want to change the hostname and possibly the database name. |
|||
|
|||
3. Start the `radiks-server` in the command line to confirm your installation. |
|||
|
|||
``` |
|||
$ radiks-server |
|||
(node:37750) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor. |
|||
radiks-server is ready on http://localhost:1260 |
|||
``` |
|||
|
|||
The `radiks-server` defaults to running on port `1260`. To change the default port, specify the `PORT` environment variable in your environment. |
|||
|
|||
4. By default, the server is running at `http://localhost:1260` |
|||
|
|||
4. Stop the `radiks` server process after you confirm it runs, and your installation was a success. |
|||
|
|||
## Task 2. Set up your application |
|||
|
|||
You must set up your application to use Radiks. This requires installing the `radiks` client package and then configuring your application to connect to your Radiks server. |
|||
|
|||
### Install the radiks client software |
|||
|
|||
If you are using `blockstack.js` version 18 or earlier, you must use the Radiks version 0.1.\*, otherwise if you're using `blockstack.js` version 19 or higher, use Radiks 0.2.\* . |
|||
|
|||
1. Change directory to the root of you application code. |
|||
2. Install the `radiks` client package. |
|||
|
|||
<table class="uk-table uk-table-small"> |
|||
<thead> |
|||
<tr> |
|||
<td><strong>Use npm</strong></td> |
|||
<td><strong>Use yarn</strong></td> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr> |
|||
<td><code class="highlighter-rouge">npm install --save radiks</code></td> |
|||
<td><code class="highlighter-rouge">yarn add radiks</code></td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
|
|||
### Configure the MongoDB for your application |
|||
|
|||
1. Start the mongo shell application. |
|||
|
|||
``` |
|||
$ mongo |
|||
MongoDB shell version v4.2.0 |
|||
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb |
|||
Implicit session: session { "id" : UUID("8d43cf80-490d-4cac-8bd6-40eec5c128de") } |
|||
MongoDB server version: 4.2.0 |
|||
.... |
|||
|
|||
To enable free monitoring, run the following command: db.enableFreeMonitoring() |
|||
To permanently disable this reminder, run the following command: db.disableFreeMonitoring() |
|||
> |
|||
``` |
|||
|
|||
2. Create a new database for your application. |
|||
|
|||
``` |
|||
> show dbs |
|||
admin 0.000GB |
|||
config 0.000GB |
|||
local 0.000GB |
|||
> use test1 |
|||
switched to db test1 |
|||
> show dbs |
|||
admin 0.000GB |
|||
config 0.000GB |
|||
local 0.000GB |
|||
> db.createUser({user: "admin", pwd:"foobar1",roles: ["readWrite","dbAdmin"]}); |
|||
Successfully added user: { "user" : "admin", "roles" : [ "readWrite", "dbAdmin" ] } |
|||
``` |
|||
|
|||
3. Add a user with administrative rights to the database. |
|||
|
|||
``` |
|||
> db.createUser({user: "admin", pwd:"foobar1",roles: ["readWrite","dbAdmin"]}); |
|||
Successfully added user: { "user" : "admin", "roles" : [ "readWrite", "dbAdmin" ] } |
|||
``` |
|||
|
|||
4. Create an `MONGODB_URI` environment variable on the same machine where you are running the `radiks-server`. |
|||
|
|||
Use the `mongodb://username:password@host:port/db_name` format for your variable. For example, to set this variable in a `bash` shell: |
|||
|
|||
```bash |
|||
export MONGODB_URI="mongodb://admin:foobar1@localhost:27017/test1" |
|||
``` |
|||
|
|||
|
|||
## Task 3. Add startup code and build your application |
|||
|
|||
To set up radiks.js, you only need to configure the URL that your Radiks-server instance is running on. If you're using the pre-built Radiks server, this will be `http://localhost:1260`. If you're in production or are using a custom Radiks server, you'll need to specify the exact URL where it's available. |
|||
|
|||
Radiks also is compatible with version 19 of blockstack.js, which requires you to configure a `UserSession` object to handle all user-data-related methods. You'll need to define this and pass it to your Radiks configuration so that Radiks can know how to fetch information about the current logged in user. |
|||
|
|||
### Configure your application to use your `radiks-server`. |
|||
|
|||
To configure your application as a `radiks` client, do the following: |
|||
|
|||
1. Start your application so that a `UserSession` allows the app to both write and publish data: |
|||
|
|||
```js |
|||
import { UserSession, AppConfig } from 'blockstack'; |
|||
import { configure } from 'radiks'; |
|||
|
|||
const userSession = new UserSession({ |
|||
appConfig: new AppConfig(['store_write', 'publish_data']) |
|||
}) |
|||
|
|||
configure({ |
|||
apiServer: 'http://localhost:1260', |
|||
userSession |
|||
}); |
|||
``` |
|||
|
|||
2. Add authentication to your application |
|||
|
|||
After your user logs in with Blockstack, you'll have some code to save the user's data in your applications `localStorage`. You'll want to use the same `UserSession` you configured with Radiks, which can be fetched from the `getConfig` method. |
|||
|
|||
```js |
|||
import { User, getConfig } from 'radiks'; |
|||
|
|||
const handleSignIn = () => { |
|||
const { userSession } = getConfig(); |
|||
if (userSession.isSignInPending()) { |
|||
await userSession.handlePendingSignIn(); |
|||
await User.createWithCurrentUser(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Calling `User.createWithCurrentUser` does the following: |
|||
|
|||
* Fetch user data that Blockstack.js stores in `localStorage` |
|||
* Save the user's public data (including their public key) in Radiks-server |
|||
* Find or create a signing key that is used to authorize writes on behalf of this user |
|||
* Cache the user's signing key (and any group-related signing keys) to make signatures and decryption happen quickly later on |
|||
|
|||
### Build and run your application |
|||
|
|||
After you have added Radiks to your application, build and run the application. Test the application by logging in with your Blockstack ID. Create some data using the application. If you inspect the MongoDB database, you should see the encrypted data stored in the database. |
|||
|
|||
|
|||
You can specify the `mongoDBUrl` or the `maxLimit` option when initiating the Radiks server in your application. |
|||
|
|||
```javascript |
|||
const { setup } = require('radiks-server'); |
|||
|
|||
setup({ |
|||
...myOptions, |
|||
}); |
|||
``` |
|||
|
|||
The `mongoDBUrl` option is the MongoDB URL for the Radiks server |
|||
The `maxLimit` option is the maximum `limit` field used inside the mongo queries. The default is 1000. |
|||
|
|||
|
|||
## Where to go next |
|||
|
|||
Creating models for your application's data is where radiks truly becomes helpful. To learn how to use models, see the [Create and use models](radiks-models.html) section. |
@ -1 +1 @@ |
|||
<div class="uk-alert-danger uk-card" uk-alert><b>QUESTION FOR REVIEWERS:</b> {{include.content}}</div> |
|||
<div class="uk-alert-danger uk-card" uk-alert><b>QUESTION FOR REVIEWERS:</b> {{include.content}}</div> |
|||
|
Loading…
Reference in new issue