mirror of https://github.com/lukechilds/docs.git
Browse Source
Adding in base radiks files Work in progress Signed-off-by: Mary Anthony <mary@blockstack.com>feat/clarity-updates
Mary Anthony
5 years ago
17 changed files with 813 additions and 48 deletions
@ -0,0 +1,82 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Collaboration |
|||
|
|||
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 for different apps. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. |
|||
|
|||
Radiks is built in a way that provides maximum privacy and security for collaborative groups. Radiks-server and external users have no knowledge about who is in a group. |
|||
|
|||
### UserGroup Model |
|||
|
|||
The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can create multiple subclasses of `UserGroup` later on with different attributes, if you need to. |
|||
|
|||
### General Workflow |
|||
|
|||
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`, which 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: |
|||
1. The admin specifies the username of the user they want to invite |
|||
2. Radiks looks up the user's public key |
|||
3. Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup` |
|||
4. When the invited user 'activates' an invitation, they create a `GroupMembership`, which they can later use to reference information (such as private keys and signing keys) related to the group. |
|||
3. Later on, members of the group can create and update models that are related to the group. These models **must** contain a reference to the group, using the attribute `userGroupId`. This allows Radiks to know which keys to use for encryption and signing. |
|||
4. The admin of the group can later remove a user from a group. They do this by creating a new private key for signing and encryption, and updating the `GroupMembership` of all users _except_ the user they just removed. |
|||
5. 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. |
|||
|
|||
#### Creating a UserGroup |
|||
|
|||
~~~javascript |
|||
import { UserGroup } from 'radiks'; |
|||
|
|||
// ... |
|||
|
|||
const group = new UserGroup({ name: 'My Group Name' }); |
|||
await group.create(); |
|||
~~~ |
|||
|
|||
Calling `create` on a new `UserGroup` will create the group and activate an invitation for the creator of the group. |
|||
|
|||
#### Inviting a User |
|||
|
|||
Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the username of the user you want to invite. |
|||
|
|||
~~~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 |
|||
~~~ |
|||
|
|||
#### Accepting an invitation |
|||
|
|||
Use the `activate` method on a `GroupInvitation` instance to activate an invitation: |
|||
|
|||
~~~javascript |
|||
import { GroupInvitation } from 'radiks'; |
|||
|
|||
const invitation = await GroupInvitation.findById(myInvitationID); |
|||
await invitation.activate(); |
|||
~~~ |
|||
|
|||
#### Viewing 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(); |
|||
~~~ |
|||
|
|||
#### Finding 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,74 @@ |
|||
--- |
|||
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 complex application data |
|||
- query a user's publicly saved data |
|||
- display real-time updates that reflect in progress changes |
|||
- support collaboration among sets of users |
|||
|
|||
{% include question.html content="I added the word <strong>application</strong> to the complex data bullet above. Your origianl text seemed to make a distinction between publicly saved data and complex data. I assumed applciation data was the something Radiks could store and that was intended here. Was this your intent?"%} |
|||
|
|||
|
|||
|
|||
## 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 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, developer need a way to track of everyone's tweets, display tweets in user timelines, and perform searches across tweets. Radiks exists to support these kind 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. |
|||
|
|||
{% include question.html content="Can one Radiks server support multiple client applications or is a single server required for each application?"%} |
|||
|
|||
## 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 applications data schema for the Radiks server. Then, you can use calls to this model to write and query data that uses that schema. When ever an application saves or updates data on behalf of a user, Radiks follows this process: |
|||
|
|||
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. |
|||
|
|||
{% include question.html content="You didn't state this in the original but I am assuming the data is user data not just general application data. Is this correct? See the following paragraph which was taken from your server docs, in particular you had the last sentence regarding unencrypted data. Still isn't clear to me when is data in unencrypted and how it is identified from data the user wants signed." %} |
|||
|
|||
Radiks stores both public data and sensitive, non-private data. Radiks encrypts sensitive data before it leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. This means that the 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 over-write 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 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 current 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,245 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Create and use models |
|||
|
|||
To create a model class, first import the `Model` class from radiks. Then, create a class that extends this model, and provide a schema. |
|||
|
|||
**Important**: Make sure you add a static `className` property to your class. This is used when storing and querying information. If you don't add this, radiks will default to the actual model's class name. However, in production, your code will likely be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. |
|||
|
|||
#### Schema |
|||
|
|||
The first static property you'll need to define is a schema. Create a static `schema` property on your class to define it. Each `key` in this object is the name of the field. The value is whatever type you want the field to be, or you can pass some options. |
|||
|
|||
If you don't want to include any options, just pass the class for that field, like `String`, `Boolean`, or `Number`. |
|||
|
|||
To include options, pass an object, with a mandatory `type` field. The only supported option right now is `decrypted`. This defaults to `false`, but if you provide `true`, then this field will not be encrypted before storing data publicly. This is useful if you want to be able to query this field when fetching data. |
|||
|
|||
**Important**: do not add the `decrypted` option to fields that contain sensitive user data. Remember, because this is decentralized storage, anyone can read the user's data. That's why encrypting it is so important. If you want to be able to filter sensitive data, then you should do it on the client-side, after decrypting it. A good use-case for storing decrypted fields is to store a `foreignId` that references a different model, for a "belongs-to" type of relation. |
|||
|
|||
#### Defaults |
|||
|
|||
Include an optional `defaults` static property to define default values for a field. |
|||
|
|||
#### Example |
|||
|
|||
~~~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 |
|||
} |
|||
} |
|||
~~~ |
|||
|
|||
### Using models |
|||
|
|||
All model instances have an `_id` attribute. If you don't pass an `_id` to the model (when constructing it), then an `_id` will be created automatically using [`uuid/v4`](https://github.com/kelektiv/node-uuid). This `_id` is used as a primary key when storing data, and would be used for fetching this model in the future. |
|||
|
|||
In addition to automatically creating an `_id` attribute, radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. |
|||
|
|||
#### Constructing a model |
|||
|
|||
To create an instance of a model, 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! |
|||
}) |
|||
~~~ |
|||
|
|||
#### Fetching a model |
|||
|
|||
To fetch an existing model, first construct it with a required `id` property. Then, call the `fetch()` function, which returns a promise. |
|||
|
|||
~~~javascript |
|||
const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44'); |
|||
~~~ |
|||
|
|||
After calling `fetch`, radiks will automatically decrypt all encrypted fields. |
|||
|
|||
#### Accessing model attributes |
|||
|
|||
All attributes (other than `id`) are stored in an `attrs` property on the model. |
|||
|
|||
~~~javascript |
|||
const { name, likesDogs } = person.attrs; |
|||
console.log(`Does ${name} like dogs?`, likesDogs); |
|||
~~~ |
|||
|
|||
#### Updating a model |
|||
|
|||
To quickly update multiple attributes of a model, pass those attributes to the `update` function. |
|||
|
|||
~~~javascript |
|||
const newAttributes = { |
|||
likesDogs: false, |
|||
age: 30 |
|||
} |
|||
person.update(newAttributes) |
|||
~~~ |
|||
|
|||
Note that calling `update` does **not** save the model. |
|||
|
|||
#### Saving a model |
|||
|
|||
To save a model to Gaia and MongoDB, call the `save` function. First, it 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 MongoDB. `save` returns a promise. |
|||
|
|||
~~~javascript |
|||
await person.save(); |
|||
~~~ |
|||
|
|||
#### Deleting a model |
|||
|
|||
To delete a model, just call the `destroy` method on it. |
|||
|
|||
~~~javascript |
|||
await person.destroy(); |
|||
~~~ |
|||
|
|||
### Querying models |
|||
|
|||
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`](https://github.com/pbatey/query-to-mongo) package to turn an HTTP query into a MongoDB query. Read the documentation for that package to learn how to do complex querying, sorting, limiting, etc. |
|||
|
|||
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' |
|||
}) |
|||
~~~ |
|||
|
|||
### Counting models |
|||
|
|||
You can also get the count record directly. |
|||
~~~javascript |
|||
const dogHaters = await Person.count({ likesDogs: false }); |
|||
// dogHaters is the count number |
|||
~~~ |
|||
|
|||
### Fetching models created by the current user |
|||
|
|||
Use the `fetchOwnList` method to find models 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 |
|||
}); |
|||
``` |
|||
|
|||
### Managing 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'll want to 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, which you can use 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 |
|||
~~~ |
|||
|
|||
### Extending the user model |
|||
|
|||
You can extend the default user model to add your own fields. |
|||
|
|||
~~~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, |
|||
name: { |
|||
type: String, |
|||
decrypted: true, |
|||
}, |
|||
}; |
|||
} |
|||
~~~ |
@ -0,0 +1,129 @@ |
|||
--- |
|||
layout: learn |
|||
permalink: /:collection/:path.html |
|||
--- |
|||
# Other ways to use Radiks |
|||
|
|||
|
|||
### Running 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. Right now, the only option supported is `mongoDBUrl`. 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); |
|||
}); |
|||
``` |
|||
|
|||
### Accessing the MongoDB Collection |
|||
|
|||
#### Using `getDB` to manually connecting to the MongoDB collection |
|||
|
|||
Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection. [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. |
|||
|
|||
```js |
|||
const { getDB } = require('radiks-server'); |
|||
|
|||
const mongo = await getDB(MONGODB_URL); |
|||
``` |
|||
|
|||
#### Migration 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 proably 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(); |
|||
}); |
|||
``` |
|||
|
|||
### Options |
|||
|
|||
You can specify some options while initiating the Radiks server. |
|||
|
|||
```javascript |
|||
const { setup } = require('radiks-server'); |
|||
|
|||
setup({ |
|||
...myOptions, |
|||
}); |
|||
``` |
|||
|
|||
Available options: |
|||
|
|||
- `mongoDBUrl` - The MongoDB URL for the Radiks server |
|||
- `maxLimit` - The maximum `limit` field used inside the mongo queries - default to 1000 |
@ -0,0 +1,247 @@ |
|||
--- |
|||
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 assumes 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 would 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 possible 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 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" ] } |
|||
``` |
|||
|
|||
### Configure your application to use |
|||
|
|||
|
|||
|
|||
3. Configure your application to use your `radiks-server`. |
|||
|
|||
To configure your applciation as a `radiks` client, use code that looks like this when starting up your application: |
|||
|
|||
```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 |
|||
}); |
|||
``` |
|||
|
|||
For more information on configuring and writing a Radiks a client application, see [the Radiks client](https://github.com/blockstack-radiks/radiks) repository. |
|||
|
|||
|
|||
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" |
|||
``` |
|||
|
|||
mongodb://admin:foobar1@127.0.0.1:27017/test1 |
|||
|
|||
5. Build and run your application. |
|||
|
|||
|
|||
### Configuration |
|||
|
|||
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 exactly which URL it's available at. |
|||
|
|||
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. |
|||
|
|||
To configure radiks, use code that looks like this when starting up your application: |
|||
|
|||
~~~javascript |
|||
import { UserSession, AppConfig } from 'blockstack'; |
|||
import { configure } from 'radiks'; |
|||
|
|||
const userSession = new UserSession({ |
|||
appConfig: new AppConfig(['store_write', 'publish_data']) |
|||
}) |
|||
|
|||
configure({ |
|||
apiServer: 'http://my-radiks-server.com', |
|||
userSession |
|||
}); |
|||
~~~ |
|||
|
|||
### Authentication |
|||
|
|||
Most of your code will be informed by following [Blockstack's authentication documentation](https://github.com/blockstack/blockstack.js/blob/master/src/auth/README.md). |
|||
|
|||
After your user logs in with Blockstack, you'll have some code to save the user's data in localStorage. You'll want to use the same `UserSession` you configured with Radiks, which can be fetched from the `getConfig` method. |
|||
|
|||
~~~javascript |
|||
import { User, getConfig } from 'radiks'; |
|||
|
|||
const handleSignIn = () => { |
|||
const { userSession } = getConfig(); |
|||
if (userSession.isSignInPending()) { |
|||
await userSession.handlePendingSignIn(); |
|||
await User.createWithCurrentUser(); |
|||
} |
|||
} |
|||
~~~ |
|||
|
|||
Calling `User.createWithCurrentUser` will do a few things: |
|||
|
|||
1. Fetch user data that Blockstack.js stores in `localStorage` |
|||
2. Save the user's public data (including their public key) in Radiks-server |
|||
3. Find or create a signing key that is used to authorize writes on behalf of this user |
|||
4. Cache the user's signing key (and any group-related signing keys) to make signatures and decryption happen quickly later on |
|||
|
|||
## Models |
|||
|
|||
Creating models for your application's data is where radiks truly becomes helpful. We provide a `Model` class that you can extend to easily create, save, and fetch models. |
|||
|
|||
### Quick start |
|||
|
|||
```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 |
|||
``` |
@ -1 +1,5 @@ |
|||
<div class="uk-alert-danger uk-card" uk-alert><b>QUESTION FOR REVIEWERS:</b> {{include.content}}</div> |
|||
<<<<<<< HEAD |
|||
<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> |
|||
>>>>>>> 6350bd1... Adding in base radiks files |
|||
|
Loading…
Reference in new issue