Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
node_modules

.git
.github

dist
.env*
docker-compose.yml
Dockerfile
*.md
claudia.*
94 changes: 94 additions & 0 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: build-docker-image

on:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3

# qemu and buildx are for multi-platform images and caching
- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

# set up local caching
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
verida/storage-node
ghcr.io/verida/storage-node
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha

# get the correct AW role to push image to ECR
- name: Configure AWS credentials from Testnet account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-duration-seconds: 1500
role-to-assume: arn:aws:iam::737954963756:role/github-testnet-deploy-role
aws-region: us-east-1

# login to push to DockerHub
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# login to push to GHCR
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v3
with:
platforms: linux/amd64 # ,linux/arm64,linux/arm/v7
context: .
push: ${{ github.event_name != 'pull_request' }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
tags: ${{ steps.meta.output.tags }}
labels: ${{ steps.meta.output.labels }}

# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ dist
.DS_Store
.vscode
curl_tests
dbdata

.env
.env*
.env*
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.17.1
14.20.0
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM node:14.20.0-slim as node
FROM ubuntu:focal-20220531 as base
COPY --from=node /usr/local/include/ /usr/local/include/
COPY --from=node /usr/local/lib/ /usr/local/lib/
COPY --from=node /usr/local/bin/ /usr/local/bin/

ENV NODE_ENV=production
EXPOSE 5000

RUN groupadd --gid 1000 node \
&& useradd --uid 1000 --gid node --shell /bin/bash --create-home node
RUN mkdir /storage-node && chown -R node:node /storage-node
WORKDIR /storage-node

FROM base as build
# this ensures we fix simlinks for npx and yarn
RUN corepack disable && corepack enable

RUN apt-get update \
&& apt-get -qq install -y --no-install-recommends \
git ca-certificates \
&& rm -rf /var/lib/apt/lists/*

USER node
COPY --chown=node:node package.json yarn.lock ./
RUN yarn install --prod --frozen-lockfile

FROM base as source
COPY --from=build --chown=node:node /storage-node/node_modules ./node_modules
COPY . .

### prod stage
# Note: use --init option when running the container to have better signal forwarding
FROM source as prod
CMD ["node", "./src/server.js"]
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ DO NOT include ` {chttpd_auth, default_authentication_handler}` in the authentic
Learn more here: https://stackoverflow.com/questions/32670580/prevent-authentication-popup-401-with-couchdb-pouchdb

```
[couchdb]
single_node=true

[chttpd]
authentication_handlers = {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, cookie_authentication_handler}
enable_cors = false
Expand Down Expand Up @@ -145,6 +148,18 @@ See the [Claudia Docs for information](https://claudiajs.com/news/2016/11/24/cla

Verida staff can see the [internal Verida repo]( https://github.com/verida/infrastructure/blob/develop/storage_node.md) for docs on this.

## Docker

You can spin up storage node API on your machine with Docker:
```shell
docker run --init --env-file=.env verida/storage-node:latest
```

Using the example [docker-compose.yml](./docker-compose.yml) you can run storage node together with CouchDB.
```shell
docker compose up
```

## Tests

Run tests with `yarn run tests`
Expand All @@ -154,4 +169,4 @@ Note: The tests in `server.js` require the server to be running locally. The oth
Common issues when running tests:

1. `Bad key`: The key in CouchDB configuration for `jwt_keys/hmac:_default` is not a valid Base64 encoded key
2. `HMAC error`: The key in CouchDB configuration for `jwt_keys/hmac:_default` does not match `ACCESS_JWT_SIGN_PK` in `.env`
2. `HMAC error`: The key in CouchDB configuration for `jwt_keys/hmac:_default` does not match `ACCESS_JWT_SIGN_PK` in `.env`
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
services:
couchdb:
image: couchdb
restart: always
ports:
- "5984:5984"
environment:
- COUCHDB_USER=${DB_USER}
- COUCHDB_PASSWORD=${DB_PASS}
volumes:
- ./dbdata:/opt/couchdb/data

storage-node:
build: .
ports:
- "5000:5000"
environment:
PORT: 5000
DB_HOST: couchdb
restart: on-failure
init: true
env_file:
- .env
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"homepage": "https://github.com/verida/storage-node/README.md",
"dependencies": {
"@babel/runtime": "^7.16.7",
"@verida/did-client": "^2.0.0-rc2",
"@verida/did-client": "^2.0.0-rc3",
"@verida/encryption-utils": "^2.0.0-rc1",
"aws-serverless-express": "^3.4.0",
"cors": "^2.8.5",
Expand Down Expand Up @@ -81,7 +81,6 @@
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"body-parser": "^1.19.2",
"claudia": "^5.14.1",
"mocha": "^7.0.0",
"nodemon": "^2.0.14",
Expand Down
22 changes: 11 additions & 11 deletions sample.env
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
HASH_KEY=this_is_not_the_prod_hash_key
DID_RPC_URL=""
DID_NETWORK="testnet"
DID_RPC_URL=
DID_NETWORK=testnet
DID_CACHE_DURATION=3600
DB_PROTOCOL="http"
DB_USER="admin"
DB_PASS="admin"
DB_HOST="localhost"
DB_PROTOCOL=http
DB_USER=admin
DB_PASS=admin
DB_HOST=localhost
DB_PORT=5984
DB_REJECT_UNAUTHORIZED_SSL=true
ACCESS_JWT_SIGN_PK="insert-random-access-symmetric-key"
ACCESS_JWT_SIGN_PK=insert-random-access-symmetric-key
# 5 Minutes
ACCESS_TOKEN_EXPIRY=300
REFRESH_JWT_SIGN_PK="insert-random-refresh-symmetric-key"
REFRESH_JWT_SIGN_PK=insert-random-refresh-symmetric-key
# 30 Days
REFRESH_TOKEN_EXPIRY=2592000
DB_REFRESH_TOKENS="verida_refresh_tokens"
Expand All @@ -20,7 +20,7 @@ DB_DB_INFO="verida_db_info"
GC_PERCENT=0.1

// alpha numeric only
DB_PUBLIC_USER="784c2n780c9cn0789"
DB_PUBLIC_PASS="784c2n780c9cn0789"
DB_PUBLIC_USER=784c2n780c9cn0789
DB_PUBLIC_PASS=784c2n780c9cn0789

PORT=5151
PORT=5151
14 changes: 8 additions & 6 deletions src/components/authManager.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
require('dotenv').config();
const randtoken = require('rand-token');
const jwt = require('jsonwebtoken');
const mcache = require("memory-cache")
import dotenv from 'dotenv';
import randtoken from 'rand-token';
import jwt from 'jsonwebtoken';
import mcache from 'memory-cache';

import { DIDClient } from '@verida/did-client'
import EncryptionUtils from '@verida/encryption-utils';
import Utils from "./utils";
import Db from "./db";
import Utils from './utils.js';
import Db from './db.js';

dotenv.config();

let didClient

Expand Down
8 changes: 5 additions & 3 deletions src/components/db.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require('dotenv').config();
const CouchDb = require('nano');
import dotenv from 'dotenv';
import CouchDb from 'nano';

dotenv.config();

class Db {

Expand All @@ -9,7 +11,7 @@ class Db {
this._couch = new CouchDb({
url: dsn,
requestDefaults: {
rejectUnauthorized: process.env.DB_REJECT_UNAUTHORIZED_SSL.toLowerCase() != "false"
rejectUnauthorized: process.env.DB_REJECT_UNAUTHORIZED_SSL.toLowerCase() !== "false"
}
});
}
Expand Down
10 changes: 4 additions & 6 deletions src/components/dbManager.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require('dotenv').config();
import Utils from './utils';
import Utils from './utils.js';
import _ from 'lodash';
import Db from "./db"
import Db from "./db.js"
import EncryptionUtils from "@verida/encryption-utils"

class DbManager {
Expand Down Expand Up @@ -113,7 +112,6 @@ class DbManager {
async createDatabase(username, databaseName, applicationName, options) {
let couch = Db.getCouch();

let response;
// Create database
try {
await couch.db.create(databaseName);
Expand Down Expand Up @@ -247,12 +245,12 @@ class DbManager {
await this._insertOrUpdate(db, writeDoc, '_design/only_permit_write_users');
} catch (err) {
// CouchDB throws a document update conflict without any obvious reason
if (err.reason != "Document update conflict.") {
if (err.reason !== "Document update conflict.") {
throw err;
}
}

if (permissions.write == "public") {
if (permissions.write === "public") {
// If the public has write permissions, disable public from deleting records
try {
const deleteFunction = "\n function(newDoc, oldDoc, userCtx, secObj) {\n if ("+deleteUsersJson+".indexOf(userCtx.name) == -1 && newDoc._deleted) throw({ unauthorized: 'User is not permitted to delete from database' });\n}";
Expand Down
8 changes: 4 additions & 4 deletions src/components/userManager.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import crypto from 'crypto';
const jwt = require('jsonwebtoken')
import Db from "./db"
import AuthManager from './authManager';
//import jwt from 'jsonwebtoken';
import Db from './db.js'
//import AuthManager from './authManager.js';

class UserManager {

Expand Down Expand Up @@ -77,7 +77,7 @@ class UserManager {
try {
await usersDb.insert(userData);
} catch (err) {
if (err.error == "conflict") {
if (err.error === "conflict") {
console.log("Public user not created -- already existed");
} else {
throw err;
Expand Down
1 change: 0 additions & 1 deletion src/components/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import EncryptionUtils from "@verida/encryption-utils"

class Utils {
Expand Down
8 changes: 4 additions & 4 deletions src/controllers/auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UserManager from '../components/userManager';
import Utils from "../components/utils";
import AuthManager from "../components/authManager"
import Db from "../components/db"
import UserManager from '../components/userManager.js';
import Utils from '../components/utils.js';
import AuthManager from '../components/authManager.js';
import Db from '../components/db.js';

class AuthController {

Expand Down
6 changes: 3 additions & 3 deletions src/controllers/user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import DbManager from '../components/dbManager';
import Utils from "../components/utils";
import Db from "../components/db"
import DbManager from '../components/dbManager.js';
import Utils from '../components/utils.js';
import Db from '../components/db.js'

class UserController {

Expand Down
2 changes: 1 addition & 1 deletion src/middleware/requestValidator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AuthManager from "../components/authManager"
import AuthManager from '../components/authManager.js'

export default async function requestValidator(req, res, next) {
const authHeader = req.headers.authorization;
Expand Down
Loading