diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..9735aca
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,22 @@
+node_modules
+.idea
+
+.git
+
+docker/
+!docker/concordia-contracts/migrate.sh
+!docker/concordia-contracts/test-contracts.sh
+!docker/concordia-app/test-app.sh
+!docker/concordia-app/nginx.conf
+!docker/ganache/start-blockchain.sh
+
+packages/*/node_modules
+packages/*/dist
+packages/*/coverage
+# TO-NEVER-DO: exclude the build folder of the contracts package, it's needed for building the application image.
+packages/concordia-app/build
+
+Jenkinsfile
+
+README.md
+packages/*/README.md
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f37fc48..31c619d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,8 +21,12 @@ yarn-error.log*
# Docker volumes
docker/volumes
+docker/ganache/volumes
docker/reports
+
+# Env var files
docker/env/concordia.env
+docker/env/contracts.env
# Misc
.env.local
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6e4a383
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 ECEntrics
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..97b0b0c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# Concordia
+> A distributed forum using Blockchain, supporting direct democratic voting
+
+## Setup
+
+```shell script
+cd apella
+yarn
+```
+
+## Compile contracts
+
+```shell script
+cd packages/apella-contracts
+yarn compile
+```
+
+## Run app
+
+```shell script
+cd packages/apella-app
+yarn start
+```
+
+## Build app
+
+```shell script
+cd packages/apella-app
+yarn build
+```
+
+## Using Docker images
+
+This project provides docker images for a number of services required to setup Concordia, as well as for Concordia
+itself.
+
+Check out the README.md in the `./docker` directory
diff --git a/docker/Makefile b/docker/Makefile
new file mode 100644
index 0000000..0b47f47
--- /dev/null
+++ b/docker/Makefile
@@ -0,0 +1,67 @@
+PACKAGES := $(abspath ${CURDIR}/../packages)
+REPORTS := $(abspath ${CURDIR}/reports)
+GANACHE_VOLUMES := $(abspath ${CURDIR}/ganache/volumes)
+
+run: compose-run build-contracts-migrate run-contracts-migrate build-app run-app
+ @echo "Concordia is up and running, head over to http://localhost:7777."
+
+# Targets for building/running/stopping the blockchain and rendezvous server (using the docker-compose file)
+compose-build:
+ @docker-compose -f ./docker-compose.yml -p concordia build
+compose-run:
+ @docker-compose -f ./docker-compose.yml -p concordia up -d
+compose-stop:
+ @docker-compose -f ./docker-compose.yml -p concordia down
+compose-stop-clean-data:
+ @docker-compose -f ./docker-compose.yml -p concordia down -v
+
+# Ganache targets
+build-ganache:
+ @docker build ../ -f ./ganache/Dockerfile -t concordia-ganache
+run-ganache:
+ @docker network create --driver bridge concordia_ganache_network || true &&\
+ docker run -d -v ${GANACHE_VOLUMES}/ganache_keys:/home/ganache_keys -p 8545:8545 --env-file=./env/ganache.docker.env --name concordia-ganache --net=concordia_ganache_network concordia-ganache:latest
+run-ganache-test:
+ @docker network create --driver bridge concordia_ganache_test_network || true &&\
+ docker run --rm -d -p 8546:8546 --env-file=./env/ganache.test.docker.env --name concordia-ganache-test --net=concordia_ganache_test_network concordia-ganache:latest
+
+# Rendezvous targets
+run-rendezvous:
+ @docker network create --driver bridge concordia_rendezvous_network || true &&\
+ docker run -d -p 9090:9090 --name concordia-rendezvous libp2p/js-libp2p-webrtc-star:version-0.20.5
+
+# Contracts targets
+build-contracts:
+ @docker build ../ -f ./concordia-contracts/Dockerfile --target compile -t concordia-contracts --build-arg TZ=Europe/Athens
+build-contracts-migrate:
+ @docker build ../ -f ./concordia-contracts/Dockerfile -t concordia-contracts-migrate --build-arg TZ=Europe/Athens
+build-contracts-tests:
+ @docker build ../ -f ./concordia-contracts/Dockerfile --target test -t concordia-contracts-tests --build-arg TZ=Europe/Athens
+run-contracts-tests:
+ @docker run --rm -v ${REPORTS}/contracts/:/usr/test-reports/ --env-file=./env/contracts.docker.env --net=concordia_ganache_test_network concordia-contracts-tests:latest
+run-contracts-tests-host-chain:
+ @docker run --rm -v ${REPORTS}/contracts/:/usr/test-reports/ --env-file=./env/contracts.env --net=host concordia-contracts-tests:latest
+run-contracts-migrate:
+ @docker run --rm -v ${PACKAGES}/concordia-contracts/build/:/usr/src/concordia/packages/concordia-contracts/build/ --env-file=./env/contracts.docker.env --net=concordia_ganache_network concordia-contracts-migrate:latest
+run-contracts-migrate-host-chain:
+ @docker run --rm -v ${PACKAGES}/concordia-contracts/build/:/usr/src/concordia/packages/concordia-contracts/build/ --env-file=./env/contracts.env --net=host concordia-contracts-migrate:latest
+get-contracts:
+ @docker run --rm -v ${PACKAGES}/concordia-contracts/build/:/build --entrypoint=sh concordia-contracts:latest -c 'cp /usr/src/concordia/packages/concordia-contracts/build/* /build'
+
+# App targets
+build-app:
+ @docker build ../ -f ./concordia-app/Dockerfile -t concordia-app --build-arg TZ=Europe/Athens
+build-app-tests:
+ @docker build ../ -f ./concordia-app/Dockerfile --target test -t concordia-app-tests --build-arg TZ=Europe/Athens
+run-app-tests:
+ @docker run --rm -v ${REPORTS}/app/:/usr/test-reports/ --env-file=./env/concordia.docker.env concordia-app-tests:latest
+run-app:
+ @docker create --env-file=./env/concordia.docker.env -p 7777:80 --name concordia-app --net=concordia_ganache_network concordia-app:latest &&\
+ docker network connect concordia_rendezvous_network concordia-app &&\
+ docker start concordia-app
+run-app-host-chain:
+ @docker run -d --env-file=./env/concordia.env --name concordia-app --net=host concordia-app:latest
+
+# Other
+clean-images:
+ @docker rmi `docker images -q -f "dangling=true"`
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..8f0d350
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,204 @@
+# Concordia Dockerized
+
+This page provides information about the provided docker images, their configuration and supported deployment
+strategies.
+
+TLDR: head down to [Putting it all together/Scripts](#piat-mkfile-targets) for a quick setup.
+
+## Services
+
+Concordia requires at the minimum two services to work, a blockchain and a rendezvous server.
+
+Additionally, the Concordia application code must be provided to the user. Currently, the only way of distributing the
+application code is via a webserver as a web application.
+
+### Ganache
+
+Ganache is a personal blockchain software used during development. It is a very convenient way of developing and testing
+dApps. More information can be found in the project's [website](https://www.trufflesuite.com/ganache).
+
+Note that any other Ethereum compliant blockchain can be used.
+
+### Rendezvous
+
+Concordia uses a distributed database to store forum data. A rendezvous server is needed in order for users to discover
+peers in the network and get access to the data.
+
+### Application
+
+The Concordia application is a React app that handles interactions with the contracts and the distributed database used.
+
+## Docker images
+
+This repository provides docker images to easily setup (and destroy) instances of all required services Concordia.
+Furthermore, we provide an image that builds the contracts and handles their migration to the blockchain in use.
+
+### Ganache
+
+The Dockerfile is provided in the path `./ganache`. The image makes use of the environment variables described
+bellow.
+
+| Environment variable | Default value | Usage |
+| --- | --- | --- |
+| ACCOUNTS_NUMBER | 10 | Set the number of accounts generated |
+| ACCOUNTS_ETHER | 100 | Set the amount of ETH assigned to each account |
+| MNEMONIC | NaN | The mnemonic phrase sued as a seed for deterministic account generation |
+| HOST | 0.0.0.0 | The hostname to listen on |
+| PORT | 8545 | The port to listen on |
+| NETWORK_ID | 5778 | The network id used to identify ganache |
+
+Note that the Ganache instance running inside the container will save the generated blockchain keys in the path
+`/home/ganache_keys/keys.json`. If you need to access the keys (eg for getting a private key and importing in Metamask)
+you can mount a volume to this path to have easier access.
+
+Also, the database used by Ganache for storing blockchain information is placed in the path `/home/ganache_db/`. You can
+maintain the blockchain state between runs by mounting a volume to the database path. To do that, add the docker flag
+`-v host/absolute/path/to/ganache_db:/home/ganache_db`.
+
+### Rendezvous
+
+The rendezvous server used here is `js-libp2p-webrtc-star`. The server listens on port 9090. More information can be
+found on the github page of the project [here](https://github.com/libp2p/js-libp2p-webrtc-star).
+
+### Contracts
+
+This is a provision system that compiles and deploys the contracts to any Ethereum blockchain.
+
+A Dockerfile is provided in the path `./concordia-contracts` that will build the contracts used by Concordia and
+handle their deployment to any Ethereum network defined using env-vars upon container run. Dockerfile contains three
+useful stages, described in the table bellow.
+
+| Stage name | Entrypoint | Usage |
+| --- | --- | --- |
+| compile | Exits immediately | Compiles the contracts |
+| test | Runs contract tests | Compiles contracts and runs tests using blockchain defined by env vars |
+| runtime | Migrates contracts | Compiles contracts and migrates to the blockchain defined by env vars. Does **not** run tests |
+
+The image makes use of the environment variables described bellow.
+
+| Environment variable | Default value | Usage |
+| --- | --- | --- |
+| MIGRATE_NETWORK | develop | Set the network where the contracts will be deployed/tested (set this to "env" unless you know what you're doing) |
+| DEPLOY_CHAIN_HOST | NaN | Set the hostname of the blockchain network that will be used for deployment (requires network to be "env") |
+| DEPLOY_CHAIN_PORT | NaN | Set the port of the blockchain network that will be used for deployment (requires network to be "env") |
+| TEST_CHAIN_HOST | NaN | Set the hostname of the blockchain network that will be used for testing (requires network to be "env") |
+| TEST_CHAIN_PORT | NaN | Set the port of the blockchain network that will be used for testing (requires network to be "env") |
+
+You can find the contract artifacts in the directory `/usr/src/concordia/packages/concordia-contracts/build/` inside
+the image.
+
+**Attention**: make sure the targeted blockchain is up and running before trying to migrate the contracts.
+
+### Application
+
+The Dockerfile provided in the path `./concordia-application` builds the application for production and serves
+the resulting build using an nginx server. Dockerfile contains two useful stages, described in the table bellow.
+
+| Stage name | Entrypoint | Usage |
+| --- | --- | --- |
+| test | Runs tests | Fetches npm packages and runs tests |
+| runtime | Serves application | Builds for production and serves it through nginx |
+
+
+The image makes use of the environment variables described bellow.
+
+| Environment variable | Default value | Usage |
+| --- | --- | --- |
+| REACT_APP_RENDEZVOUS_HOST | 127.0.0.1 | Set the hostname of the rendezvous server |
+| REACT_APP_RENDEZVOUS_PORT | 9090 | Set the port of the rendezvous server |
+
+**Attention**: this image will copy the contract artifacts from the directory `/packages/concordia-contracts/build`.
+The image is bound the these artifacts after build. If the contracts change or get re-deployed the image must be
+re-built to use the new artifacts.
+
+**Attention**: make sure the contracts have been deployed before **building** this image. Also, make sure the rendezvous
+server is up and running.
+
+## Docker Compose
+
+A docker-compose file also is provided. The docker-compose handles the lifecycle of the Ganache and Rendezvous server
+containers.
+
+## Putting it all together
+
+You can find some ready to use scripts for common scenarios like dev deploys and testing in the `./docker` directory.
+These scripts are documented in the following chapters.
+
+### Makefile targets
+
+Concordia uses blockchain and other distributed technologies. There are a number of ways to set up a running instance of
+this application.
+
+This chapter will guide you through simple setups for testing and production that depend on local blockchain (ganache)
+instances which do not require real ETH to work or have any other charges.
+
+#### Testing the contracts
+
+Build the ganache image and spin up a blockchain for testing:
+
+```shell
+make build-ganache run-ganache-test
+```
+
+Build the testing stage of the contracts image:
+
+```shell
+make build-contracts-tests
+```
+
+Run the tests:
+
+```shell
+make run-contracts-tests
+```
+
+The results should be printed in the terminal, but are also available in the directory `./reports/contracts`.
+
+#### Testing the application
+
+Build the testing stage of the application image:
+
+```shell
+make build-app-tests
+```
+
+Run the test:
+
+```shell
+make run-app-tests
+```
+
+The results should be printed in the terminal, but are also available in the directory `./reports/app`.
+
+#### Production
+
+Just run the target:
+
+```shell
+make run
+```
+
+And you' re done! Head to [localhost:7777](localhost:7777) and voilĂ , a working Concordia instance appears! The
+blockchain is exposed in the address `localhost:8545`.
+
+**Tip**: the accounts (private keys) generated by Ganache are available in the file `./volumes/ganache_keys/keys.json`.
+
+Note that the `make run` command might take several minutes to execute (depending on your system). What happens under
+the hood is that:
+
+- the ganache image is built
+- blockchain and rendezvous server containers are started
+- migration stage of the contracts image is built
+- the contracts are deployed to the blockchain:
+- the application image is built and then deployed
+
+### Env Files
+
+Targets in the Makefile make use of env files suffixed by `.docker` located in the directory `./env`. Using this
+environment variables, you can change various configuration options of the testing/production deploys.
+
+Targets suffixed with `host-chain` will try to use a blockchain and rendezvous server running in the host machine. They
+use the `--net=host` docker option and get the required environment variables from different env files,
+`./env/contracts.env` and `./env/concordia.env` (notice these env files don't include the `.docker`). These env files do
+not exist by default. The values set will largely depend on how you choose to run services in your system (which ports
+you use etc.), so be sure to create them before running any `host-chain` target. Luckily example files are provided.
diff --git a/docker/concordia-app/Dockerfile b/docker/concordia-app/Dockerfile
new file mode 100644
index 0000000..564f0be
--- /dev/null
+++ b/docker/concordia-app/Dockerfile
@@ -0,0 +1,72 @@
+# --------------------------------------------------
+# Stage 1 (Init application build base)
+# --------------------------------------------------
+FROM node:14-buster as base
+LABEL maintainers.1="Apostolos Fanakis "
+LABEL maintainers.2="Panagiotis Nikolaidis "
+LABEL gr.thmmy.ecentrics.concordia-image.name="app"
+
+WORKDIR /usr/src/concordia
+
+# Copy the root package.json and yarn.lock
+COPY ./package.json .
+COPY ./yarn.lock .
+
+# Copy package.json files from contracts and app, then install base modules
+COPY ./packages/concordia-contracts/package.json ./packages/concordia-contracts/package.json
+COPY ./packages/concordia-app/package.json ./packages/concordia-app/
+
+RUN yarn install --frozen-lockfile
+
+# Gets the rest of the source code
+COPY ./packages/concordia-contracts ./packages/concordia-contracts
+COPY ./packages/concordia-app ./packages/concordia-app
+
+# --------------------------------------------------
+# Stage 2 (Test)
+# --------------------------------------------------
+FROM base as test
+
+# Fix timezome (needed for timestamps on report files)
+ARG TZ
+ENV TZ=${TZ}
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+WORKDIR /opt/concordia-app
+
+COPY ./docker/concordia-app/test-app.sh .
+
+WORKDIR /usr/src/concordia/packages/concordia-app
+
+ENTRYPOINT ["/opt/concordia-app/test-app.sh"]
+
+# --------------------------------------------------
+# Stage 3 (Build)
+# --------------------------------------------------
+FROM base as build
+
+WORKDIR /usr/src/concordia/packages/concordia-app
+
+RUN yarn build
+
+# --------------------------------------------------
+# Stage 4 (Runtime)
+# --------------------------------------------------
+FROM nginx:1.17-alpine as runtime
+LABEL maintainers.1="Apostolos Fanakis "
+LABEL maintainers.2="Panagiotis Nikolaidis /etc/timezone \
+ && apk del tzdata \
+ && rm -rf /var/cache/apk/*
+
+WORKDIR "/var/www/concordia-app"
+
+COPY ./docker/concordia-app/nginx.conf /etc/nginx/conf.d/default.conf
+COPY --chown=nginx:nginx --from=build /usr/src/concordia/packages/concordia-app/build .
diff --git a/docker/concordia-app/nginx.conf b/docker/concordia-app/nginx.conf
new file mode 100644
index 0000000..cb598c0
--- /dev/null
+++ b/docker/concordia-app/nginx.conf
@@ -0,0 +1,22 @@
+server {
+ listen 80;
+ server_name localhost;
+
+ #charset koi8-r;
+ #access_log /var/log/nginx/host.access.log main;
+
+ location / {
+ root /var/www/concordia-app;
+ index index.html index.htm;
+ try_files "$uri" "$uri/" /index.html;
+ }
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
diff --git a/docker/concordia-app/test-app.sh b/docker/concordia-app/test-app.sh
new file mode 100644
index 0000000..70c4d9d
--- /dev/null
+++ b/docker/concordia-app/test-app.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+yarn lint -f html -o /usr/test-reports/concordia-app-eslint.html --no-color
+
+if [ $? -eq 0 ]; then
+ echo "TESTS RAN SUCCESSFULLY!"
+ exit 0
+else
+ echo "SOME TESTS FAILED!"
+ exit 1
+fi
diff --git a/docker/concordia-contracts/Dockerfile b/docker/concordia-contracts/Dockerfile
new file mode 100644
index 0000000..414c2b3
--- /dev/null
+++ b/docker/concordia-contracts/Dockerfile
@@ -0,0 +1,66 @@
+# --------------------------------------------------
+# Stage 1 (Init contracts build base)
+# --------------------------------------------------
+FROM node:14-alpine as base
+LABEL maintainers.1="Apostolos Fanakis "
+LABEL maintainers.2="Panagiotis Nikolaidis "
+LABEL gr.thmmy.ecentrics.concordia-image.name="contracts"
+
+# Fix timezome (needed for timestamps on report files)
+ARG TZ
+
+RUN apk add -U tzdata \
+ && cp /usr/share/zoneinfo/$TZ /etc/localtime \
+ && echo $TZ > /etc/timezone \
+ && apk del tzdata \
+ && rm -rf /var/cache/apk/*
+
+WORKDIR /usr/src/concordia
+
+# Copy the root package.json and yarn.lock
+COPY ./package.json .
+COPY ./yarn.lock .
+
+# Copy the contracts package.json, then install modules
+COPY ./packages/concordia-contracts/package.json ./packages/concordia-contracts/
+
+RUN yarn install --frozen-lockfile --network-timeout 100000
+
+# Gets the rest of the source code
+COPY ./packages/concordia-contracts ./packages/concordia-contracts
+
+# --------------------------------------------------
+# Stage 2 (Compile)
+# --------------------------------------------------
+FROM base as compile
+
+WORKDIR /usr/src/concordia/packages/concordia-contracts
+RUN yarn compile
+
+# --------------------------------------------------
+# Stage 3 (Test)
+# --------------------------------------------------
+FROM compile as test
+
+WORKDIR /opt/concordia-contracts
+
+COPY ./docker/concordia-contracts/test-contracts.sh .
+
+WORKDIR /usr/src/concordia/packages/concordia-contracts
+
+ENTRYPOINT ["/opt/concordia-contracts/test-contracts.sh"]
+
+# --------------------------------------------------
+# Stage 4 (Runtime)
+# --------------------------------------------------
+FROM compile as runtime
+LABEL maintainers.1="Apostolos Fanakis "
+LABEL maintainers.2="Panagiotis Nikolaidis "
+LABEL gr.thmmy.ecentrics.concordia-image.name="contracts"
+
+WORKDIR /opt/concordia-contracts
+
+COPY ./docker/concordia-contracts/migrate.sh .
+RUN ["chmod", "+x", "/opt/concordia-contracts/migrate.sh"]
+
+ENTRYPOINT ["/opt/concordia-contracts/migrate.sh"]
diff --git a/docker/concordia-contracts/migrate.sh b/docker/concordia-contracts/migrate.sh
new file mode 100644
index 0000000..29d7449
--- /dev/null
+++ b/docker/concordia-contracts/migrate.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+export CHAIN_HOST="$DEPLOY_CHAIN_HOST"
+export CHAIN_PORT="$DEPLOY_CHAIN_PORT"
+
+cd /usr/src/concordia/packages/concordia-contracts && yarn _migrate --network "${MIGRATE_NETWORK}" --reset
diff --git a/docker/concordia-contracts/test-contracts.sh b/docker/concordia-contracts/test-contracts.sh
new file mode 100644
index 0000000..cbae85d
--- /dev/null
+++ b/docker/concordia-contracts/test-contracts.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+export CHAIN_HOST="$TEST_CHAIN_HOST"
+export CHAIN_PORT="$TEST_CHAIN_PORT"
+
+yarn _eslint -f html -o /usr/test-reports/concordia-contracts-eslint.html --no-color &&
+ (yarn _solhint >/usr/test-reports/concordia-contracts-solhint.report) &&
+ (yarn test --network env >/usr/test-reports/concordia-contracts-truffle-tests.report)
+
+if [ $? -eq 0 ]; then
+ echo "TESTS RAN SUCCESSFULLY!"
+ exit 0
+else
+ echo "SOME TESTS FAILED!"
+ exit 1
+fi
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..3fa4386
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,34 @@
+version: '3.8'
+
+services:
+ ganache:
+ build:
+ context: ../
+ dockerfile: ./docker/ganache/Dockerfile
+ image: concordia-ganache
+ container_name: concordia-ganache
+ env_file:
+ - env/ganache.docker.env
+ expose:
+ - 8545
+ ports:
+ - 8545:8545
+ user: root
+ volumes:
+ - ./ganache/volumes/ganache_keys:/home/ganache_keys
+ networks:
+ ganache_network:
+ restart: always
+
+ rendezvous:
+ image: libp2p/js-libp2p-webrtc-star:version-0.20.5
+ container_name: concordia-rendezvous
+ networks:
+ rendezvous_network:
+ ports:
+ - 9090:9090
+ restart: always
+
+networks:
+ ganache_network:
+ rendezvous_network:
diff --git a/docker/env/concordia.docker.env b/docker/env/concordia.docker.env
new file mode 100644
index 0000000..1810795
--- /dev/null
+++ b/docker/env/concordia.docker.env
@@ -0,0 +1,7 @@
+# Variables needed in runtime (in browser)
+REACT_APP_RENDEZVOUS_HOST=rendezvous
+REACT_APP_RENDEZVOUS_PORT=9090
+
+# If the rendezvous server is running on host use these instead
+#REACT_APP_RENDEZVOUS_HOST=127.0.0.1
+#REACT_APP_RENDEZVOUS_PORT=9090
diff --git a/docker/env/concordia.example.env b/docker/env/concordia.example.env
new file mode 100644
index 0000000..c593d66
--- /dev/null
+++ b/docker/env/concordia.example.env
@@ -0,0 +1,20 @@
+# Set to "CI" if in CI environment, anything else (including unset) will be ignored
+BUILD_ENV={CI}
+
+# Docker compose variables
+VIRTUAL_HOST=example.com
+VIRTUAL_PORT=3000
+
+# If you uncomment the lines below, Concordia will become available through https BUT the rendezvous
+# server will stop working and IPFS initialization won't complete
+#LETSENCRYPT_HOST=example.com
+#LETSENCRYPT_EMAIL=someemail.email.com
+
+# Variables needed in runtime
+# TO-NEVER-DO: change CONCORDIA_HOST to localhost
+CONCORDIA_HOST=0.0.0.0
+CONCORDIA_PORT=3000
+
+# Variables needed in runtime (in browser)
+REACT_APP_RENDEZVOUS_HOST=xx.xxx.xxx.xxx
+REACT_APP_RENDEZVOUS_PORT=9090
diff --git a/docker/env/contracts.docker.env b/docker/env/contracts.docker.env
new file mode 100644
index 0000000..1b6d49c
--- /dev/null
+++ b/docker/env/contracts.docker.env
@@ -0,0 +1,14 @@
+# Variables needed in runtime
+MIGRATE_NETWORK=env
+DEPLOY_CHAIN_HOST=concordia-ganache
+DEPLOY_CHAIN_PORT=8545
+
+TEST_CHAIN_HOST=concordia-ganache-test
+TEST_CHAIN_PORT=8546
+
+# If the blockchain is running on host use these instead
+#DEPLOY_CHAIN_HOST=127.0.0.1
+#DEPLOY_CHAIN_PORT=8545
+
+#TEST_CHAIN_HOST=127.0.0.1
+#TEST_CHAIN_PORT=8546
diff --git a/docker/env/contracts.example.env b/docker/env/contracts.example.env
new file mode 100644
index 0000000..3194b9c
--- /dev/null
+++ b/docker/env/contracts.example.env
@@ -0,0 +1,7 @@
+# Variables needed in runtime
+MIGRATE_NETWORK=env
+DEPLOY_CHAIN_HOST=xx.xxx.xxx.xxx
+DEPLOY_CHAIN_PORT=8545
+
+TEST_CHAIN_HOST=xx.xxx.xxx.xxx
+TEST_CHAIN_PORT=8545
diff --git a/docker/env/ganache.docker.env b/docker/env/ganache.docker.env
new file mode 100644
index 0000000..0187b06
--- /dev/null
+++ b/docker/env/ganache.docker.env
@@ -0,0 +1,5 @@
+ACCOUNTS_NUMBER=10
+ACCOUNTS_ETHER=100
+HOST=0.0.0.0
+PORT=8545
+NETWORK_ID=5778
diff --git a/docker/env/ganache.test.docker.env b/docker/env/ganache.test.docker.env
new file mode 100644
index 0000000..479da1f
--- /dev/null
+++ b/docker/env/ganache.test.docker.env
@@ -0,0 +1,6 @@
+ACCOUNTS_NUMBER=5
+ACCOUNTS_ETHER=1
+MNEMONIC="myth like bonus scare over problem client lizard pioneer submit female collect"
+HOST=0.0.0.0
+PORT=8546
+NETWORK_ID=5778
diff --git a/docker/ganache/Dockerfile b/docker/ganache/Dockerfile
new file mode 100644
index 0000000..80eba88
--- /dev/null
+++ b/docker/ganache/Dockerfile
@@ -0,0 +1,10 @@
+FROM trufflesuite/ganache-cli:latest
+
+RUN mkdir /home/ganache_db /home/ganache_keys
+
+WORKDIR /opt/concordia-ganache
+
+COPY ./docker/ganache/start-blockchain.sh .
+RUN ["chmod", "+x", "/opt/concordia-ganache/start-blockchain.sh"]
+
+ENTRYPOINT ["/opt/concordia-ganache/start-blockchain.sh"]
diff --git a/docker/ganache/start-blockchain.sh b/docker/ganache/start-blockchain.sh
new file mode 100644
index 0000000..36aceea
--- /dev/null
+++ b/docker/ganache/start-blockchain.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+N_ACCOUNTS="${ACCOUNTS_NUMBER:-10}"
+ETHER="${ACCOUNTS_ETHER:-10}"
+HOST="${HOST:-"0.0.0.0"}"
+PORT="${PORT:-8545}"
+ID="${NETWORK_ID:-5778}"
+
+if [ -z "${MNEMONIC}" ]; then
+ echo "Starting Ganache with non deterministic address generation"
+ node /app/ganache-core.docker.cli.js \
+ --accounts "$N_ACCOUNTS" \
+ --defaultBalanceEther "$ETHER" \
+ --host "$HOST" \
+ --port "$PORT" \
+ --networkId "$ID" \
+ --account_keys_path "/home/ganache_keys/keys.json" \
+ --db "/home/ganache_db/" \
+ --allowUnlimitedContractSize \
+ --noVMErrorsOnRPCResponse \
+ --verbose
+else
+ echo "Starting Ganache with deterministic address generation"
+ node /app/ganache-core.docker.cli.js \
+ --accounts "$N_ACCOUNTS" \
+ --defaultBalanceEther "$ETHER" \
+ --mnemonic "$MNEMONIC" \
+ --host "$HOST" \
+ --port "$PORT" \
+ --networkId "$ID" \
+ --account_keys_path "/home/ganache_keys/keys.json" \
+ --db "/home/ganache_db/" \
+ --allowUnlimitedContractSize \
+ --noVMErrorsOnRPCResponse \
+ --deterministic \
+ --verbose
+fi
diff --git a/package.json b/package.json
index fafeadd..5c9bdd6 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,13 @@
{
- "name": "apella",
+ "name": "concordia",
"private": true,
"workspaces": {
- "packages": ["packages/*"],
- "nohoist": ["**/web3", "**/web3/**"]
+ "packages": [
+ "packages/*"
+ ],
+ "nohoist": [
+ "**/web3",
+ "**/web3/**"
+ ]
}
}
diff --git a/packages/concordia-app/.dockerignore b/packages/concordia-app/.dockerignore
new file mode 100644
index 0000000..e69de29
diff --git a/packages/concordia-app/.env.development.example b/packages/concordia-app/.env.development.example
new file mode 100644
index 0000000..eeeb884
--- /dev/null
+++ b/packages/concordia-app/.env.development.example
@@ -0,0 +1,12 @@
+# This is an example development configuration for the app
+# To create your own configuration, copy this one and ommit the ".example" from the filename, then change the
+# environment cariables to the prefered values.
+
+# Node dev-server host & port
+HOST=localhost
+PORT=7000
+
+# Variables needed in runtime (in browser)
+# Carefull, IPFS won't accept localhost as a valid hostname
+REACT_APP_RENDEZVOUS_HOST=127.0.0.1
+REACT_APP_RENDEZVOUS_PORT=9090
diff --git a/packages/concordia-app/.gitignore b/packages/concordia-app/.gitignore
new file mode 100644
index 0000000..74ef14b
--- /dev/null
+++ b/packages/concordia-app/.gitignore
@@ -0,0 +1,22 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env
+.env.local
+.env.development
+.env.development.local
+.env.test
+.env.test.local
+.env.production
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/packages/concordia-app/package.json b/packages/concordia-app/package.json
index f2bcbf9..6645ee2 100644
--- a/packages/concordia-app/package.json
+++ b/packages/concordia-app/package.json
@@ -25,8 +25,8 @@
},
"dependencies": {
"@ezerous/breeze": "~0.4.0",
- "@ezerous/drizzle": "~0.4.0",
- "@ezerous/eth-identity-provider": "^0.1.0",
+ "@ezerous/drizzle": "~0.4.1",
+ "@ezerous/eth-identity-provider": "~0.1.2",
"@reduxjs/toolkit": "~1.4.0",
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"concordia-contracts": "~0.1.0",
@@ -44,6 +44,7 @@
"react-router-dom": "^5.2.0",
"react-scripts": "~3.4.3",
"redux-saga": "~1.1.3",
+ "react-timeago": "~5.2.0",
"semantic-ui-css": "~2.4.1",
"semantic-ui-react": "~1.2.1",
"web3": "1.3.0"
diff --git a/packages/concordia-app/public/locales/en/translation.json b/packages/concordia-app/public/locales/en/translation.json
index c55754f..74f1e18 100644
--- a/packages/concordia-app/public/locales/en/translation.json
+++ b/packages/concordia-app/public/locales/en/translation.json
@@ -2,22 +2,80 @@
"board.header.no.topics.message": "There are no topics yet!",
"board.sub.header.no.topics.guest": "Sign up and be the first to post.",
"board.sub.header.no.topics.user": "Be the first to post.",
+ "clear.databases.modal.cancel.button": "Cancel, keep databases",
+ "clear.databases.modal.clear.button": "Yes, delete databases",
+ "clear.databases.modal.clearing.progress.message": "This might take a minute...",
+ "clear.databases.modal.clearing.progress.title": "Clearing all Concordia databases",
+ "clear.databases.modal.description.body.user": "Although this action is generally recoverable some of your topics and posts may be permanently lost.",
+ "clear.databases.modal.description.pre": "You are about to clear the Concordia databases stored locally in your browser.",
+ "clear.databases.modal.form.username.label.guest": "Please type concordia to confirm.",
+ "clear.databases.modal.form.username.label.user": "Please type your username to confirm.",
+ "clear.databases.modal.title": "Clear all Concordia databases. Are you sure?",
+ "custom.loading.tab.pane.default.generic.message": "Magic in the background",
+ "edit.information.modal.form.cancel.button": "Cancel",
+ "edit.information.modal.form.error.invalid.profile.picture.url.message": "The profile picture URL provided is not valid.",
+ "edit.information.modal.form.error.message.header": "Form contains errors",
+ "edit.information.modal.form.location.field.label": "Location",
+ "edit.information.modal.form.location.field.placeholder": "Location",
+ "edit.information.modal.form.profile.picture.field.label": "Profile picture URL",
+ "edit.information.modal.form.profile.picture.field.placeholder": "URL",
+ "edit.information.modal.form.submit.button": "Submit",
+ "edit.information.modal.title": "Edit profile information",
+ "post.create.form.send.button": "Post",
+ "post.form.content.field.placeholder": "Message",
+ "post.form.subject.field.placeholder": "Subject",
+ "post.list.row.post.id": "#{{id}}",
+ "profile.general.tab.address.row.title": "Account address:",
+ "profile.general.tab.clear.databases.button.title": "Clear databases",
+ "profile.general.tab.edit.info.button.title": "Edit information",
+ "profile.general.tab.location.row.not.set": "Not set",
+ "profile.general.tab.location.row.title": "Location:",
+ "profile.general.tab.number.of.posts.row.title": "Number of posts:",
+ "profile.general.tab.number.of.topics.row.title": "Number of topics created:",
+ "profile.general.tab.posts.db.address.row.title": "PostsDB:",
+ "profile.general.tab.registration.date.row.title": "Member since:",
+ "profile.general.tab.save.info.button.title": "Save information",
+ "profile.general.tab.title": "General",
+ "profile.general.tab.topics.db.address.row.title": "TopicsDB:",
+ "profile.general.tab.user.db.address.row.title": "UserDB:",
+ "profile.general.tab.username.row.title": "Username:",
+ "profile.posts.tab.title": "Posts",
+ "profile.topics.tab.title": "Topics",
+ "profile.user.has.no.posts.header.message": "{{user}} has not posted yet",
+ "profile.user.has.no.topics.header.message": "{{user}} has created no topics yet",
"register.card.header": "Sign Up",
"register.form.button.back": "Back",
- "register.form.button.guest": "Continue as guest",
- "register.form.button.submit": "Sign Up",
- "register.form.error.message.header": "Form contains errors",
- "register.form.error.username.taken.message": "The username {{username}} is already taken.",
"register.form.header.already.member.message": "There is already an account for this address.\nIf you want to create another account please change your address.",
- "register.form.username.field.label": "Username",
- "register.form.username.field.placeholder": "Username",
+ "register.form.personal.information.step.button.skip": "Skip for now",
+ "register.form.personal.information.step.button.submit": "Submit",
+ "register.form.personal.information.step.error.invalid.profile.picture.url.message": "The profile picture URL provided is not valid.",
+ "register.form.personal.information.step.error.message.header": "Form contains errors",
+ "register.form.personal.information.step.location.field.label": "Location",
+ "register.form.personal.information.step.location.field.placeholder": "Location",
+ "register.form.personal.information.step.profile.picture.field.label": "Profile picture URL",
+ "register.form.personal.information.step.profile.picture.field.placeholder": "URL",
+ "register.form.profile.information.step.description": "Give a hint about who you are",
+ "register.form.profile.information.step.title": "Profile Information",
+ "register.form.sign.up.step.button.guest": "Continue as guest",
+ "register.form.sign.up.step.button.submit": "Sign Up",
+ "register.form.sign.up.step.description": "Create a Concordia account",
+ "register.form.sign.up.step.error.message.header": "Form contains errors",
+ "register.form.sign.up.step.title": "Sign Up",
"register.p.account.address": "Account address:",
+ "topbar.button.clear.databases": "Clear databases",
"topbar.button.create.topic": "Create topic",
"topbar.button.profile": "Profile",
"topbar.button.register": "Sign Up",
+ "topic.create.form.content.field.label": "First post content",
+ "topic.create.form.content.field.placeholder": "Message",
+ "topic.create.form.post.button": "Post",
"topic.create.form.subject.field.label": "Topic subject",
"topic.create.form.subject.field.placeholder": "Subject",
- "topic.create.form.message.field.label": "First post message",
- "topic.create.form.message.field.placeholder": "Message",
- "topic.create.form.post.button": "Post"
+ "topic.list.row.author": "by {{author}}",
+ "topic.list.row.number.of.replies": "{{numberOfReplies}} replies",
+ "topic.list.row.topic.id": "#{{id}}",
+ "username.selector.error.username.empty.message": "Username is required",
+ "username.selector.error.username.taken.message": "The username {{username}} is already taken.",
+ "username.selector.username.field.label": "Username",
+ "username.selector.username.field.placeholder": "Username"
}
\ No newline at end of file
diff --git a/packages/concordia-app/public/manifest.json b/packages/concordia-app/public/manifest.json
index ce536e4..60b7e11 100644
--- a/packages/concordia-app/public/manifest.json
+++ b/packages/concordia-app/public/manifest.json
@@ -1,6 +1,6 @@
{
- "short_name": "Apella",
- "name": "Apella",
+ "short_name": "Concordia",
+ "name": "Concordia",
"icons": [
{
"src": "favicon.ico",
diff --git a/packages/concordia-app/src/App.jsx b/packages/concordia-app/src/App.jsx
index 7ed70c0..4e97d13 100644
--- a/packages/concordia-app/src/App.jsx
+++ b/packages/concordia-app/src/App.jsx
@@ -2,23 +2,23 @@ import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import PropTypes from 'prop-types';
-import LoadingContainer from './components/LoadingContainer';
+import InitializationScreen from './components/InitializationScreen';
import Routes from './Routes';
import './intl/index';
import 'semantic-ui-css/semantic.min.css';
-import './assets/css/app.css';
const App = ({ store }) => (
-
+
-
+
);
App.propTypes = {
+ // eslint-disable-next-line react/forbid-prop-types
store: PropTypes.object.isRequired,
};
diff --git a/packages/concordia-app/src/Routes.jsx b/packages/concordia-app/src/Routes.jsx
index 9931cfa..15a11b2 100644
--- a/packages/concordia-app/src/Routes.jsx
+++ b/packages/concordia-app/src/Routes.jsx
@@ -44,6 +44,11 @@ const routesConfig = [
path: '/topics/:id(\\bnew\\b|\\d+)',
component: lazy(() => import('./views/Topic')),
},
+ {
+ exact: true,
+ path: ['/users/:id', '/profiles/:id', '/profile'],
+ component: lazy(() => import('./views/Profile')),
+ },
{
component: () => ,
},
diff --git a/packages/concordia-app/src/assets/css/app.css b/packages/concordia-app/src/assets/css/app.css
deleted file mode 100644
index 4e01f90..0000000
--- a/packages/concordia-app/src/assets/css/app.css
+++ /dev/null
@@ -1,7 +0,0 @@
-body {
- margin: 1em !important;
-}
-
-.i18next-newlines {
- white-space: pre-line !important;
-}
\ No newline at end of file
diff --git a/packages/concordia-app/src/assets/css/index.css b/packages/concordia-app/src/assets/css/index.css
new file mode 100644
index 0000000..2a2d849
--- /dev/null
+++ b/packages/concordia-app/src/assets/css/index.css
@@ -0,0 +1,17 @@
+body.app {
+ overflow: auto;
+ margin: 1em !important;
+}
+
+#root {
+ height: 100%;
+}
+
+.i18next-newlines {
+ white-space: pre-line !important;
+}
+
+.text-secondary {
+ color: gray;
+ font-style: italic;
+}
diff --git a/packages/concordia-app/src/assets/css/loading-component.css b/packages/concordia-app/src/assets/css/loading-component.css
index e4635a2..37a8f96 100644
--- a/packages/concordia-app/src/assets/css/loading-component.css
+++ b/packages/concordia-app/src/assets/css/loading-component.css
@@ -2,16 +2,16 @@ body {
overflow: hidden;
}
-ul {
- list-style-position: inside;
-}
-
.loading-screen {
margin-top: 10em;
text-align: center;
font-size: large;
}
+.loading-screen ul {
+ list-style-position: inside;
+}
+
.loading-img {
margin-bottom: 3em;
height: 12em;
diff --git a/packages/concordia-app/src/assets/images/metamask_logo.svg b/packages/concordia-app/src/assets/images/metamask_logo.svg
new file mode 100644
index 0000000..a6cffef
--- /dev/null
+++ b/packages/concordia-app/src/assets/images/metamask_logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/concordia-app/src/assets/particles.js b/packages/concordia-app/src/assets/particles.js
index 20cfa33..1862f6f 100644
--- a/packages/concordia-app/src/assets/particles.js
+++ b/packages/concordia-app/src/assets/particles.js
@@ -1,7 +1,7 @@
const particlesOptions = {
particles: {
number: {
- value: 60,
+ value: 90,
density: {
enable: true,
value_area: 1500,
@@ -9,11 +9,11 @@ const particlesOptions = {
},
line_linked: {
enable: true,
- opacity: 0.02,
+ opacity: 0.04,
},
move: {
- direction: 'right',
- speed: 0.05,
+ direction: 'none',
+ speed: 0.12,
},
size: {
value: 1,
@@ -21,24 +21,11 @@ const particlesOptions = {
opacity: {
anim: {
enable: true,
- speed: 1,
+ speed: 1.3,
opacity_min: 0.05,
},
},
},
- interactivity: {
- events: {
- onclick: {
- enable: true,
- mode: 'push',
- },
- },
- modes: {
- push: {
- particles_nb: 1,
- },
- },
- },
retina_detect: true,
};
diff --git a/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx b/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx
new file mode 100644
index 0000000..26e47fe
--- /dev/null
+++ b/packages/concordia-app/src/components/ClearDatabasesModal/index.jsx
@@ -0,0 +1,151 @@
+import React, {
+ useCallback, useMemo, useState,
+ useEffect,
+} from 'react';
+import {
+ Button, Form, Input, Modal,
+} from 'semantic-ui-react';
+import PropTypes from 'prop-types';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import purgeIndexedDBs from '../../utils/indexedDB/indexedDBUtils';
+
+const ClearDatabasesModal = (props) => {
+ const {
+ open, onDatabasesCleared, onCancel,
+ } = props;
+ const [confirmationInput, setConfirmationInput] = useState('');
+ const [userConfirmed, setUserConfirmed] = useState(false);
+ const [isClearing, setIsClearing] = useState(false);
+ const user = useSelector((state) => state.user);
+ const { t } = useTranslation();
+
+ useEffect(() => {
+ if (user.hasSignedUp && confirmationInput === user.username) {
+ setUserConfirmed(true);
+ } else if (!user.hasSignedUp && confirmationInput === 'concordia') {
+ setUserConfirmed(true);
+ } else {
+ setUserConfirmed(false);
+ }
+ }, [confirmationInput, user.hasSignedUp, user.username]);
+
+ const handleSubmit = useCallback(() => {
+ setIsClearing(true);
+
+ purgeIndexedDBs()
+ .then(() => {
+ onDatabasesCleared();
+ }).catch((reason) => console.log(reason));
+ }, [onDatabasesCleared]);
+
+ const onCancelTry = useCallback(() => {
+ if (!isClearing) {
+ setConfirmationInput('');
+ onCancel();
+ }
+ }, [isClearing, onCancel]);
+
+ const handleInputChange = (event, { value }) => { setConfirmationInput(value); };
+
+ const modalContent = useMemo(() => {
+ if (isClearing) {
+ return (
+ <>
+