1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00

Merge branch 'master' into feature/search-tweaks

This commit is contained in:
Matthias Kretschmann 2019-04-04 17:23:10 +02:00 committed by GitHub
commit 0c38b6caa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 488 additions and 155 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -29,7 +29,12 @@ If you're a developer and want to contribute to, or want to utilize this marketp
## Get Started
To make use of all the functionality, you need to connect to the Ocean network. By default, the client will connect to Ocean's Nile test network remotely.
To make use of all the functionality, you need to connect to the Ocean network. By default, the client will connect to [Ocean's Nile test network](https://docs.oceanprotocol.com/concepts/testnets/#the-nile-testnet) remotely.
This repo contains a client and a server, both written in TypeScript:
- **client**: React app setup with [squid-js](https://github.com/oceanprotocol/squid-js), bootstrapped with [Create React App](https://github.com/facebook/create-react-app)
- **server**: Node.js app, utilizing [Express](https://expressjs.com). The server provides various microservices, like remote file checking.
To spin up both, the client and the server in a watch mode for local development, execute:
@ -58,7 +63,7 @@ Modify `./client/src/config.ts` to use those local connections.
## Production
To create a production build of both, the client and the server:
To create a production build of both, the client and the server, run from the root of the project:
```bash
npm run build
@ -72,12 +77,11 @@ Builds the client for production to the `./client/build` folder, and the server
npm test
```
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
Launches the test runner in the interactive watch mode.
## Code Style
For linting and auto-formatting you can use:
For linting and auto-formatting you can use from the root of the project:
```bash
# auto format all ts & css with eslint & stylelint

1
client/.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules

47
client/Dockerfile Normal file
View File

@ -0,0 +1,47 @@
FROM node:11-alpine
LABEL maintainer="Ocean Protocol <devops@oceanprotocol.com>"
RUN apk add --no-cache --update\
bash\
g++\
gcc\
git\
gettext\
make\
python
COPY . /app/frontend
WORKDIR /app/frontend
RUN npm install -g npm serve
RUN npm install
# Default ENV values
# src/config/config.ts
ENV SERVICE_SCHEME='http'
ENV SERVICE_HOST='localhost'
ENV SERVICE_PORT='4000'
ENV NODE_SCHEME='http'
ENV NODE_HOST='localhost'
ENV NODE_PORT='8545'
ENV AQUARIUS_SCHEME='http'
ENV AQUARIUS_HOST='localhost'
ENV AQUARIUS_PORT='5000'
ENV BRIZO_SCHEME='http'
ENV BRIZO_HOST='localhost'
ENV BRIZO_PORT='8030'
ENV BRIZO_ADDRESS='0x00bd138abd70e2f00903268f3db08f2d25677c9e'
ENV PARITY_SCHEME='http'
ENV PARITY_HOST='localhost'
ENV PARITY_PORT='8545'
ENV SECRET_STORE_SCHEME='http'
ENV SECRET_STORE_HOST='localhost'
ENV SECRET_STORE_PORT='12001'
ENV FAUCET_SCHEME='http'
ENV FAUCET_HOST='localhost'
ENV FAUCET_PORT='3001'
ENV LISTEN_ADDRESS='0.0.0.0'
ENV LISTEN_PORT='3000'
ENTRYPOINT ["/app/frontend/scripts/docker-entrypoint.sh"]

View File

@ -11,7 +11,7 @@
},
"dependencies": {
"@oceanprotocol/art": "^2.2.0",
"@oceanprotocol/squid": "^0.5.0",
"@oceanprotocol/squid": "^0.5.1",
"@oceanprotocol/typographies": "^0.1.0",
"classnames": "^2.2.6",
"filesize": "^4.1.2",

View File

@ -10,7 +10,40 @@
<meta name="theme-color" content="#141414" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Commons Marketplace</title>
<title>Commons</title>
<meta
content="A marketplace to find and publish open data sets in the Ocean Network."
name="description"
/>
<meta
content="https://commons.oceanprotocol.com/share.png"
name="image"
/>
<link href="https://commons.oceanprotocol.com" rel="canonical" />
<meta content="https://commons.oceanprotocol.com" property="og:url" />
<meta content="Commons" property="og:title" />
<meta
content="A marketplace to find and publish open data sets in the Ocean Network."
property="og:description"
/>
<meta
content="https://commons.oceanprotocol.com/share.png"
property="og:image"
/>
<meta content="summary_large_image" name="twitter:card" />
<meta content="@oceanprotocol" name="twitter:creator" />
<meta content="Commons" name="twitter:title" />
<meta
content="A marketplace to find and publish open data sets in the Ocean Network."
name="twitter:description"
/>
<meta
content="https://commons.oceanprotocol.com/share.png"
name="twitter:image"
/>
<style>
.loader {

BIN
client/public/share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

View File

@ -0,0 +1,37 @@
#!/bin/sh
SERVICE_SCHEME=${SERVICE_SCHEME:-http}
SERVICE_HOST=${SERVICE_HOST:-localhost}
SERVICE_PORT=${SERVICE_PORT:-8545}
NODE_SCHEME=${NODE_SCHEME:-http}
NODE_HOST=${NODE_HOST:-localhost}
NODE_PORT=${NODE_PORT:-8545}
AQUARIUS_SCHEME=${AQUARIUS_SCHEME:-http}
AQUARIUS_HOST=${AQUARIUS_HOST:-localhost}
AQUARIUS_PORT=${AQUARIUS_PORT:-5000}
BRIZO_SCHEME=${BRIZO_SCHEME:-http}
BRIZO_HOST=${BRIZO_HOST:-localhost}
BRIZO_PORT=${BRIZO_PORT:-8030}
BRIZO_PASSWORD=${BRIZO_PASSWORD:-0x00bd138abd70e2f00903268f3db08f2d25677c9e}
PARITY_SCHEME=${PARITY_SCHEME:-http}
PARITY_HOST=${PARITY_HOST:-localhost}
PARITY_PORT=${PARITY_PORT:-8545}
SECRET_STORE_SCHEME=${SECRET_STORE_SCHEME:-http}
SECRET_STORE_HOST=${SECRET_STORE_HOST:-localhost}
SECRET_STORE_PORT=${SECRET_STORE_PORT:-12001}
FAUCET_SCHEME=${FAUCET_SCHEME:-http}
FAUCET_HOST=${FAUCET_HOST:-localhost}
FAUCET_PORT=${FAUCET_PORT:-443}
envsubst < /app/frontend/src/config/config.ts.template > /app/frontend/src/config/config.ts
if [ "${LOCAL_CONTRACTS}" = "true" ]; then
echo "Waiting for contracts to be generated..."
while [ ! -f "/app/frontend/node_modules/@oceanprotocol/keeper-contracts/artifacts/ready" ]; do
sleep 2
done
fi
echo "Starting Commons..."
npm run build
serve -l tcp://"${LISTEN_ADDRESS}":"${LISTEN_PORT}" -s /app/frontend/build/

View File

@ -18,7 +18,7 @@ import {
faucetHost,
faucetPort,
faucetScheme
} from './config'
} from './config/config'
declare global {
interface Window {
@ -40,6 +40,7 @@ interface AppState {
web3: Web3
ocean: {}
startLogin: () => void
message: string
}
class App extends Component<{}, AppState> {
@ -94,7 +95,8 @@ class App extends Component<{}, AppState> {
account: '',
ocean: {},
startLogin: this.startLogin,
requestFromFaucet: this.requestFromFaucet
requestFromFaucet: this.requestFromFaucet,
message: 'Connecting to Ocean...'
}
public async componentDidMount() {
@ -185,7 +187,7 @@ class App extends Component<{}, AppState> {
<main className={styles.main}>
{this.state.isLoading ? (
<div className={styles.loader}>
<Spinner message="Connecting to Ocean..." />
<Spinner message={this.state.message} />
</div>
) : (
<Routes />

View File

@ -66,6 +66,7 @@
padding: 0;
color: $brand-pink;
font-size: $font-size-base;
font-weight: $font-weight-base;
font-family: inherit;
box-shadow: none;
cursor: pointer;

View File

@ -1,4 +1,5 @@
import React, { PureComponent } from 'react'
import { Link } from 'react-router-dom'
import cx from 'classnames'
import styles from './Button.module.scss'
@ -10,6 +11,7 @@ interface ButtonProps {
href?: string
onClick?: any
disabled?: boolean
to?: string
}
export default class Button extends PureComponent<ButtonProps, any> {
@ -21,6 +23,7 @@ export default class Button extends PureComponent<ButtonProps, any> {
href,
children,
className,
to,
...props
} = this.props
@ -32,11 +35,23 @@ export default class Button extends PureComponent<ButtonProps, any> {
classes = styles.button
}
return href ? (
<a href={href} className={cx(classes, className)} {...props}>
{children}
</a>
) : (
if (to) {
return (
<Link to={to} className={cx(classes, className)} {...props}>
{children}
</Link>
)
}
if (href) {
return (
<a href={href} className={cx(classes, className)} {...props}>
{children}
</a>
)
}
return (
<button className={cx(classes, className)} {...props}>
{children}
</button>

View File

@ -12,8 +12,11 @@ const AssetLink = ({ asset, list }: { asset: any; list?: boolean }) => {
<article className={styles.assetList}>
<Link to={`/asset/${asset.id}`}>
<h1>{base.name}</h1>
<div className={styles.date} title={base.dateCreated}>
{moment(base.dateCreated, 'YYYYMMDD').fromNow()}
<div
className={styles.date}
title={`Published on ${base.datePublished}`}
>
{moment(base.datePublished, 'YYYYMMDD').fromNow()}
</div>
</Link>
</article>

View File

@ -52,7 +52,7 @@ export default class AssetsUser extends PureComponent<
<div className={styles.assetsUser}>
{this.props.recent && (
<h2 className={styles.subTitle}>
Your Latest Data Sets
Your Latest Published Data Sets
</h2>
)}

View File

@ -1,12 +1,28 @@
import React from 'react'
import { NavLink } from 'react-router-dom'
import { ReactComponent as Logo } from '@oceanprotocol/art/logo/logo.svg'
import { User } from '../../context/User'
import AccountStatus from '../molecules/AccountStatus'
import styles from './Header.module.scss'
import menu from '../../data/menu.json'
import meta from '../../data/meta.json'
const MenuItem = ({ item, isWeb3 }: { item: any; isWeb3: boolean }) => {
if (item.web3 && !isWeb3) return null
return (
<NavLink
to={item.link}
className={styles.link}
activeClassName={styles.linkActive}
exact
>
{item.title}
</NavLink>
)
}
const Header = () => (
<header className={styles.header}>
<div className={styles.headerContent}>
@ -16,17 +32,17 @@ const Header = () => (
</NavLink>
<nav className={styles.headerMenu}>
{menu.map(item => (
<NavLink
key={item.title}
to={item.link}
className={styles.link}
activeClassName={styles.linkActive}
exact
>
{item.title}
</NavLink>
))}
<User.Consumer>
{states =>
menu.map(item => (
<MenuItem
key={item.title}
item={item}
isWeb3={states.isWeb3}
/>
))
}
</User.Consumer>
</nav>
<AccountStatus className={styles.accountStatus} />
</div>

View File

@ -0,0 +1,36 @@
//
// commons-server connection
//
export const serviceScheme = '${SERVICE_SCHEME}'
export const serviceHost = '${SERVICE_HOST}'
export const servicePort = '${SERVICE_PORT}'
//
// OCEAN REMOTE CONNECTIONS
//
export const nodeScheme = '${NODE_SCHEME}'
export const nodeHost = '${NODE_HOST}'
export const nodePort = '${NODE_PORT}'
export const aquariusScheme = '${AQUARIUS_SCHEME}'
export const aquariusHost = '${AQUARIUS_HOST}'
export const aquariusPort = '${AQUARIUS_PORT}'
export const brizoScheme = '${BRIZO_SCHEME}'
export const brizoHost = '${BRIZO_HOST}'
export const brizoPort = '${BRIZO_ADDRESS}'
export const brizoAddress = '${BRIZO_ADDRESS}'
export const parityScheme = '${PARITY_SCHEME}'
export const parityHost = '${PARITY_HOST}'
export const parityPort = '${PARITY_PORT}'
export const secretStoreScheme = '${SECRET_STORE_SCHEME}'
export const secretStoreHost = '${SECRET_STORE_HOST}'
export const secretStorePort = '${SECRET_STORE_PORT}'
export const faucetScheme = '${FAUCET_SCHEME}'
export const faucetHost = '${FAUCET_HOST}'
export const faucetPort = '${FAUCET_PORT}'
export const verbose = true

View File

@ -1,22 +1,21 @@
[
{
"title": "Publish",
"link": "/publish"
"link": "/publish",
"web3": true
},
{
"title": "History",
"link": "/history"
"link": "/history",
"web3": true
},
{
"title": "Faucet",
"link": "/faucet"
"link": "/faucet",
"web3": true
},
{
"title": "About",
"link": "/about"
},
{
"title": "Styleguide",
"link": "/styleguide"
}
]

View File

@ -18,7 +18,7 @@ import {
secretStorePort,
secretStoreScheme,
verbose
} from './config'
} from './config/config'
export async function provideOcean() {
const nodeUri = `${nodeScheme}://${nodeHost}:${nodePort}`

View File

@ -10,6 +10,13 @@ interface AssetDetailsProps {
}
export default class AssetDetails extends PureComponent<AssetDetailsProps> {
private datafilesLine = (files: any) => {
if (files.length === 1) {
return <span>{files.length} data file</span>
}
return <span>{files.length} data files</span>
}
public render() {
const { metadata, ddo } = this.props
const { base } = metadata
@ -43,9 +50,7 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
</Link>
)}
{base.files && (
<span>{base.files.length} data files</span>
)}
{base.files && this.datafilesLine(base.files)}
</div>
</aside>
@ -79,9 +84,9 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
ddo={ddo}
/>
<pre>
{/* <pre>
<code>{JSON.stringify(metadata, null, 2)}</code>
</pre>
</pre> */}
</>
)
}

View File

@ -0,0 +1,60 @@
@import '../../styles/variables';
.buttonMain {
margin: auto;
margin-bottom: $spacer / 2;
display: block;
}
.error {
text-align: center;
color: $red;
font-size: $font-size-small;
}
.fileWrap {
margin-left: $spacer / 4;
margin-right: $spacer / 4;
margin-bottom: $spacer * $line-height;
flex: 1 1 100%;
@media (min-width: $break-point--small) {
flex-basis: calc(100% / 3 - #{$spacer} / 2);
}
}
.file {
display: inline-block;
background: $brand-grey;
padding: $spacer $spacer / 2;
margin-bottom: $spacer / 2;
text-align: left;
position: relative;
min-height: 100px;
&:before {
content: '';
position: absolute;
right: 0;
top: 0;
width: 0;
height: 0;
border-top: 1rem solid $body-background;
border-left: 1rem solid transparent;
}
li {
font-size: $font-size-small;
font-weight: $font-weight-bold;
color: $brand-white;
&:before {
display: none;
}
}
// move spinner a bit up
+ div {
margin-top: $spacer / 2;
}
}

View File

@ -0,0 +1,99 @@
import React, { PureComponent } from 'react'
import { Logger } from '@oceanprotocol/squid'
import filesize from 'filesize'
import { User } from '../../context/User'
import Button from '../../components/atoms/Button'
import Spinner from '../../components/atoms/Spinner'
import styles from './AssetFile.module.scss'
interface AssetFileProps {
file: any
ddo: any
}
interface AssetFileState {
isLoading: boolean
error: string
message: string
}
export default class AssetFile extends PureComponent<
AssetFileProps,
AssetFileState
> {
public state = {
isLoading: false,
error: '',
message: 'Decrypting file, please sign...'
}
private resetState = () => this.setState({ isLoading: true, error: '' })
private purchaseAsset = async (ddo: any, index: number) => {
this.resetState()
const { ocean } = this.context
const accounts = await ocean.accounts.list()
try {
const service = ddo.findServiceByType('Access')
const agreementId = await ocean.assets.order(
ddo.id,
service.serviceDefinitionId,
accounts[0]
)
const path = await ocean.assets.consume(
agreementId,
ddo.id,
service.serviceDefinitionId,
accounts[0],
'',
index
)
Logger.log('path', path)
this.setState({ isLoading: false })
} catch (error) {
Logger.log('error', error)
this.setState({ isLoading: false, error: error.message })
}
}
public render() {
const { ddo, file } = this.props
return (
<div className={styles.fileWrap}>
<ul key={file.index} className={styles.file}>
<li>
{file.contentType && file.contentType.split('/')[1]}
</li>
<li>
{file.contentLength && filesize(file.contentLength)}
</li>
{/* <li>{file.encoding}</li> */}
{/* <li>{file.compression}</li> */}
</ul>
{this.state.isLoading ? (
<Spinner message={this.state.message} />
) : (
<Button
primary
className={styles.buttonMain}
onClick={() => this.purchaseAsset(ddo, file.index)}
>
Get file
</Button>
)}
{this.state.error !== '' && (
<div className={styles.error}>{this.state.error}</div>
)}
</div>
)
}
}
AssetFile.contextType = User

View File

@ -1,36 +1,8 @@
@import '../../styles/variables';
.buttonMain {
margin: auto;
margin-bottom: $spacer / 2;
display: block;
}
.error {
text-align: center;
color: $red;
font-size: $font-size-small;
}
.files {
text-align: center;
}
.file {
display: inline-block;
border: .1rem solid $brand-grey-light;
padding: $spacer $spacer / 2;
text-align: left;
margin-left: $spacer / 4;
margin-right: $spacer / 4;
li {
font-size: $font-size-small;
font-weight: $font-weight-bold;
color: $brand-grey-light;
&:before {
display: none;
}
}
display: flex;
flex-wrap: wrap;
justify-content: center;
}

View File

@ -1,51 +1,11 @@
import React, { PureComponent } from 'react'
import { Logger } from '@oceanprotocol/squid'
import filesize from 'filesize'
import { User } from '../../context/User'
import Button from '../../components/atoms/Button'
import Spinner from '../../components/atoms/Spinner'
import AssetFile from './AssetFile'
import styles from './AssetFilesDetails.module.scss'
interface AssetFilesDetailsProps {
export default class AssetFilesDetails extends PureComponent<{
files: any[]
ddo: any
}
export default class AssetFilesDetails extends PureComponent<
AssetFilesDetailsProps
> {
public state = { isLoading: false, error: null }
private purchaseAsset = async (ddo: any) => {
this.setState({ isLoading: true, error: null })
const { ocean } = this.context
const accounts = await ocean.accounts.list()
try {
const service = ddo.findServiceByType('Access')
const agreementId = await ocean.assets.order(
ddo.id,
service.serviceDefinitionId,
accounts[0]
)
const path = await ocean.assets.consume(
agreementId,
ddo.id,
service.serviceDefinitionId,
accounts[0],
''
)
Logger.log('path', path)
this.setState({ isLoading: false })
} catch (error) {
Logger.log('error', error)
this.setState({ isLoading: false, error: error.message })
}
}
}> {
public render() {
const { files, ddo } = this.props
@ -53,41 +13,12 @@ export default class AssetFilesDetails extends PureComponent<
<>
<div className={styles.files}>
{files.map(file => (
<ul key={file.index} className={styles.file}>
<li>
{file.contentType &&
file.contentType.split('/')[1]}
</li>
<li>
{file.contentLength &&
filesize(file.contentLength)}
</li>
{/* <li>{file.encoding}</li> */}
{/* <li>{file.compression}</li> */}
</ul>
<AssetFile key={file.index} ddo={ddo} file={file} />
))}
</div>
{this.state.isLoading ? (
<Spinner message="Decrypting files, please sign with your wallet..." />
) : (
<Button
primary
className={styles.buttonMain}
onClick={() => this.purchaseAsset(ddo)}
>
Get asset files
</Button>
)}
{this.state.error && (
<div className={styles.error}>{this.state.error}</div>
)}
</>
) : (
<div>No files attached.</div>
)
}
}
AssetFilesDetails.contextType = User

View File

@ -6,7 +6,7 @@ import ItemForm from './ItemForm'
import Item from './Item'
import styles from './index.module.scss'
import { serviceHost, servicePort, serviceScheme } from '../../../config'
import { serviceHost, servicePort, serviceScheme } from '../../../config/config'
interface File {
url: string

View File

@ -3,3 +3,35 @@
.message {
margin-bottom: $spacer;
}
.success {
composes: message;
background: $green;
padding: $spacer / 1.5;
border-radius: $border-radius;
color: $brand-white;
font-weight: $font-weight-bold;
text-align: center;
&,
a,
button {
color: $brand-white;
}
a,
button {
transition: color .2s ease-out;
&:hover,
&:focus {
color: $brand-pink;
transform: none;
}
}
a {
display: inline-block;
margin-right: $spacer;
}
}

View File

@ -1,6 +1,7 @@
import React, { PureComponent } from 'react'
import Web3message from '../../components/organisms/Web3message'
import Spinner from '../../components/atoms/Spinner'
import Button from '../../components/atoms/Button'
import styles from './StepRegisterContent.module.scss'
interface StepRegisterContentProps {
@ -26,11 +27,14 @@ export default class StepRegisterContent extends PureComponent<
)
public publishedState = () => (
<div className={styles.message}>
Your asset is published! See it{' '}
<a href={'/asset/' + this.props.state.publishedDid}>here</a>, submit
another asset by clicking{' '}
<a onClick={() => this.props.toStart()}>here</a>
<div className={styles.success}>
<p>Your asset is published!</p>
<Button link to={'/asset/' + this.props.state.publishedDid}>
See published asset
</Button>
<Button link onClick={() => this.props.toStart()}>
Publish another asset
</Button>
</div>
)
@ -38,7 +42,6 @@ export default class StepRegisterContent extends PureComponent<
return (
<>
<Web3message />
{this.props.state.isPublishing ? (
this.publishingState()
) : this.props.state.publishingError ? (

View File

@ -18,6 +18,8 @@ $yellow: #eac146;
$brand-gradient: linear-gradient(to right bottom, $brand-purple, $brand-pink);
$body-background: darken($brand-white, 1%);
// Fonts
$font-family-base: 'Sharp Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Helvetica, Arial, sans-serif;

View File

@ -29,7 +29,7 @@ body {
font-family: $font-family-base;
font-weight: $font-weight-base;
line-height: $line-height;
background: darken($brand-white, 1%);
background: $body-background;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

View File

@ -15,5 +15,5 @@
"noEmit": true,
"jsx": "preserve"
},
"include": ["src"]
"include": ["src", "config/config.ts"]
}

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
version: '3.4'
services:
client:
build:
context: ./client
dockerfile: ./Dockerfile
ports:
- 3000:3000
depends_on:
- server
environment:
- SERVICE_SCHEME=http
- SERVICE_HOST=localhost
- SERVICE_PORT=4000
server:
build:
context: ./server
dockerfile: ./Dockerfile
ports:
- 4000:4000
command: npm run start

10
server/Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM node:11-alpine
LABEL maintainer="Ocean Protocol <devops@oceanprotocol.com>"
WORKDIR /app/backend/
COPY . .
RUN npm install
RUN npm run build