forked from cerc-io/laconic-console
Use published packages and remove console-server (#8)
* Use published packages in lirewine * Remove console-server package * Use dxos packages until lirewine packages are published * Use published cerc-io/laconic-sdk package * Add readme to run console app with laconicd
This commit is contained in:
parent
78be111ee4
commit
780cd29807
2
.npmrc
Normal file
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@cerc-io:registry=https://git.vdb.to/api/packages/cerc-io/npm/
|
||||||
|
@lirewine:registry=https://git.vdb.to/api/packages/cerc-io/npm/
|
157
README.md
157
README.md
@ -5,3 +5,160 @@ Laconic Kubenet GraphQL server and console application.
|
|||||||
User interface for submitting and reading records registered on Laconic.
|
User interface for submitting and reading records registered on Laconic.
|
||||||
|
|
||||||
![Console](./docs/images/console.png)
|
![Console](./docs/images/console.png)
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
* Clone the required repos:
|
||||||
|
* [laconicd](https://github.com/cerc-io/laconicd)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@github.com:cerc-io/laconicd.git
|
||||||
|
```
|
||||||
|
|
||||||
|
* [debug](https://github.com/lirewine/debug)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@github.com:lirewine/debug.git
|
||||||
|
```
|
||||||
|
|
||||||
|
* [gem](https://github.com/lirewine/gem)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@github.com:lirewine/gem.git
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the `laconicd` chain:
|
||||||
|
* In [laconicd](https://github.com/cerc-io/laconicd) repo, checkout to a different branch (see [issues](#issues) below)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout console
|
||||||
|
```
|
||||||
|
|
||||||
|
* Start the chain
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./init.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the laconic-console app
|
||||||
|
* In [laconic-console](https://github.com/cerc-io/laconic-console) repo, install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
* Register the dependencies to link
|
||||||
|
* `lirewine/debug`
|
||||||
|
* In [debug](https://github.com/lirewine/debug) repo, checkout to release version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout v1.0.0-beta.78
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install and build packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies and build packages
|
||||||
|
yarn && yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run `yarn link` in repo root
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn link
|
||||||
|
```
|
||||||
|
|
||||||
|
* `lirewine/gem-core`
|
||||||
|
* In [gem](https://github.com/lirewine/gem) repo, checkout to release version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout v1.0.0-beta.26
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install and build packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies and build packages
|
||||||
|
yarn && yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change directory to core package and run `yarn link`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd packages/core
|
||||||
|
|
||||||
|
# Register package to link
|
||||||
|
yarn link
|
||||||
|
```
|
||||||
|
|
||||||
|
* Link the packages in `laconic-console`
|
||||||
|
* In root of [repo](https://github.com/cerc-io/laconic-console), run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn link "@lirewine/debug"
|
||||||
|
yarn link "@lirewine/gem-core"
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change directory to [packages/console-app](https://github.com/cerc-io/laconic-console/tree/main/packages/console-app) and start the react app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Change directory
|
||||||
|
cd packages/console-app/
|
||||||
|
|
||||||
|
# Start app
|
||||||
|
CONFIG_FILE=config-local.yml yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
* Open console-app at <http://localhost:8080>
|
||||||
|
|
||||||
|
* To view records in the app, test suite in laconic-sdk can be run
|
||||||
|
|
||||||
|
* Clone the [laconic-sdk](https://github.com/cerc-io/laconic-sdk) repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@github.com:cerc-io/laconic-sdk.git
|
||||||
|
```
|
||||||
|
|
||||||
|
* In [laconic-sdk](https://github.com/cerc-io/laconic-sdk) repo, copy [.env.example](https://github.com/cerc-io/laconic-sdk/blob/main/.env.example) file and create a `.env` file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
* Export the private key using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laconicd keys export mykey --unarmored-hex --unsafe
|
||||||
|
```
|
||||||
|
|
||||||
|
* Copy the private key exported above and assign it to variable `PRIVATE_KEY` in the `.env` file.
|
||||||
|
|
||||||
|
* Also change value of `COSMOS_CHAIN_ID` in `.env` file
|
||||||
|
|
||||||
|
```env
|
||||||
|
COSMOS_CHAIN_ID=ethermint_9000-1
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the tests in laconic-sdk repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
*NOTE*: One test from [util.test.ts](https://github.com/cerc-io/laconic-sdk/blob/main/src/util.test.ts) fails as mentioned in the [PR](https://github.com/cerc-io/laconic-sdk/pull/5#issuecomment-1299572012)
|
||||||
|
|
||||||
|
* Open console-app at <http://localhost:8080> to view the records.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
* [Changes](https://github.com/cerc-io/laconicd/compare/main...console) in laconicd (exists in [console branch](https://github.com/cerc-io/laconicd/tree/console)) for running with console app can be pushed once the following PRs (for [fixing laconic-sdk test suite](https://github.com/cerc-io/laconic-sdk/issues/8)) are merged:
|
||||||
|
* <https://github.com/cerc-io/laconic-sdk/pull/15>
|
||||||
|
* <https://github.com/cerc-io/laconicd/pull/70>
|
||||||
|
|
||||||
|
* Once `lirewine/debug` and `lirewine/gem` packages are published to gitea (like [lirewine/react-ux](https://git.vdb.to/cerc-io/-/packages/npm/@lirewine%2Freact-ux/1.1.0-beta.0) package), yarn linking can be removed from development setup.
|
||||||
|
@ -29,9 +29,10 @@
|
|||||||
"@apollo/react-components": "^3.1.5",
|
"@apollo/react-components": "^3.1.5",
|
||||||
"@apollo/react-hooks": "^3.1.5",
|
"@apollo/react-hooks": "^3.1.5",
|
||||||
"@babel/runtime": "^7.8.7",
|
"@babel/runtime": "^7.8.7",
|
||||||
|
"@cerc-io/laconic-sdk": "0.1.4",
|
||||||
"@dxos/debug": "^1.0.0-beta.78",
|
"@dxos/debug": "^1.0.0-beta.78",
|
||||||
"@dxos/gem-core": "^1.0.0-beta.25",
|
"@dxos/gem-core": "^1.0.0-beta.25",
|
||||||
"@dxos/react-ux": "https://github.com/dxos-deprecated/sdk.git#v1.1.0-beta.0",
|
"@lirewine/react-ux": "1.1.0-beta.0",
|
||||||
"@material-ui/core": "^4.10.0",
|
"@material-ui/core": "^4.10.0",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.54",
|
"@material-ui/lab": "^4.0.0-alpha.54",
|
||||||
@ -87,6 +88,7 @@
|
|||||||
"eslint-plugin-jsdoc": "^21.0.0",
|
"eslint-plugin-jsdoc": "^21.0.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
|
"graphql": "^15.0.0",
|
||||||
"html-webpack-plugin": "^4.3.0",
|
"html-webpack-plugin": "^4.3.0",
|
||||||
"jest": "^24.8.0",
|
"jest": "^24.8.0",
|
||||||
"react-scripts": "^3.4.1",
|
"react-scripts": "^3.4.1",
|
||||||
|
@ -9,10 +9,10 @@ import Link from '@material-ui/core/Link';
|
|||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import blueGrey from '@material-ui/core/colors/blueGrey';
|
import blueGrey from '@material-ui/core/colors/blueGrey';
|
||||||
import GraphQLIcon from '@material-ui/icons/Adb';
|
// import GraphQLIcon from '@material-ui/icons/Adb';
|
||||||
|
|
||||||
// import LaconicIcon from '../icons/Laconic';
|
// import LaconicIcon from '../icons/Laconic';
|
||||||
import { graphqlApi } from '../client';
|
// import { graphqlApi } from '../client';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
offset: theme.mixins.denseToolbar,
|
offset: theme.mixins.denseToolbar,
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from '@material-ui/core';
|
||||||
|
|
||||||
import { JsonTreeView } from '@dxos/react-ux';
|
import { JsonTreeView } from '@lirewine/react-ux';
|
||||||
|
|
||||||
import { omitDeep } from '../util/omit';
|
import { omitDeep } from '../util/omit';
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from '@material-ui/core';
|
||||||
|
|
||||||
import { FullScreen } from '@dxos/gem-core';
|
import { FullScreen } from '@lirewine/gem-core';
|
||||||
|
|
||||||
import { ConsoleContext } from '../hooks';
|
import { ConsoleContext } from '../hooks';
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import { ApolloProvider } from '@apollo/react-hooks';
|
|||||||
import { ThemeProvider } from '@material-ui/core/styles';
|
import { ThemeProvider } from '@material-ui/core/styles';
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||||
|
|
||||||
import { ErrorHandler } from '@dxos/debug';
|
import { ErrorHandler } from '@lirewine/debug';
|
||||||
|
|
||||||
import { build } from '../version.json';
|
import { build } from '../version.json';
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Copyright 2020 DXOS.org
|
// Copyright 2020 DXOS.org
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Registry } from 'laconic-sdk';
|
import { Registry } from '@cerc-io/laconic-sdk';
|
||||||
|
|
||||||
import { getServiceUrl } from '../util/config';
|
import { getServiceUrl } from '../util/config';
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
|
||||||
import { Registry } from 'laconic-sdk';
|
import { Registry } from '@cerc-io/laconic-sdk';
|
||||||
|
|
||||||
import { getServiceUrl } from './util/config';
|
import { getServiceUrl } from './util/config';
|
||||||
|
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
# Console
|
|
||||||
|
|
||||||
Apollo GraphQL client.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Use the following command to run the server at: http://localhost:9004
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn start
|
|
||||||
```
|
|
||||||
|
|
||||||
To test the Console app, the `@cerc-io/console-app` must be built first:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../console-app
|
|
||||||
yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the following command to run the client in development mode (via the `webpack-dev-server`) at http://localhost:8080
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn dev
|
|
||||||
```
|
|
||||||
|
|
||||||
To build the client and serve it directly from the server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn build
|
|
||||||
yarn start
|
|
||||||
```
|
|
||||||
|
|
||||||
Then open the app at: http://localhost:9004/console
|
|
||||||
|
|
||||||
|
|
||||||
## Production
|
|
||||||
|
|
||||||
When running the Console server, either set the `CONFIG_FILE` environment variable or set the `--config` option.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./bin/console.js --config ./config.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
NOTE: The server passes its configuration directly to the Console app when it is loaded.
|
|
@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
'@babel/preset-env',
|
|
||||||
'@babel/preset-react'
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
'babel-plugin-inline-import', {
|
|
||||||
extensions: [
|
|
||||||
'.mustache',
|
|
||||||
'.graphql'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// Allows export of components importing GQL files (without webpack).
|
|
||||||
'import-graphql',
|
|
||||||
'inline-json-import',
|
|
||||||
|
|
||||||
'@babel/plugin-proposal-class-properties',
|
|
||||||
'@babel/plugin-proposal-export-default-from'
|
|
||||||
]
|
|
||||||
};
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
module.exports = require('../dist/es/server/main.js');
|
|
@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# NODE_ENV=development
|
|
||||||
# NOTE: Set CONFIG_FILE to swap out this config file.
|
|
||||||
#
|
|
||||||
|
|
||||||
app:
|
|
||||||
title: 'Console'
|
|
||||||
org': 'Laconic'
|
|
||||||
theme: 'dark'
|
|
||||||
website: 'https://laconic.com'
|
|
||||||
publicUrl: '/console'
|
|
||||||
|
|
||||||
api:
|
|
||||||
path: '/api'
|
|
||||||
port: 9004
|
|
||||||
intervalLog: 5000
|
|
||||||
pollInterval: 10000
|
|
||||||
|
|
||||||
system:
|
|
||||||
debug: 'laconic:console:*'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
prefix: '/app'
|
|
||||||
server: 'https://kube.local'
|
|
||||||
|
|
||||||
wns:
|
|
||||||
server: 'https://kube.local/laconic/wns/api'
|
|
||||||
webui: 'https://kube.local/laconic/wns/console'
|
|
||||||
|
|
||||||
signal:
|
|
||||||
server: 'wss://kube.local/laconic/signal/api'
|
|
||||||
api: 'https://kube.local/laconic/signal'
|
|
||||||
|
|
||||||
ipfs:
|
|
||||||
server: 'https://kube.local/laconic/ipfs/api'
|
|
||||||
gateway: 'https://kube.local/laconic/ipfs/gateway'
|
|
||||||
|
|
||||||
wellknown:
|
|
||||||
endpoint: 'https://kube.local/.well-known/laconic'
|
|
@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# NODE_ENV=development
|
|
||||||
# NOTE: Set CONFIG_FILE to swap out this config file.
|
|
||||||
#
|
|
||||||
|
|
||||||
app:
|
|
||||||
title: 'Console'
|
|
||||||
org': 'Laconic'
|
|
||||||
theme: 'dark'
|
|
||||||
website: 'https://laconic.com'
|
|
||||||
publicUrl: '/console'
|
|
||||||
|
|
||||||
api:
|
|
||||||
path: '/api'
|
|
||||||
port: 9004
|
|
||||||
intervalLog: 5000
|
|
||||||
pollInterval: 10000
|
|
||||||
|
|
||||||
system:
|
|
||||||
debug: 'laconic:console:*'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
prefix: '/app'
|
|
||||||
server: 'https://apollo1.kube.moon.laconic.network'
|
|
||||||
|
|
||||||
wns:
|
|
||||||
server: 'https://apollo1.kube.moon.laconic.network/laconic/wns/api'
|
|
||||||
webui: 'https://apollo1.kube.moon.laconic.network/laconic/wns/console'
|
|
||||||
|
|
||||||
signal:
|
|
||||||
server: 'wss://apollo1.kube.moon.laconic.network/laconic/signal'
|
|
||||||
api: 'https://apollo1.kube.moon.laconic.network/laconic/signal/api'
|
|
||||||
|
|
||||||
ipfs:
|
|
||||||
server: 'https://apollo1.kube.moon.laconic.network/laconic/ipfs/api'
|
|
||||||
gateway: 'https://apollo1.kube.moon.laconic.network/laconic/ipfs/gateway'
|
|
||||||
|
|
||||||
wellknown:
|
|
||||||
endpoint: 'https://apollo1.kube.moon.laconic.network/.well-known/laconic'
|
|
@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# NODE_ENV=production
|
|
||||||
# NOTE: Set CONFIG_FILE to swap out this config file.
|
|
||||||
#
|
|
||||||
|
|
||||||
app:
|
|
||||||
title: 'Console'
|
|
||||||
org': 'Laconic'
|
|
||||||
theme: 'dark'
|
|
||||||
website: 'https://laconic.com'
|
|
||||||
publicUrl: '/console'
|
|
||||||
|
|
||||||
api:
|
|
||||||
path: '/api'
|
|
||||||
port: 9004
|
|
||||||
intervalLog: 5000
|
|
||||||
pollInterval: 10000
|
|
||||||
|
|
||||||
system:
|
|
||||||
debug: 'laconic:console:*'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
prefix: '/app'
|
|
||||||
server: 'http://127.0.0.1:5999'
|
|
||||||
|
|
||||||
wns:
|
|
||||||
server: 'http://127.0.0.1:9473/api'
|
|
||||||
webui: 'http://127.0.0.1:9473/console'
|
|
||||||
|
|
||||||
signal:
|
|
||||||
server: 'ws://127.0.0.1:4000'
|
|
||||||
api: 'http://127.0.0.1:4000/api'
|
|
||||||
|
|
||||||
ipfs:
|
|
||||||
server: 'http://127.0.0.1:5001'
|
|
||||||
gateway: 'http://127.0.0.1:8888/ipfs/'
|
|
||||||
|
|
||||||
wellknown:
|
|
||||||
endpoint: 'http://127.0.0.1:9000/.well-known/laconic'
|
|
@ -1,114 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cerc-io/console-server",
|
|
||||||
"version": "1.2.9",
|
|
||||||
"description": "Kubenet Console Server",
|
|
||||||
"main": "dist/es/index.js",
|
|
||||||
"bin": {
|
|
||||||
"laconic-console": "bin/console.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"bin/",
|
|
||||||
"dist/es"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"build": "npm run clean && npm run build:client && npm run build:server",
|
|
||||||
"build:client": "PUBLIC_URL=/console webpack --mode development",
|
|
||||||
"build:server": "babel ./src --out-dir ./dist/es --ignore \"**/*.test.js\" --copy-files",
|
|
||||||
"clean": "rm -rf ./dist",
|
|
||||||
"dev": "VERBOSE=true webpack-dev-server --mode development --watch",
|
|
||||||
"lint": "semistandard 'src/**/*.js'",
|
|
||||||
"test": "jest --rootDir ./src --passWithNoTests --no-cache",
|
|
||||||
"start": "CONFIG_FILE=${CONFIG_FILE=./config.yml} BABEL_DISABLE_CACHE=1 nodemon --exec babel-node src/server/main.js -- --verbose"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"browserslist": [
|
|
||||||
"> 5%"
|
|
||||||
],
|
|
||||||
"jest": {
|
|
||||||
"testEnvironment": "node"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/polyfill": "^7.8.7",
|
|
||||||
"@babel/runtime": "^7.8.7",
|
|
||||||
"@cerc-io/console-app": "^1.2.9",
|
|
||||||
"apollo-boost": "^0.4.9",
|
|
||||||
"apollo-server-express": "^2.13.1",
|
|
||||||
"body-parser": "^1.19.0",
|
|
||||||
"compression": "^1.7.4",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"express-graphql": "^0.9.0",
|
|
||||||
"graphql": "^15.0.0",
|
|
||||||
"graphql-tag": "^2.10.3",
|
|
||||||
"graphql-tools": "^6.0.3",
|
|
||||||
"ipfs-http-client": "^44.1.0",
|
|
||||||
"js-yaml": "^3.14.0",
|
|
||||||
"lodash.defaultsdeep": "^4.6.1",
|
|
||||||
"lodash.pick": "^4.4.0",
|
|
||||||
"mustache-express": "^1.3.0",
|
|
||||||
"react": "^16.13.1",
|
|
||||||
"react-dom": "^16.13.1",
|
|
||||||
"source-map-support": "^0.5.12",
|
|
||||||
"systeminformation": "^4.26.5",
|
|
||||||
"tree-kill": "^1.2.2",
|
|
||||||
"yargs": "^15.3.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/cli": "7.4.4",
|
|
||||||
"@babel/core": "^7.4.5",
|
|
||||||
"@babel/node": "^7.8.7",
|
|
||||||
"@babel/plugin-proposal-class-properties": "^7.5.5",
|
|
||||||
"@babel/plugin-proposal-export-default-from": "^7.5.2",
|
|
||||||
"@babel/preset-env": "^7.4.5",
|
|
||||||
"babel-eslint": "^10.0.3",
|
|
||||||
"babel-jest": "^24.8.0",
|
|
||||||
"babel-loader": "^8.1.0",
|
|
||||||
"babel-plugin-add-module-exports": "^1.0.2",
|
|
||||||
"babel-plugin-import-graphql": "^2.7.0",
|
|
||||||
"babel-plugin-inline-import": "^3.0.0",
|
|
||||||
"babel-plugin-inline-json-import": "^0.3.2",
|
|
||||||
"dotenv-webpack": "^1.8.0",
|
|
||||||
"eslint": "^6.7.2",
|
|
||||||
"eslint-config-semistandard": "^15.0.0",
|
|
||||||
"eslint-config-standard": "^14.1.1",
|
|
||||||
"eslint-loader": "^3.0.3",
|
|
||||||
"eslint-plugin-babel": "^5.3.0",
|
|
||||||
"eslint-plugin-import": "^2.18.2",
|
|
||||||
"eslint-plugin-jest": "^23.13.1",
|
|
||||||
"eslint-plugin-jsdoc": "^21.0.0",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
|
||||||
"jest": "^24.8.0",
|
|
||||||
"nodemon": "^2.0.4",
|
|
||||||
"semistandard": "^14.2.0",
|
|
||||||
"webpack": "^4.41.2",
|
|
||||||
"webpack-cli": "^3.3.11",
|
|
||||||
"webpack-dev-server": "^3.11.0",
|
|
||||||
"webpack-merge": "^4.2.2"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
"extends": [
|
|
||||||
"plugin:jest/recommended",
|
|
||||||
"semistandard"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"babel"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"babel/semi": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"semistandard": {
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
"env": [
|
|
||||||
"jest",
|
|
||||||
"node",
|
|
||||||
"browser"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title><%= title %></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import debug from 'debug';
|
|
||||||
import React from 'react';
|
|
||||||
import { render } from 'react-dom';
|
|
||||||
|
|
||||||
import { Main } from '@cerc-io/console-app';
|
|
||||||
|
|
||||||
// Load from global printed into HTML page via template.
|
|
||||||
const { config } = window.__Laconic__;
|
|
||||||
|
|
||||||
debug.enable(config.system.debug);
|
|
||||||
|
|
||||||
render(<Main config={config} />, document.getElementById('root'));
|
|
@ -1,35 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright 2020 DXOS.org
|
|
||||||
#
|
|
||||||
|
|
||||||
# TODO(burdon): Replace generic results with schema.
|
|
||||||
type JSONResult {
|
|
||||||
timestamp: String!
|
|
||||||
json: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Schema
|
|
||||||
#
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
logs(service: String!, incremental: Boolean): JSONResult!
|
|
||||||
app_status: JSONResult!
|
|
||||||
ipfs_status: JSONResult!
|
|
||||||
ipfs_swarm_status: JSONResult!
|
|
||||||
service_status: JSONResult!
|
|
||||||
signal_status: JSONResult!
|
|
||||||
system_status: JSONResult!
|
|
||||||
wns_status: JSONResult!
|
|
||||||
bot_list: JSONResult!
|
|
||||||
extensions: JSONResult!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
bot_kill(botId: String!): JSONResult!
|
|
||||||
}
|
|
||||||
|
|
||||||
schema {
|
|
||||||
query: Query
|
|
||||||
mutation: Mutation
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
import debug from 'debug';
|
|
||||||
import fs from 'fs';
|
|
||||||
import yaml from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
import os from 'os';
|
|
||||||
import kill from 'tree-kill';
|
|
||||||
|
|
||||||
const DEFAULT_BOT_FACTORY_CWD = '.wire/bots';
|
|
||||||
const SERVICE_CONFIG_FILENAME = 'service.yml';
|
|
||||||
|
|
||||||
const log = debug('laconic:console:server:resolvers');
|
|
||||||
|
|
||||||
let topic;
|
|
||||||
const getBotFactoryTopic = (botFactoryCwd) => {
|
|
||||||
if (!topic) {
|
|
||||||
// TODO(egorgripasov): Get topic from config or registry.
|
|
||||||
const serviceFilePath = path.join(os.homedir(), botFactoryCwd || DEFAULT_BOT_FACTORY_CWD, SERVICE_CONFIG_FILENAME);
|
|
||||||
if (fs.existsSync(serviceFilePath)) {
|
|
||||||
const botFactoryInfo = yaml.safeLoad(fs.readFileSync(serviceFilePath));
|
|
||||||
topic = botFactoryInfo.topic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return topic;
|
|
||||||
};
|
|
||||||
|
|
||||||
const executeCommand = async (command, args, timeout = 10000) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const child = spawn(command, args, { encoding: 'utf8' });
|
|
||||||
|
|
||||||
const stdout = [];
|
|
||||||
const stderr = [];
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
try {
|
|
||||||
kill(child.pid, 'SIGKILL');
|
|
||||||
} catch (err) {
|
|
||||||
log(`Can not kill ${command} process: ${err}`);
|
|
||||||
}
|
|
||||||
stderr.push('Timeout.');
|
|
||||||
}, timeout);
|
|
||||||
|
|
||||||
child.stdout.on('data', (data) => stdout.push(data));
|
|
||||||
|
|
||||||
child.stderr.on('data', (data) => stderr.push(data));
|
|
||||||
|
|
||||||
child.on('exit', (code) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve({
|
|
||||||
code: code === null ? 1 : code,
|
|
||||||
stdout: stdout.join('').trim(),
|
|
||||||
stderr: stderr.join('').trim()
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRunningBots = async () => {
|
|
||||||
const command = 'wire';
|
|
||||||
const args = ['bot', 'factory', 'status', '--topic', getBotFactoryTopic()];
|
|
||||||
|
|
||||||
const { code, stdout, stderr } = await executeCommand(command, args);
|
|
||||||
return {
|
|
||||||
success: !code,
|
|
||||||
bots: code ? [] : JSON.parse(stdout).bots || [],
|
|
||||||
error: (stderr || code) ? stderr || stdout : undefined
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const sendBotCommand = async (botId, botCommand) => {
|
|
||||||
const command = 'wire';
|
|
||||||
const args = ['bot', botCommand, '--topic', getBotFactoryTopic(), '--bot-id', botId];
|
|
||||||
|
|
||||||
const { code, stdout, stderr } = await executeCommand(command, args);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: !code,
|
|
||||||
botId: code ? undefined : botId,
|
|
||||||
error: (stderr || code) ? stderr || stdout : undefined
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const botsResolvers = {
|
|
||||||
Query: {
|
|
||||||
bot_list: async () => {
|
|
||||||
const result = await getRunningBots();
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify(result)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mutation: {
|
|
||||||
bot_kill: async (_, { botId }) => {
|
|
||||||
const result = await sendBotCommand(botId, 'kill');
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify(result)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import childProcess from 'child_process';
|
|
||||||
|
|
||||||
// TODO(telackey): Make pluggable.
|
|
||||||
|
|
||||||
const ifBigDipper = () => {
|
|
||||||
try {
|
|
||||||
const result = childProcess.execSync('docker ps -f "ancestor=big-dipper_app" -q');
|
|
||||||
if (result && result.toString()) {
|
|
||||||
return {
|
|
||||||
title: 'Block Explorer',
|
|
||||||
url: 'http://%HOST%:3080/'
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
title: 'Block Explorer',
|
|
||||||
url: 'http://blockexplorer.moon.laconic.network:3080/'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ifRadicle = () => {
|
|
||||||
try {
|
|
||||||
const result = childProcess.execSync('docker ps -f "ancestor=laconic/radicle-seed-node" -q');
|
|
||||||
if (result && result.toString()) {
|
|
||||||
return {
|
|
||||||
title: 'Radicle',
|
|
||||||
url: '/radicle/'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO(telackey): Use the local Sentry.
|
|
||||||
const ifSentry = () => {
|
|
||||||
return {
|
|
||||||
title: 'Sentry',
|
|
||||||
url: 'http://sentry.kube.laconic.network:9000/'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const extensionResolvers = {
|
|
||||||
Query: {
|
|
||||||
extensions: async (_, __, { config }) => {
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify([
|
|
||||||
ifBigDipper(),
|
|
||||||
ifRadicle(),
|
|
||||||
ifSentry()
|
|
||||||
].filter(x => x))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import debug from 'debug';
|
|
||||||
import defaultsDeep from 'lodash.defaultsdeep';
|
|
||||||
|
|
||||||
import { extensionResolvers } from './extensions';
|
|
||||||
import { ipfsResolvers } from './ipfs';
|
|
||||||
import { systemResolvers } from './system';
|
|
||||||
import { logResolvers } from './log';
|
|
||||||
import { botsResolvers } from './bots';
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const log = debug('laconic:console:server:resolvers');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolvers
|
|
||||||
* https://www.apollographql.com/docs/graphql-tools/resolvers
|
|
||||||
*/
|
|
||||||
export const resolvers = defaultsDeep({
|
|
||||||
|
|
||||||
// TODO(burdon): Auth.
|
|
||||||
// https://www.apollographql.com/docs/apollo-server/data/errors/#codes
|
|
||||||
|
|
||||||
}, ipfsResolvers, systemResolvers, logResolvers, botsResolvers, extensionResolvers);
|
|
@ -1,58 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import debug from 'debug';
|
|
||||||
import IpfsHttpClient from 'ipfs-http-client';
|
|
||||||
const log = debug('laconic:console:server:resolvers');
|
|
||||||
|
|
||||||
export const ipfsResolvers = {
|
|
||||||
Query: {
|
|
||||||
//
|
|
||||||
// IPFS
|
|
||||||
// TODO(burdon): Call from client?
|
|
||||||
// https://github.com/ipfs/js-ipfs
|
|
||||||
// https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs-http-client#api
|
|
||||||
//
|
|
||||||
|
|
||||||
ipfs_status: async (_, __, { config }) => {
|
|
||||||
log('Calling IPFS...');
|
|
||||||
|
|
||||||
// NOTE: Hangs if server not running.
|
|
||||||
const ipfs = new IpfsHttpClient(config.services.ipfs.server);
|
|
||||||
|
|
||||||
const id = await ipfs.id();
|
|
||||||
const version = await ipfs.version();
|
|
||||||
const peers = await ipfs.swarm.peers();
|
|
||||||
const stats = await ipfs.stats.repo();
|
|
||||||
// Do not expose the repo path.
|
|
||||||
delete stats.repoPath;
|
|
||||||
|
|
||||||
const refs = [];
|
|
||||||
for await (const ref of ipfs.refs.local()) {
|
|
||||||
if (ref.err) {
|
|
||||||
log(ref.err);
|
|
||||||
} else {
|
|
||||||
refs.push(ref.ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify({
|
|
||||||
id,
|
|
||||||
version,
|
|
||||||
repo: {
|
|
||||||
stats
|
|
||||||
},
|
|
||||||
refs: {
|
|
||||||
local: refs
|
|
||||||
},
|
|
||||||
swarm: {
|
|
||||||
peers
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import { spawnSync } from 'child_process';
|
|
||||||
|
|
||||||
class LogCache {
|
|
||||||
constructor (maxLines = 500) {
|
|
||||||
// Sets in JS iterate in insertion order.
|
|
||||||
this.buffer = new Set();
|
|
||||||
this.maxLines = maxLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
append (lines) {
|
|
||||||
const added = [];
|
|
||||||
for (const line of lines) {
|
|
||||||
if (!this.buffer.has(line)) {
|
|
||||||
this.buffer.add(line);
|
|
||||||
added.push(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.buffer.size > this.maxLines) {
|
|
||||||
this.buffer = new Set(Array.from(this.buffer).slice(parseInt(this.maxLines / 2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return added;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _caches = new Map();
|
|
||||||
|
|
||||||
const getLogCache = (name) => {
|
|
||||||
let cache = _caches.get(name);
|
|
||||||
if (!cache) {
|
|
||||||
cache = new LogCache();
|
|
||||||
_caches.set(name, cache);
|
|
||||||
}
|
|
||||||
return cache;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLogs = async (name, incremental = false, lines = 100) => {
|
|
||||||
const command = 'wire';
|
|
||||||
const args = ['service', 'logs', '--lines', lines, name];
|
|
||||||
|
|
||||||
const child = spawnSync(command, args, { encoding: 'utf8' });
|
|
||||||
const logLines = child.stdout.split(/\n/);
|
|
||||||
const cache = getLogCache(name);
|
|
||||||
const added = cache.append(logLines);
|
|
||||||
|
|
||||||
return incremental ? added : Array.from(cache.buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const logResolvers = {
|
|
||||||
Query: {
|
|
||||||
logs: async (_, { service, incremental }) => {
|
|
||||||
const lines = await getLogs(service, incremental);
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify({ incremental, lines })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,139 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import fs from 'fs';
|
|
||||||
import moment from 'moment';
|
|
||||||
import pick from 'lodash.pick';
|
|
||||||
import os from 'os';
|
|
||||||
import si from 'systeminformation';
|
|
||||||
import { spawnSync } from 'child_process';
|
|
||||||
|
|
||||||
const num = new Intl.NumberFormat('en', { maximumSignificantDigits: 3 });
|
|
||||||
|
|
||||||
const size = (n, unit) => {
|
|
||||||
const units = {
|
|
||||||
K: 3,
|
|
||||||
M: 6,
|
|
||||||
G: 9,
|
|
||||||
T: 12
|
|
||||||
};
|
|
||||||
|
|
||||||
const power = units[unit] || 0;
|
|
||||||
|
|
||||||
return num.format(Math.round(n / (10 ** power))) + (unit ? ` ${unit}` : '');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getVersionInfo = () => {
|
|
||||||
// TODO(telackey): Get from config (or figure out a better way to do this).
|
|
||||||
const versionFile = '/opt/kube/VERSION';
|
|
||||||
if (fs.existsSync(versionFile)) {
|
|
||||||
return fs.readFileSync(versionFile, { encoding: 'UTF8' }).replace(/^\s+|\s+$/g, '');
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCliVersionInfo = () => {
|
|
||||||
const command = 'wire';
|
|
||||||
const args = ['version'];
|
|
||||||
|
|
||||||
const child = spawnSync(command, args, { encoding: 'utf8' });
|
|
||||||
return child.stdout;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get system inforamtion.
|
|
||||||
* https://www.npmjs.com/package/systeminformation
|
|
||||||
*/
|
|
||||||
const getSystemInfo = async () => {
|
|
||||||
const ifaces = os.networkInterfaces();
|
|
||||||
const addresses = Object.entries(ifaces).reduce((result, [, values]) => {
|
|
||||||
values.forEach(({ family, address }) => {
|
|
||||||
address = address.toLowerCase();
|
|
||||||
// TODO(telackey): Include link-local IPv6?
|
|
||||||
if (!address.startsWith('127.') && !address.startsWith('fe80::') && !address.startsWith('::1')) {
|
|
||||||
result.push(address);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const cpu = await si.cpu();
|
|
||||||
const memory = await si.mem();
|
|
||||||
const device = await si.system();
|
|
||||||
|
|
||||||
return {
|
|
||||||
cpu: pick(cpu, 'brand', 'cores', 'manufacturer', 'vendor', 'speed'),
|
|
||||||
|
|
||||||
memory: {
|
|
||||||
total: size(memory.total, 'M'),
|
|
||||||
free: size(memory.free, 'M'),
|
|
||||||
used: size(memory.used, 'M'),
|
|
||||||
swaptotal: size(memory.swaptotal, 'M')
|
|
||||||
},
|
|
||||||
|
|
||||||
device: pick(device, 'model', 'serial', 'version'),
|
|
||||||
|
|
||||||
network: {
|
|
||||||
addresses
|
|
||||||
},
|
|
||||||
|
|
||||||
// https://nodejs.org/api/os.html
|
|
||||||
os: {
|
|
||||||
arch: os.arch(),
|
|
||||||
platform: os.platform(),
|
|
||||||
version: os.version ? os.version() : undefined // Node > 13
|
|
||||||
},
|
|
||||||
|
|
||||||
time: {
|
|
||||||
now: moment(),
|
|
||||||
up: moment().subtract(os.uptime(), 'seconds')
|
|
||||||
},
|
|
||||||
|
|
||||||
nodejs: {
|
|
||||||
version: process.version
|
|
||||||
},
|
|
||||||
|
|
||||||
laconic: {
|
|
||||||
kube: {
|
|
||||||
version: getVersionInfo()
|
|
||||||
},
|
|
||||||
wire: {
|
|
||||||
version: getCliVersionInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get system inforamtion.
|
|
||||||
* https://www.npmjs.com/package/systeminformation
|
|
||||||
*/
|
|
||||||
const getServiceInfo = async () => {
|
|
||||||
const command = 'wire';
|
|
||||||
const args = ['service', '--json'];
|
|
||||||
|
|
||||||
const child = spawnSync(command, args, { encoding: 'utf8' });
|
|
||||||
return JSON.parse(child.stdout);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const systemResolvers = {
|
|
||||||
Query: {
|
|
||||||
system_status: async () => {
|
|
||||||
const system = await getSystemInfo();
|
|
||||||
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify(system)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
service_status: async () => {
|
|
||||||
const serviceInfo = await getServiceInfo();
|
|
||||||
|
|
||||||
return {
|
|
||||||
timestamp: new Date().toUTCString(),
|
|
||||||
json: JSON.stringify(serviceInfo)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,155 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import compression from 'compression';
|
|
||||||
import bodyParser from 'body-parser';
|
|
||||||
import cors from 'cors';
|
|
||||||
import debug from 'debug';
|
|
||||||
import express from 'express';
|
|
||||||
import mustache from 'mustache-express';
|
|
||||||
import fs from 'fs';
|
|
||||||
import yaml from 'js-yaml';
|
|
||||||
import { ApolloServer, gql } from 'apollo-server-express';
|
|
||||||
import { print } from 'graphql/language';
|
|
||||||
import yargs from 'yargs';
|
|
||||||
|
|
||||||
// TODO(burdon): Use once published by @ashwinp.
|
|
||||||
// import { extensions as WNS_EXTENSIONS, schema as WNS_SCHEMA } from '@wirelineio/wns-schema';
|
|
||||||
|
|
||||||
import SYSTEM_STATUS from '@cerc-io/console-app/src/gql/system_status.graphql';
|
|
||||||
|
|
||||||
import { resolvers } from '../resolvers';
|
|
||||||
|
|
||||||
import API_SCHEMA from '../gql/api.graphql';
|
|
||||||
|
|
||||||
const argv = yargs
|
|
||||||
.option('config', {
|
|
||||||
alias: 'c',
|
|
||||||
description: 'Config file',
|
|
||||||
type: 'string'
|
|
||||||
})
|
|
||||||
.option('verbose', {
|
|
||||||
alias: 'v',
|
|
||||||
description: 'Verbse info',
|
|
||||||
type: 'boolean'
|
|
||||||
})
|
|
||||||
.help()
|
|
||||||
.alias('help', 'h')
|
|
||||||
.argv;
|
|
||||||
|
|
||||||
const configFile = argv.config || process.env.CONFIG_FILE;
|
|
||||||
if (!configFile) {
|
|
||||||
yargs.showHelp();
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = yaml.safeLoad(fs.readFileSync(configFile));
|
|
||||||
|
|
||||||
const log = debug('laconic:console:server');
|
|
||||||
|
|
||||||
debug.enable(config.system.debug);
|
|
||||||
|
|
||||||
if (argv.verbose) {
|
|
||||||
log(JSON.stringify(config, undefined, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Express server.
|
|
||||||
//
|
|
||||||
|
|
||||||
const { app: { publicUrl } } = config;
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.set('views', `${__dirname}/views`);
|
|
||||||
app.set('view engine', 'mustache');
|
|
||||||
app.engine('mustache', mustache());
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
|
||||||
app.use(compression());
|
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.redirect(publicUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// CORS
|
|
||||||
//
|
|
||||||
|
|
||||||
// import cors from 'cors'
|
|
||||||
// https://expressjs.com/en/resources/middleware/cors.html
|
|
||||||
// https://www.prisma.io/blog/enabling-cors-for-express-graphql-apollo-server-1ef999bfb38d
|
|
||||||
app.use(cors({
|
|
||||||
origin: true,
|
|
||||||
credentials: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
//
|
|
||||||
// React app
|
|
||||||
// TODO(burdon): Can we load this via WNS?
|
|
||||||
//
|
|
||||||
|
|
||||||
const bundles = [
|
|
||||||
'runtime', 'vendor', 'material-ui', 'cerc-io', 'main'
|
|
||||||
];
|
|
||||||
|
|
||||||
app.use(`${publicUrl}/lib`, express.static('./dist/client'));
|
|
||||||
|
|
||||||
app.get(publicUrl, (req, res) => {
|
|
||||||
res.render('console', {
|
|
||||||
title: 'Console',
|
|
||||||
container: 'root',
|
|
||||||
config: JSON.stringify(config),
|
|
||||||
scripts: bundles.map(bundle => ({ src: `${publicUrl}/lib/${bundle}.bundle.js` }))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// Apollo Server and middleware
|
|
||||||
// https://www.apollographql.com/docs/apollo-server/api/apollo-server
|
|
||||||
// https://www.apollographql.com/docs/apollo-server/api/apollo-server/#apolloserverapplymiddleware
|
|
||||||
//
|
|
||||||
|
|
||||||
const server = new ApolloServer({
|
|
||||||
typeDefs: [
|
|
||||||
API_SCHEMA
|
|
||||||
],
|
|
||||||
|
|
||||||
// https://www.apollographql.com/docs/graphql-tools/resolvers
|
|
||||||
resolvers,
|
|
||||||
|
|
||||||
// https://www.apollographql.com/docs/apollo-server/data/resolvers/#the-context-argument
|
|
||||||
context: ({ req }) => ({
|
|
||||||
config,
|
|
||||||
|
|
||||||
// TODO(burdon): Auth.
|
|
||||||
authToken: req.headers.authorization
|
|
||||||
}),
|
|
||||||
|
|
||||||
// https://www.apollographql.com/docs/apollo-server/testing/graphql-playground
|
|
||||||
// https://github.com/prisma-labs/graphql-playground#usage
|
|
||||||
// introspection: true,
|
|
||||||
playground: {
|
|
||||||
settings: {
|
|
||||||
'editor.theme': config.app.theme
|
|
||||||
},
|
|
||||||
tabs: [
|
|
||||||
{
|
|
||||||
name: 'Status',
|
|
||||||
endpoint: config.api.path,
|
|
||||||
query: print(gql(SYSTEM_STATUS))
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.applyMiddleware({ app, path: config.api.path });
|
|
||||||
|
|
||||||
//
|
|
||||||
// Start server
|
|
||||||
//
|
|
||||||
|
|
||||||
const { api: { port } } = config;
|
|
||||||
app.listen({ port }, () => {
|
|
||||||
log(`Running: http://localhost:${port}`);
|
|
||||||
});
|
|
@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="{{ container }}"></div>
|
|
||||||
|
|
||||||
<!-- Config loaded by client. -->
|
|
||||||
<script charset="utf-8" type="application/javascript">
|
|
||||||
window.__Lacnonic__ = { config: {{{ config }}} };
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- React bundles. -->
|
|
||||||
{{#scripts}}
|
|
||||||
<script charset="utf-8" type="application/javascript" src="{{src}}"></script>
|
|
||||||
{{/scripts}}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2019 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const Dotenv = require('dotenv-webpack');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
|
||||||
|
|
||||||
const PUBLIC_URL = process.env.PUBLIC_URL || '';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
devtool: 'eval-source-map',
|
|
||||||
|
|
||||||
devServer: {
|
|
||||||
contentBase: path.join(__dirname, 'dist'),
|
|
||||||
compress: true,
|
|
||||||
disableHostCheck: true,
|
|
||||||
port: 8080,
|
|
||||||
watchOptions: {
|
|
||||||
ignored: /node_modules/,
|
|
||||||
aggregateTimeout: 600
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
node: {
|
|
||||||
fs: 'empty'
|
|
||||||
},
|
|
||||||
|
|
||||||
entry: './src/client/main.js',
|
|
||||||
|
|
||||||
output: {
|
|
||||||
path: `${__dirname}/dist/client`,
|
|
||||||
filename: '[name].bundle.js',
|
|
||||||
publicPath: PUBLIC_URL
|
|
||||||
},
|
|
||||||
|
|
||||||
optimization: {
|
|
||||||
runtimeChunk: 'single',
|
|
||||||
splitChunks: {
|
|
||||||
chunks: 'all',
|
|
||||||
maxInitialRequests: Infinity,
|
|
||||||
minSize: 0,
|
|
||||||
cacheGroups: {
|
|
||||||
vendor: {
|
|
||||||
test: /[\\/]node_modules[\\/]/,
|
|
||||||
name (module) {
|
|
||||||
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
|
|
||||||
|
|
||||||
if (packageName.startsWith('@cerc-io')) {
|
|
||||||
return 'cerc-io';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageName.startsWith('@material-ui')) {
|
|
||||||
return 'material-ui';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'vendor';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
// https://github.com/jantimon/html-webpack-plugin#options
|
|
||||||
new HtmlWebPackPlugin({
|
|
||||||
template: './public/index.html',
|
|
||||||
templateParameters: {
|
|
||||||
title: 'Console'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
// https://www.npmjs.com/package/dotenv-webpack#properties
|
|
||||||
new Dotenv({
|
|
||||||
path: process.env.DOT_ENV || '.env'
|
|
||||||
}),
|
|
||||||
|
|
||||||
// NOTE: Must be defined below Dotenv (otherwise will override).
|
|
||||||
// https://webpack.js.org/plugins/environment-plugin
|
|
||||||
new webpack.EnvironmentPlugin({})
|
|
||||||
],
|
|
||||||
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /(node_modules)/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// https://github.com/eemeli/yaml-loader
|
|
||||||
{
|
|
||||||
test: /\.ya?ml$/,
|
|
||||||
type: 'json',
|
|
||||||
use: 'yaml-loader'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@material-ui/styles': path.resolve(__dirname, '..', '..', 'node_modules/@material-ui/styles'),
|
|
||||||
'react': path.resolve(__dirname, '..', '..', 'node_modules/react'),
|
|
||||||
'react-dom': path.resolve(__dirname, '..', '..', 'node_modules/react-dom')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user