{
7 | console.log(props.value)
8 | return (
9 |
10 | );
11 | };
12 |
13 |
14 | export default JsonWidget
--------------------------------------------------------------------------------
/server/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Fabric Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/json_schema/channel.js:
--------------------------------------------------------------------------------
1 | const schema = {
2 | type: "object",
3 | required:["Name","Consortium","OrdererName"],
4 | properties: {
5 | Name: {
6 | type:"string",
7 | title:"channel_name",
8 | },
9 | Desc: {
10 | type:"string",
11 | title:"channel_desc",
12 | },
13 | Consortium:{
14 | type: "string",
15 | enum: [],
16 | title: "consortiums"
17 | },
18 | OrdererName:{
19 | type: "string",
20 | enum: [],
21 | title: "orderer_name"
22 | }
23 | }
24 |
25 | }
26 |
27 |
28 |
29 | export { schema }
--------------------------------------------------------------------------------
/app/json_schema/organization.js:
--------------------------------------------------------------------------------
1 | const organizationSchema = {
2 | type: "object",
3 | required:["Organization","CommonName"],
4 | properties: {
5 | Country: {
6 | type:"string",
7 | title:"country"
8 | },
9 | Province:{
10 | type:"string",
11 | title:"province"
12 | },
13 | Locality:{
14 | type:"string",
15 | title:"locality"
16 | },
17 | Organization:{
18 | type:"string",
19 | title:"organization"
20 | },
21 | CommonName:{
22 | type:"string",
23 | title:"common_name"
24 | }
25 | }
26 |
27 | }
28 |
29 |
30 |
31 | export { organizationSchema }
--------------------------------------------------------------------------------
/app/json_schema/consortium.js:
--------------------------------------------------------------------------------
1 | const schema = {
2 | type: "object",
3 | required:["Name","MspNames"],
4 | properties: {
5 | Name: {
6 | type:"string",
7 | title:"consortium_name",
8 | },
9 | Desc: {
10 | type:"string",
11 | title:"consortium_desc",
12 | },
13 | Type: {
14 | type: "string",
15 | enum: ["application"],
16 | default: "application",
17 | title:"consortium_type",
18 | },
19 | MspNames:{
20 | type:"array",
21 | title:"consortium_msps",
22 | items: {
23 | enum: [],
24 | type: "string",
25 | },
26 | uniqueItems: true,
27 | }
28 | }
29 |
30 | }
31 |
32 |
33 |
34 | export { schema }
--------------------------------------------------------------------------------
/app/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Link} from 'react-router';
3 |
4 |
5 | class Footer extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | }
10 |
11 |
12 |
13 | render() {
14 |
15 |
16 | return (
17 |
29 | );
30 | }
31 | }
32 |
33 | export default Footer;
--------------------------------------------------------------------------------
/app/json_schema/chaincode.js:
--------------------------------------------------------------------------------
1 | const schema = {
2 | type: "object",
3 | required:["Name","Path","Lang","Version","PeerName"],
4 | properties: {
5 | Name: {
6 | type:"string",
7 | title:"chaincode_name",
8 | },
9 | Path: {
10 | type:"string",
11 | title:"path",
12 | },
13 | Lang: {
14 | type:"string",
15 | title:"language",
16 | },
17 | Version: {
18 | type:"string",
19 | title:"version",
20 | },
21 | PeerName:{
22 | type: "string",
23 | enum: [],
24 | title: "peers"
25 | },
26 | Init: {
27 | type:"string",
28 | title:"init_code",
29 | },
30 | Invoke: {
31 | type:"string",
32 | title:"invoke_code",
33 | },
34 | Query: {
35 | type:"string",
36 | title:"query_code",
37 | },
38 | }
39 |
40 | }
41 |
42 |
43 |
44 | export { schema }
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { HashRouter } from 'react-router-dom'
4 | import createBrowserHistory from 'history/lib/createBrowserHistory';
5 | import routes from './routes';
6 | import { addLocaleData,IntlProvider } from 'react-intl';
7 | import en from 'react-intl/locale-data/en';
8 | import zh from 'react-intl/locale-data/zh';
9 | import zh_CN from './message/zh_CN';
10 | import en_US from './message/en_US';
11 |
12 |
13 | let history = createBrowserHistory();
14 | let locale = "en"
15 | let messages = en_US
16 | addLocaleData([...en, ...zh]);
17 | if(window.location.href.indexOf("zh_CN")!=-1){
18 | locale = "zh"
19 | messages = zh_CN
20 | }
21 | const errorReporter = (error, locale, message) =>{
22 | // custom error reporting code here
23 | }
24 | ReactDOM.render( {routes}, document.getElementById('app'));
--------------------------------------------------------------------------------
/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg"
5 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/entity"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
7 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
8 | "github.com/gin-contrib/static"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func main() {
13 | util.Init()
14 | entity.Init()
15 | store.Init()
16 | defer store.Bt.DB.Close()
17 | r := gin.Default()
18 |
19 | r.Use(static.Serve("/", static.LocalFile("static", false)))
20 |
21 | r.GET("/api/entity/:entity", pkg.GetEntitys)
22 | r.GET("/api/entity/:entity/:id", pkg.GetEntity)
23 | r.GET("/api/entity/:entity/:id/state", pkg.GetNodeState)
24 | r.POST("/api/entity/:entity/:id", pkg.CreateEntity)
25 | r.PUT("/api/entity/:entity/:id/cmd", pkg.ExecCMD)
26 | r.DELETE("/api/entity/:entity/:id", pkg.DelEntity)
27 |
28 | r.GET("/api/organizations/:id/:ca", pkg.GetCert)
29 | r.Run()
30 | }
31 |
--------------------------------------------------------------------------------
/app/json_schema/peer.js:
--------------------------------------------------------------------------------
1 | const schema = {
2 |
3 | type: "object",
4 | required:["Name","ListenAddress","ListenPort","EventListenPort","LocalMSPID","AdminMSPID","ChainCodeListenPort"],
5 | properties: {
6 | Name:{
7 | type: "string",
8 | default: "Peer",
9 | title:"node_name"
10 | },
11 | ListenAddress:{
12 | type: "string",
13 | default: "127.0.0.1",
14 | title:"ip_address"
15 | },
16 | ListenPort:{
17 | type: "number",
18 | default: 7051,
19 | title:"port"
20 | },
21 | ChainCodeListenPort:{
22 | type: "number",
23 | default: 7052,
24 | title:"chaincode_port"
25 | },
26 | EventListenPort:{
27 | type: "number",
28 | default: 7053,
29 | title:"Event端口"
30 | },
31 | LocalMSPID:{
32 | type: "string",
33 | enum: [],
34 | title: "peer_msp"
35 | },
36 | AdminMSPID:{
37 | type: "string",
38 | enum: [],
39 | title: "admin_msp"
40 | }
41 | }
42 |
43 |
44 | }
45 |
46 | const uiSchema = {
47 | }
48 |
49 | const formData = {
50 | };
51 |
52 | export { schema, uiSchema,formData }
--------------------------------------------------------------------------------
/server/pkg/entity/entity.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "github.com/mitchellh/mapstructure"
5 | "reflect"
6 | )
7 |
8 | type Action interface {
9 | Create() error
10 | }
11 |
12 | type Get interface {
13 | GetEntity() error
14 | }
15 |
16 | var entityRegistry = make(map[string]reflect.Type)
17 |
18 | func Init() {
19 | entityRegistry["organizations"] = reflect.TypeOf(Organization{})
20 | entityRegistry["peers"] = reflect.TypeOf(Peer{})
21 | }
22 |
23 | func GetEntityInstance(name string) interface{} {
24 | if entityRegistry[name] == nil {
25 | return nil
26 | }
27 | v := reflect.New(entityRegistry[name]).Elem()
28 | return v.Interface()
29 | }
30 |
31 | func MapToEntity(i interface{}, entityName string) interface{} {
32 | switch entityName {
33 | case "peers":
34 | var e Peer
35 | mapstructure.Decode(i, &e)
36 | return &e
37 | case "orderers":
38 | var e Orderer
39 | mapstructure.Decode(i, &e)
40 | return &e
41 | case "organizations":
42 | var e Organization
43 | mapstructure.Decode(i, &e)
44 | return &e
45 | case "consortiums":
46 | var e Consortium
47 | mapstructure.Decode(i, &e)
48 | return &e
49 | case "channels":
50 | var e Channel
51 | mapstructure.Decode(i, &e)
52 | return &e
53 | case "chaincodes":
54 | var e ChainCode
55 | mapstructure.Decode(i, &e)
56 | return &e
57 | }
58 | return i
59 | }
60 |
--------------------------------------------------------------------------------
/app/components/Peer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PeerTable from './PeerTable';
3 | import { injectIntl } from 'react-intl';
4 |
5 |
6 | class Peer extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | selected:0,
12 | data: []
13 | };
14 | }
15 |
16 | callback =(state)=>{
17 | this.setState(state)
18 | }
19 |
20 | componentDidMount = () => {
21 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'peer_manage'});
22 |
23 | let that = this;
24 |
25 | var url = 'api/entity/peers';
26 | fetch(url, {
27 | method: 'get',
28 | }).then(response => {
29 | return response.json();
30 | })
31 | .then(function (data) {
32 | that.setState({ data: data.peers==null?[]:data.peers });
33 | }).catch(function (e) {
34 | console.log(e);
35 | });
36 | }
37 |
38 | render() {
39 |
40 | const { history } = this.props;
41 |
42 | return (
43 |
46 | )
47 | }
48 | }
49 |
50 | export default injectIntl(Peer);
51 |
52 |
--------------------------------------------------------------------------------
/app/components/Channel.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ChannelTable from './ChannelTable';
3 | import { injectIntl } from 'react-intl';
4 |
5 |
6 | class Channel extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | selected:0,
12 | data: []
13 | };
14 | }
15 |
16 | callback =(state)=>{
17 | this.setState(state)
18 | }
19 |
20 | componentDidMount = () => {
21 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'channel_manage'})
22 | let that = this;
23 |
24 | var url = 'api/entity/channels';
25 | fetch(url, {
26 | method: 'get',
27 | }).then(response => {
28 | return response.json();
29 | })
30 | .then(function (data) {
31 | that.setState({ data: data.channels==null?[]:data.channels });
32 | }).catch(function (e) {
33 | console.log(e);
34 | });
35 | }
36 |
37 | render() {
38 |
39 | const { history } = this.props;
40 |
41 | return (
42 |
43 |
44 |
45 | )
46 | }
47 | }
48 |
49 | export default injectIntl(Channel);
50 |
51 |
--------------------------------------------------------------------------------
/app/components/Orderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import OrdererTable from './OrdererTable';
3 | import { injectIntl } from 'react-intl';
4 |
5 |
6 | class Orderer extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | selected:0,
12 | data: []
13 | };
14 | }
15 |
16 | callback =(state)=>{
17 | this.setState(state)
18 | }
19 |
20 | componentDidMount = () => {
21 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'orderer_manage'});
22 | let that = this;
23 |
24 | var url = 'api/entity/orderers';
25 | fetch(url, {
26 | method: 'get',
27 | }).then(response => {
28 | return response.json();
29 | })
30 | .then(function (data) {
31 | that.setState({ data: data.orderers==null?[]:data.orderers });
32 | }).catch(function (e) {
33 | console.log(e);
34 | });
35 | }
36 |
37 | render() {
38 |
39 | const { history } = this.props;
40 |
41 | return (
42 |
43 |
44 |
45 | )
46 | }
47 | }
48 |
49 | export default injectIntl(Orderer);
50 |
51 |
--------------------------------------------------------------------------------
/app/components/ChainCode.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ChainCodeTable from './ChainCodeTable';
3 | import { injectIntl } from 'react-intl';
4 |
5 |
6 | class ChainCode extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | selected:0,
12 | data: []
13 | };
14 | }
15 |
16 | callback =(state)=>{
17 | this.setState(state)
18 | }
19 |
20 | componentDidMount = () => {
21 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'chaincode_manage'});
22 |
23 | let that = this;
24 |
25 | var url = 'api/entity/chaincodes';
26 | fetch(url, {
27 | method: 'get',
28 | }).then(response => {
29 | return response.json();
30 | })
31 | .then(function (data) {
32 | that.setState({ data: data.chaincodes==null?[]:data.chaincodes });
33 | }).catch(function (e) {
34 | console.log(e);
35 | });
36 | }
37 |
38 | render() {
39 |
40 | const { history } = this.props;
41 |
42 | return (
43 |
44 |
45 |
46 | )
47 | }
48 | }
49 |
50 | export default injectIntl(ChainCode);
51 |
52 |
--------------------------------------------------------------------------------
/app/components/Consortium.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ConsortiumTable from './ConsortiumTable';
3 | import { injectIntl } from 'react-intl';
4 |
5 |
6 | class Consortium extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | selected:0,
12 | data: []
13 | };
14 | }
15 |
16 | callback =(state)=>{
17 | this.setState(state)
18 | }
19 |
20 | componentDidMount = () => {
21 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'consortium_manage'});
22 | let that = this;
23 | var headers = new Headers();
24 |
25 | var url = 'api/entity/consortiums';
26 | fetch(url, {
27 | method: 'get',
28 | }).then(response => {
29 | return response.json();
30 | })
31 | .then(function (data) {
32 | that.setState({ data: data.consortiums==null?[]:data.consortiums });
33 | }).catch(function (e) {
34 | console.log(e);
35 | });
36 | }
37 |
38 | render() {
39 |
40 | const { history } = this.props;
41 |
42 | return (
43 |
44 |
45 |
46 | )
47 | }
48 | }
49 |
50 | export default injectIntl(Consortium);
51 |
52 |
--------------------------------------------------------------------------------
/app/json_schema/orderer.js:
--------------------------------------------------------------------------------
1 | const schema = {
2 |
3 | type: "object",
4 | required:["Name","ListenAddress","ListenPort","Consortiums","LocalMSPID"],
5 | properties: {
6 | Name:{
7 | type: "string",
8 | default: "OrdererNode1",
9 | title:"node_name"
10 | },
11 | OrdererType: {
12 | type: "string",
13 | enum: ["solo"],
14 | default: "solo",
15 | title:"node_type"
16 | },
17 | LedgerType: {
18 | type: "string",
19 | enum: ["file", "json", "ram"],
20 | default: "file",
21 | title:"ledger_type"
22 | },
23 | ListenAddress:{
24 | type: "string",
25 | default: "127.0.0.1",
26 | title:"ip_address"
27 | },
28 | ListenPort:{
29 | type: "number",
30 | default: 7050,
31 | title:"port"
32 | },
33 | LocalMSPID:{
34 | type: "string",
35 | enum: [],
36 | title: "msp_name"
37 | },
38 | Consortiums:{
39 | type:"array",
40 | items: {
41 | enum: [],
42 | type: "string",
43 | },
44 | uniqueItems: true,
45 | title:"consortiums"
46 | }
47 |
48 | }
49 | }
50 |
51 | const uiSchema = {
52 |
53 | }
54 |
55 | const formData = {
56 | General:{TLS:{RootCAs:["tls/ca.crt"]}}
57 | };
58 |
59 | export { schema, uiSchema,formData }
--------------------------------------------------------------------------------
/server/pkg/util/cache.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | // "fmt"
5 | "sync"
6 | "time"
7 | )
8 |
9 | //过期时间 10小时
10 | const Time int64 = 1 //3600 * 10
11 |
12 | //动态缓存数据库
13 | var Caches *CacheManager
14 |
15 | type CacheManager struct {
16 | lock *sync.RWMutex
17 | caches map[string]*Cache
18 | }
19 |
20 | type Cache struct {
21 | Value interface{}
22 | Times int64
23 | }
24 |
25 | func Init() {
26 | Caches = NewCacheManager(300)
27 | }
28 |
29 | func NewCacheManager(size int) *CacheManager {
30 | return &CacheManager{new(sync.RWMutex), make(map[string]*Cache, size)}
31 | }
32 |
33 | func (this *CacheManager) Set(key string, v interface{}) {
34 | this.lock.Lock()
35 | x := Cache{Value: v, Times: Time}
36 | this.caches[key] = &x
37 | this.lock.Unlock()
38 | }
39 |
40 | func (this *CacheManager) Get(key string) *Cache {
41 | this.lock.RLock()
42 | v := this.caches[key]
43 | this.lock.RUnlock()
44 | return v
45 | }
46 |
47 | func (this *CacheManager) Delete(key string) *Cache {
48 | this.lock.Lock()
49 | v := this.caches[key]
50 | delete(this.caches, key)
51 | this.lock.Unlock()
52 | return v
53 | }
54 |
55 | func (this *CacheManager) IsExist(key string) bool {
56 | if xy := this.Get(key); xy != nil {
57 | return true
58 | } else {
59 | return false
60 | }
61 | }
62 |
63 | func (this *CacheManager) IsExpired(key string, ttl int) bool {
64 |
65 | if xy := this.Get(key); xy != nil {
66 | return (time.Now().Unix() - xy.Times) >= int64(ttl)
67 | } else {
68 | return true
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/server/pkg/entity/chainCode.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "fmt"
5 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
7 | )
8 |
9 | type ChainCode struct {
10 | Name string
11 | Lang string
12 | Version string
13 | Path string
14 | PeerName string
15 | Init string
16 | Invoke string
17 | Query string
18 | State string
19 | }
20 |
21 | func (o *ChainCode) Exec(cmdInfo map[string]string) string {
22 |
23 | cmdInfo["Path"] = o.Path
24 | cmdInfo["Name"] = fmt.Sprintf("%s:%s", o.Name, o.Version)
25 |
26 | peerName := cmdInfo["Peer"]
27 | if peerName != "" {
28 | peer, err := getPeerByName(peerName)
29 | if err != nil {
30 | return err.Error()
31 | }
32 | cmdInfo["PeerNodeName"] = peer.Name
33 | cmdInfo["PeerEndPoint"] = fmt.Sprintf("%s:%d", peer.ListenAddress, peer.ChainCodeListenPort)
34 | }
35 |
36 | return ExecChainCode(cmdInfo)
37 | }
38 |
39 | func (o *ChainCode) GetEntity() error {
40 | cacheNodeName := chaincodes + "." + o.Name+"."+o.PeerName
41 | cache := util.Caches.Get(cacheNodeName)
42 | if cache == nil {
43 | o.State = "disable"
44 | } else {
45 | o.State = "enable"
46 | }
47 | return nil
48 | }
49 |
50 | func getChaincodeByName(cName string) (*ChainCode, error) {
51 | var c *ChainCode
52 | i, err := store.Bt.ViewByKey(chaincodes, cName)
53 | if err != nil {
54 | return c, err
55 | }
56 | i = MapToEntity(i, chaincodes)
57 | if c, ok := i.(*ChainCode); ok {
58 | return c, nil
59 | }
60 | return c, nil
61 | }
62 |
--------------------------------------------------------------------------------
/app/json_schema/cert.js:
--------------------------------------------------------------------------------
1 | import JsonWidget from "./JsonWidget"
2 |
3 | const nameSchema = {
4 | type: "object",
5 | required:["CommonName"],
6 | properties: {
7 | CommonName:{
8 | type:"string"
9 | },
10 |
11 | Country: {
12 | type:"array",
13 | items:{
14 | type:"string"
15 | }
16 | },
17 | Organization:{
18 | type:"array",
19 | items:{
20 | type:"string"
21 | }
22 | },
23 | OrganizationalUnit:{
24 | type:"array",
25 | items:{
26 | type:"string"
27 | }
28 | },
29 | Locality:{
30 | type:"array",
31 | items:{
32 | type:"string"
33 | }
34 | },
35 | Province:{
36 | type:"array",
37 | items:{
38 | type:"string"
39 | }
40 | },
41 |
42 |
43 |
44 | }
45 |
46 | }
47 |
48 | const pemSchema = {
49 | type: "object",
50 | required:["Cert"],
51 | properties: {
52 | Key:{
53 | type:"string",
54 | title:"keys"
55 | },
56 | Cert:{
57 | type:"string",
58 | title:"certificate"
59 | },
60 |
61 |
62 |
63 | }
64 |
65 | }
66 |
67 | const widgets = {
68 | jsonWidget: JsonWidget
69 | };
70 |
71 |
72 | const uiPemSchema = {
73 | Cert: {
74 | "ui:widget": "jsonWidget"
75 | },
76 | Key: {
77 | "ui:widget": "textarea"
78 | },
79 | }
80 |
81 |
82 |
83 | export { nameSchema, pemSchema,uiPemSchema,widgets }
--------------------------------------------------------------------------------
/server/pkg/entity/common.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "runtime"
7 | )
8 |
9 | var (
10 | templateDir = "template"
11 | blockDir = "block"
12 | configDir = "config"
13 | peerDir = filepath.Join(configDir, "peer")
14 | OrdererDir = filepath.Join(configDir, "orderer")
15 | channelDir = filepath.Join(configDir, "channel")
16 | mspDir = filepath.Join(configDir, "msp")
17 | tempDir = filepath.Join(configDir, "tmp")
18 | binDir = filepath.Join("bin",runtime.GOOS,runtime.GOARCH)
19 | peerBin = filepath.Join(binDir, "peer")
20 | ordererBin = filepath.Join(binDir, "orderer")
21 |
22 | configtxyml = "configtx.yaml"
23 | configyml = "config.yaml"
24 | ordereryml = "orderer.yaml"
25 | coreYml = "core.yaml"
26 | channel = "channel"
27 | defaultMspDir = "msp"
28 |
29 | peers = "peers"
30 | consortiums = "consortiums"
31 | organizations = "organizations"
32 | channels = "channels"
33 | chaincodes = "chaincodes"
34 | orderers = "orderers"
35 |
36 | windows = "windows"
37 | )
38 |
39 |
40 | func Path(paths ...string) string {
41 | path := filepath.Join(paths...)
42 | os.RemoveAll(path)
43 | err := os.MkdirAll(path, 0755)
44 | if err == nil {
45 | return path
46 | }
47 | return ""
48 | }
49 |
50 | func SimpleWrite(path string, fileName string, b []byte) error {
51 | file, err := os.Create(filepath.Join(path, fileName))
52 | if err != nil {
53 | return err
54 | }
55 | defer file.Close()
56 | _, err = file.WriteString(string(b))
57 | if err != nil {
58 | return err
59 | }
60 | return nil
61 | }
62 |
63 |
64 | func WindowsBin(bin string)string{
65 | if(runtime.GOOS == windows){
66 | return bin +".exe"
67 | }
68 | return bin
69 | }
--------------------------------------------------------------------------------
/server/pkg/store/common.go:
--------------------------------------------------------------------------------
1 | package store
2 |
3 | import (
4 | //"errors"
5 | //"encoding/json"
6 | )
7 |
8 | var (
9 | ConsortiumBucket = "consortium"
10 | OrganizationBucket = "organization"
11 | )
12 |
13 | type Consortium struct {
14 | //OrderName string
15 | Name string
16 | Type string
17 | Desc string
18 | MspNames []string
19 | }
20 |
21 | type Organization struct {
22 | Country string
23 | Province string
24 | Locality string
25 | Organization string
26 | CommonName string
27 | OrganizationalUnit string
28 | StreetAddress string
29 | PostalCode string
30 | PEMs []PEM
31 | MSPs []MSP
32 | }
33 |
34 | type PEM struct {
35 | Name string
36 | Key string
37 | Cert string
38 | Type string
39 | }
40 |
41 | type MSP struct {
42 | Name string
43 | Path string
44 | Type string
45 | Role string
46 | }
47 |
48 | // func GetConsortiumByName(name string) ( *Consortium,error){
49 | // consortium := &Consortium{}
50 | // key :=name
51 | // record,err := bolt.ViewByKey(ConsortiumBucket,key)
52 | // if err != nil {
53 | // return consortium ,err
54 | // }
55 | // for _, data := range record {
56 | // json.Unmarshal([]byte(data), consortium)
57 | // return consortium,nil
58 | // }
59 |
60 | // return consortium,errors.New("NOT FOUND")
61 |
62 | // }
63 |
64 | // func GetOrganizationByName(name string)(*Organization,error){
65 | // organization := &Organization{}
66 | // key := name
67 | // record,err := bolt.ViewByKey(OrganizationBucket,key)
68 | // if err != nil {
69 | // return organization,err
70 | // }
71 | // for _, data := range record {
72 | // json.Unmarshal([]byte(data), organization)
73 | // return organization,nil
74 | // }
75 |
76 | // return organization,errors.New("NOT FOUND")
77 | // }
78 |
--------------------------------------------------------------------------------
/app/components/Organization.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import OrganizationTable from './OrganizationTable';
3 | import CertTable from './CertTable';
4 | import MspTable from './MspTable';
5 | import { injectIntl } from 'react-intl';
6 |
7 |
8 |
9 | class Organization extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | selected: 0,
15 | data:[]
16 | };
17 | }
18 |
19 | callback = (state) => {
20 | this.setState(state);
21 | }
22 |
23 | componentDidMount = () => {
24 | document.title = "Fabric Manager "+this.props.intl.formatMessage({id:'organization_manage'});
25 | let that = this;
26 | var headers = new Headers();
27 |
28 | var url = 'api/entity/organizations';
29 | fetch(url, {
30 | method: 'get',
31 | headers: headers,
32 | mode: "cors"
33 | }).then(response => {
34 | return response.json();
35 | })
36 | .then(function (data) {
37 | that.setState({ data: data.organizations == null ? [] : data.organizations });
38 | }).catch(function (e) {
39 | console.log(e);
40 | });
41 | }
42 |
43 | render() {
44 | const { history } = this.props;
45 |
46 | return (
47 |
48 |
49 |
50 |
51 |
52 | )
53 |
54 |
55 | }
56 | }
57 |
58 | export default injectIntl(Organization);
59 |
60 |
--------------------------------------------------------------------------------
/server/pkg/certificate/certificate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package certificate provide helpers to manipulate certificates.
16 | package certificate
17 |
18 | import (
19 | "crypto/rsa"
20 | "crypto/x509"
21 | "fmt"
22 | )
23 |
24 | // Bundle represents a pair of private key and certificate.
25 | type Bundle struct {
26 | Name string
27 | Key *rsa.PrivateKey
28 | Cert *x509.Certificate
29 | }
30 |
31 | // Raw returns the raw bytes for the private key and certificate.
32 | func (b *Bundle) Raw() ([]byte, []byte) {
33 | return x509.MarshalPKCS1PrivateKey(b.Key), b.Cert.Raw
34 | }
35 |
36 | // RawToBundle creates a bundle from the name and bytes given for a private key
37 | // and a certificate.
38 | func RawToBundle(name string, key []byte, cert []byte) (*Bundle, error) {
39 | k, err := x509.ParsePKCS1PrivateKey(key)
40 | if err != nil {
41 | return nil, fmt.Errorf("failed parsing private key: %v", err)
42 | }
43 | c, err := x509.ParseCertificate(cert)
44 | if err != nil {
45 | return nil, fmt.Errorf("failed parsing certificate: %v", err)
46 | }
47 | return &Bundle{Name: name, Key: k, Cert: c}, nil
48 | }
49 |
50 | // State represents a certificate state (Valid, Expired, Revoked).
51 | type State int
52 |
53 | // Certificate states.
54 | const (
55 | Valid State = iota
56 | Revoked
57 | Expired
58 | )
59 |
--------------------------------------------------------------------------------
/server/pkg/entity/consortium.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "errors"
5 | "path/filepath"
6 | "strings"
7 |
8 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
9 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
10 | profileConfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
11 | )
12 |
13 | type Consortium struct {
14 | //OrderName string
15 | Name string
16 | Type string
17 | Desc string
18 | MspNames []string
19 | }
20 |
21 | func getConsortiumByName(cName string) (*Consortium, error) {
22 | var c *Consortium
23 | i, err := store.Bt.ViewByKey(consortiums, cName)
24 | if err != nil {
25 | return c, err
26 | }
27 | i = MapToEntity(i, consortiums)
28 | if c, ok := i.(*Consortium); ok {
29 | return c, nil
30 | }
31 | return c, nil
32 | }
33 |
34 | func configConsortiumOrgs(path string, cName string) ([]*profileConfig.Organization, error) {
35 | var orgs []*profileConfig.Organization
36 |
37 | consortium, err := getConsortiumByName(cName)
38 | if err != nil {
39 | return nil, err
40 | }
41 |
42 | for _, v := range consortium.MspNames {
43 | oname := strings.SplitN(v, ".", 2)[1]
44 | peer,_:= getPeerByLocalMSPId(v)
45 | if(peer ==nil){
46 | return nil,errors.New("desc_2"+"|"+oname)
47 | }
48 |
49 | msp, err := getMspByName(v)
50 | mspPath := msp.Path
51 | if err != nil {
52 | return nil, err
53 | }
54 | dest := Path(path, consortiums, cName, v)
55 | util.Copy(mspPath, dest)
56 |
57 | var AnchorPeers []*profileConfig.AnchorPeer
58 | a := &profileConfig.AnchorPeer{
59 | Host: "127.0.0.1",
60 | Port: int(peer.ListenPort),
61 | }
62 | AnchorPeers = append(AnchorPeers, a)
63 | organization := &profileConfig.Organization{
64 | Name: oname,
65 | ID: oname,
66 | MSPDir: filepath.Join(consortiums, cName, v, "msp"),
67 | AnchorPeers: AnchorPeers,
68 | }
69 | orgs = append(orgs, organization)
70 | }
71 | return orgs, nil
72 | }
73 |
--------------------------------------------------------------------------------
/app/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import Button from '@material-ui/core/Button';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import { injectIntl,FormattedMessage } from 'react-intl';
6 |
7 | const styles = theme => ({
8 | button: {
9 | margin: theme.spacing.unit,
10 | }
11 | });
12 |
13 |
14 | class Navbar extends React.Component {
15 | constructor(props) {
16 | super(props);
17 |
18 | }
19 |
20 | changeLang = (lang) => {
21 | window.location.href = `?${lang}`
22 | }
23 |
24 |
25 | render() {
26 | const { classes,intl } = this.props;
27 | return (
28 |
52 | );
53 | }
54 | }
55 | export default withStyles(styles)(injectIntl(Navbar));
56 |
--------------------------------------------------------------------------------
/app/components/EnhancedTableToolbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withStyles } from '@material-ui/core/styles';
3 | import Toolbar from '@material-ui/core/Toolbar';
4 | import Typography from '@material-ui/core/Typography';
5 | import Paper from '@material-ui/core/Paper';
6 | import { lighten } from '@material-ui/core/styles/colorManipulator';
7 | import IconButton from '@material-ui/core/IconButton';
8 | import Tooltip from '@material-ui/core/Tooltip';
9 |
10 | const toolbarStyles = theme => ({
11 | root: {
12 | paddingRight: theme.spacing.unit,
13 | },
14 | highlight:
15 | theme.palette.type === 'light'
16 | ? {
17 | color: theme.palette.secondary.main,
18 | backgroundColor: lighten(theme.palette.secondary.light, 0.85),
19 | }
20 | : {
21 | color: theme.palette.text.primary,
22 | backgroundColor: theme.palette.secondary.dark,
23 | },
24 | spacer: {
25 | flex: '1 1 100%',
26 | },
27 | actions: {
28 | color: theme.palette.text.secondary,
29 | flex: '0 0 auto',
30 | },
31 | title: {
32 | flex: '0 0 auto',
33 | },
34 | button: {
35 | margin: theme.spacing.unit,
36 | },
37 | leftIcon: {
38 | marginRight: theme.spacing.unit,
39 | }
40 | });
41 |
42 | class EnhancedTableToolbar extends React.Component {
43 |
44 | constructor(props, context) {
45 | super(props, context);
46 | }
47 |
48 | render() {
49 | const { classes, title,tooltip } = this.props;
50 | return (
51 |
52 |
53 |
54 | {title}
55 |
56 |
57 |
58 |
59 |
60 |
61 | {tooltip}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | }
71 | }
72 |
73 | EnhancedTableToolbar = withStyles(toolbarStyles)(EnhancedTableToolbar);
74 |
75 | export default EnhancedTableToolbar;
--------------------------------------------------------------------------------
/app/components/EnhancedTableHead.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TableBody from '@material-ui/core/TableBody';
3 | import TableCell from '@material-ui/core/TableCell';
4 | import TableHead from '@material-ui/core/TableHead';
5 | import TableRow from '@material-ui/core/TableRow';
6 | import TableSortLabel from '@material-ui/core/TableSortLabel';
7 | import Tooltip from '@material-ui/core/Tooltip';
8 | import { injectIntl } from 'react-intl';
9 |
10 | class EnhancedTableHead extends React.Component {
11 |
12 | constructor(props, context) {
13 | super(props, context);
14 | }
15 |
16 | createSortHandler = property => event => {
17 | this.props.onRequestSort(event, property);
18 | };
19 |
20 |
21 |
22 | render() {
23 | const { order, orderBy,columnData,data,intl } = this.props;
24 |
25 | return (
26 |
27 |
28 |
29 | {columnData.map(column => {
30 | return (
31 |
37 |
42 |
47 | {intl.formatMessage({id:column.label}) }
48 |
49 |
50 |
51 | );
52 | }, this)}
53 |
54 | {intl.formatMessage({id:"operation"}) }
55 |
56 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
63 | export default injectIntl(EnhancedTableHead);
--------------------------------------------------------------------------------
/app/components/JsonForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { render } from "react-dom";
3 | import Form from "react-jsonschema-form";
4 | import Button from '@material-ui/core/Button';
5 | import { withStyles } from '@material-ui/core/styles';
6 | import { injectIntl,defineMessages } from 'react-intl';
7 |
8 | const styles = theme => ({
9 | button: {
10 | margin: theme.spacing.unit,
11 | },
12 | });
13 |
14 | class jsonForm extends React.Component {
15 |
16 | constructor(props) {
17 | super(props);
18 | }
19 |
20 | render() {
21 | const { classes,schema, uiSchema, fields, formContext, formData, handleForm, formMode,widgets,intl } = this.props;
22 | let values = {};
23 | Object.keys(schema.properties).forEach(function (key) {
24 | let property = schema.properties[key];
25 | let title = property.title;
26 | values[key] ={id:title};
27 | });
28 | const messages = defineMessages(values);
29 | Object.keys(schema.properties).forEach(function (key) {
30 | let property = schema.properties[key];
31 | property.title = intl.formatMessage({id:messages[key].id});
32 | });
33 | let bt = (
34 |
37 |
40 |
41 |
42 | )
43 | if (formMode == "view") {
44 | bt = (
45 |
48 |
49 | )
50 | }
51 | return (
52 |
55 | )
56 | }
57 | }
58 |
59 | export default withStyles(styles)(injectIntl(jsonForm));
--------------------------------------------------------------------------------
/server/pkg/util/copy.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "io"
5 | "io/ioutil"
6 | "os"
7 | "path/filepath"
8 | )
9 |
10 | // Copy copies src to dest, doesn't matter if src is a directory or a file
11 | func Copy(src, dest string) error {
12 | info, err := os.Lstat(src)
13 | if err != nil {
14 | return err
15 | }
16 | return copy(src, dest, info)
17 | }
18 |
19 | // copy dispatches copy-funcs according to the mode.
20 | // Because this "copy" could be called recursively,
21 | // "info" MUST be given here, NOT nil.
22 | func copy(src, dest string, info os.FileInfo) error {
23 | if info.Mode()&os.ModeSymlink != 0 {
24 | return lcopy(src, dest, info)
25 | }
26 | if info.IsDir() {
27 | return dcopy(src, dest, info)
28 | }
29 | return fcopy(src, dest, info)
30 | }
31 |
32 | // fcopy is for just a file,
33 | // with considering existence of parent directory
34 | // and file permission.
35 | func fcopy(src, dest string, info os.FileInfo) error {
36 |
37 | if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
38 | return err
39 | }
40 |
41 | f, err := os.Create(dest)
42 | if err != nil {
43 | return err
44 | }
45 | defer f.Close()
46 |
47 | if err = os.Chmod(f.Name(), info.Mode()); err != nil {
48 | return err
49 | }
50 |
51 | s, err := os.Open(src)
52 | if err != nil {
53 | return err
54 | }
55 | defer s.Close()
56 |
57 | _, err = io.Copy(f, s)
58 | return err
59 | }
60 |
61 | // dcopy is for a directory,
62 | // with scanning contents inside the directory
63 | // and pass everything to "copy" recursively.
64 | func dcopy(srcdir, destdir string, info os.FileInfo) error {
65 |
66 | if err := os.MkdirAll(destdir, info.Mode()); err != nil {
67 | return err
68 | }
69 |
70 | contents, err := ioutil.ReadDir(srcdir)
71 | if err != nil {
72 | return err
73 | }
74 |
75 | for _, content := range contents {
76 | cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
77 | if err := copy(cs, cd, content); err != nil {
78 | // If any error, exit immediately
79 | return err
80 | }
81 | }
82 | return nil
83 | }
84 |
85 | // lcopy is for a symlink,
86 | // with just creating a new symlink by replicating src symlink.
87 | func lcopy(src, dest string, info os.FileInfo) error {
88 | src, err := os.Readlink(src)
89 | if err != nil {
90 | return err
91 | }
92 | return os.Symlink(src, dest)
93 | }
94 |
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fabric-manager",
3 | "description": "this is a simple hyperledger fabric manager",
4 | "version": "1.0.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/fabric-lab/fabric-manager"
8 | },
9 | "main": "server.js",
10 | "scripts": {
11 | "start": "node server.js",
12 | "watch": "nodemon server.js",
13 | "postinstall": "bower install && gulp build"
14 | },
15 | "babel": {
16 | "presets": [
17 | "es2015",
18 | "react",
19 | "stage-1"
20 | ]
21 | },
22 | "dependencies": {
23 | "@material-ui/core": "^1.0.0",
24 | "@material-ui/icons": "^1.1.0",
25 | "alt": "^0.17.8",
26 | "async": "^1.5.0",
27 | "body-parser": "^1.14.1",
28 | "colors": "^1.1.2",
29 | "compression": "^1.6.0",
30 | "cryptiles": "^4.1.2",
31 | "express": "^4.13.3",
32 | "history": "^1.13.0",
33 | "hoek": "^5.0.4",
34 | "moment": "^2.22.1",
35 | "mongoose": "^4.2.5",
36 | "morgan": "^1.6.1",
37 | "parsejson": "0.0.3",
38 | "react": "^16.4.2",
39 | "react-bootstrap-table": "^4.3.1",
40 | "react-bootstrap-typeahead": "^3.1.3",
41 | "react-day-picker": "^7.1.9",
42 | "react-dom": "^16.2.0",
43 | "react-intl": "^2.7.1",
44 | "react-json-view": "^1.19.1",
45 | "react-jsonschema-form": "^1.0.3",
46 | "react-jsonschema-form-extras": "^0.9.16",
47 | "react-router": "^4.2.0",
48 | "react-router-dom": "^4.2.2",
49 | "react-rte": "^0.16.1",
50 | "request": "^2.65.0",
51 | "serve-favicon": "^2.3.0",
52 | "socket.io": "^1.3.7",
53 | "swig": "^1.4.2",
54 | "uglify-js": "^3.4.9",
55 | "underscore": "^1.8.3",
56 | "xml2js": "^0.4.15"
57 | },
58 | "devDependencies": {
59 | "babel-core": "^6.1.19",
60 | "babel-preset-es2015": "^6.24.1",
61 | "babel-preset-react": "^6.24.1",
62 | "babel-preset-stage-1": "^6.24.1",
63 | "babel-register": "^6.26.0",
64 | "babelify": "^7.2.0",
65 | "bower": "^1.6.5",
66 | "browserify": "^12.0.1",
67 | "gulp": "^3.9.0",
68 | "gulp-autoprefixer": "^3.1.0",
69 | "gulp-concat": "^2.6.0",
70 | "gulp-cssmin": "^0.1.7",
71 | "gulp-if": "^2.0.0",
72 | "gulp-less": "^3.0.3",
73 | "gulp-plumber": "^1.0.1",
74 | "gulp-sourcemaps": "^1.6.0",
75 | "gulp-uglify": "^1.4.2",
76 | "gulp-util": "^3.0.7",
77 | "vinyl-buffer": "^1.0.0",
78 | "vinyl-source-stream": "^1.1.0",
79 | "watchify": "^3.6.0"
80 | },
81 | "license": "MIT"
82 | }
83 |
--------------------------------------------------------------------------------
/README-zh.md:
--------------------------------------------------------------------------------
1 | [ENGLISH](https://github.com/fabric-lab/hyperledger-fabric-manager/blob/master/README.md) | [中文版](https://github.com/fabric-lab/hyperledger-fabric-manager/blob/master/README-zh.md)
2 |
3 | ## 发布版本
4 |
5 | - [v1.1.0.0-2018.10.12](https://github.com/fabric-lab/hyperledger-fabric-manager/releases/tag/V1.0.0) 内置fabric 1.1组件
6 | ## 使用说明
7 | - 下载并解压[v1.1.0.0-2018.10.12](https://github.com/fabric-lab/hyperledger-fabric-manager/releases/tag/V1.0.0)
8 | - 启动server程序
9 | - 访问http://localhost:8080
10 | ## 在线交流
11 | - QQ群 522367231
12 |
13 | ## 视频教程
14 | - [fabric manager bilibili 9分钟教程](https://www.bilibili.com/video/av33670267/)
15 | - [fabric manager baidu 9分钟教程](https://pan.baidu.com/s/1wSzHM3U6vNi2PxuZzSFYnQ)
16 |
17 | ## 项目简介
18 | - 1 快速创建hyperledger fabric网络。
19 | - 2 轻松搭建学习环境,无需使用docker容器。目前仅支持单机环境。
20 | - 3 多平台支持 windows,liunx,mac。
21 | - 4 支持hyperledger fabric 1.1,1.2,1.3版本
22 |
23 | ## 当前功能
24 | - 1 组织管理 MSP管理 证书管理 联盟管理 通道管理
25 | - 2 链码管理 添加链码 启动链码 停止链码
26 | - 3 Orderer管理 启动节点 停止节点 查看区块
27 | - 4 Peer管理 启动节点 停止节点 通道清单 加入通道 获取通道信息 安装链码 链码清单 初始化链码 调用链码 查询链码
28 |
29 | ## ubuntu下开发环境搭建
30 | - 1 获取项目
31 |
32 | $ cd $GOPATH/src/github.com
33 | $ mkdir fabric-lab && cd fabric-lab
34 | $ git clone https://github.com/fabric-lab/hyperledger-fabric-manager.git
35 | - 2 前端环境 ,预先安装node.js ,gulp,bower
36 |
37 | $ cd hyperledger-fabric-manager/app && npm install && gulp
38 |
39 | $ [17:14:56] Using gulpfile ~/work_dir/gopath/src/github.com/fabric-lab/hyperledger-fabric-manager/app/gulpfile.js
40 | $ [17:14:56] Starting 'styles'...
41 | $ [17:14:56] Starting 'vendor'...
42 | $ [17:14:56] Starting 'browserify-vendor'...
43 | $ [17:14:57] Starting 'watch'...
44 | $ [17:14:57] Finished 'watch' after 23 ms
45 | $ [17:14:58] Finished 'vendor' after 1.6 s
46 | $ [17:14:58] Finished 'styles' after 1.63 s
47 | $ [17:15:00] Finished 'browserify-vendor' after 3.24 s
48 | $ [17:15:00] Starting 'browserify-watch'...
49 |
50 | - 3 从[hyperledger-fabric releases](https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/)中获取对于平台的Orderer组件和peer组件,放到server/bin/linux(windows,drawin)/amd64/目录下。
51 |
52 | bin/
53 | └── linux
54 | └── amd64
55 | ├── orderer
56 | └── peer
57 | - 4 后端环境
58 |
59 | $ cd ../server && glide create && glide get
60 |
61 | 国内由于网络原因,可以使用gopm安装依赖包,部分依赖包不能下载,可以进入~/.gopm/repos/ 手动安装
62 |
63 | $ cd ../server && gopm get
64 |
65 | 最后启动后端服务
66 |
67 | $ go run main.go
68 |
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [ENGLISH](https://github.com/fabric-lab/hyperledger-fabric-manager/blob/master/README.md) | [中文版](https://github.com/fabric-lab/hyperledger-fabric-manager/blob/master/README-zh.md)
2 | ## Releases
3 |
4 | - [v1.1.0.0-2018.10.12](https://github.com/fabric-lab/hyperledger-fabric-manager/releases/tag/V1.0.1) Embed Hyperledger Fabric 1.1 component
5 |
6 | ## Get started
7 | - Download and Extract [v1.1.0.0-2018.10.12](https://github.com/fabric-lab/hyperledger-fabric-manager/releases/tag/V1.0.1)
8 | - Run server
9 | - Open http://localhost:8080
10 |
11 |
12 | ## Introduction
13 | - 1 Essential tool for you to create a hyperledger fabric network and manage easily.
14 | - 2 User friendly building environment, docker container not needed. Single machine mode only now.
15 | - 3 Cross-platform for windows,liunx,mac.
16 | - 4 Adapt for hyperledger fabric 1.1,1.2,1.3
17 |
18 | ## Features
19 | A few of the things you can do with hyperledger fabric manage:
20 | - 1 Organizations Manage,MSPs Auto Create,Certificates Manage,Consortiums Manage,Channels Manage.
21 | - 2 Add ChainCode,Enable Chaincode,Stop Chaincode.
22 | - 3 Orderers Manage,Run Orderer,Stop Orderer,View Block.
23 | - 4 Peers Manage, Run Peer, Stop Peer, List Channel, Join Channel, Get Channel Info, Install ChainCode, List ChainCode, Init ChainCode, Invoke ChainCode, Query Chaincode.
24 |
25 | ## Develop for Ubuntu user
26 | - 1 Clone the repository
27 |
28 | $ cd $GOPATH/src/github.com
29 | $ mkdir fabric-lab && cd fabric-lab
30 | $ git clone https://github.com/fabric-lab/hyperledger-fabric-manager.git
31 | - 2 Front-end ,depend on node.js,gulp,bower
32 |
33 | $ cd hyperledger-fabric-manager/app && npm install && gulp
34 |
35 | $ [17:14:56] Using gulpfile ~/work_dir/gopath/src/github.com/fabric-lab/hyperledger-fabric-manager/app/gulpfile.js
36 | $ [17:14:56] Starting 'styles'...
37 | $ [17:14:56] Starting 'vendor'...
38 | $ [17:14:56] Starting 'browserify-vendor'...
39 | $ [17:14:57] Starting 'watch'...
40 | $ [17:14:57] Finished 'watch' after 23 ms
41 | $ [17:14:58] Finished 'vendor' after 1.6 s
42 | $ [17:14:58] Finished 'styles' after 1.63 s
43 | $ [17:15:00] Finished 'browserify-vendor' after 3.24 s
44 | $ [17:15:00] Starting 'browserify-watch'...
45 |
46 | - 3 Get Orderer and Peer binary from [hyperledger-fabric releases](https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/),put in server/bin/linux(windows,drawin)/amd64/ directory。
47 |
48 | bin/
49 | └── linux
50 | └── amd64
51 | ├── orderer
52 | └── peer
53 | - 4 Back-end
54 |
55 | $ cd ../server && glide create && glide get
56 | $ go run main.go
57 |
58 |
--------------------------------------------------------------------------------
/app/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Route,Switch} from 'react-router-dom';
3 | import Home from './Home';
4 | import Navbar from './Navbar';
5 | import Footer from './Footer';
6 | import JsonForm from './JsonForm';
7 | import Orderer from './Orderer';
8 | import Peer from './Peer';
9 | import CertTable from './CertTable';
10 |
11 | import Organization from './Organization';
12 | import OrdererCard from './OrdererCard';
13 | import PeerCard from './PeerCard';
14 | import CertCard from './CertCard';
15 | import OrganizationCard from './OrganizationCard';
16 | import Consortium from './Consortium';
17 | import ConsortiumCard from './ConsortiumCard';
18 | import Channel from './Channel';
19 | import ChannelCard from './ChannelCard';
20 | import ChainCode from './ChainCode';
21 | import ChainCodeCard from './ChainCodeCard';
22 | import OrdererConsoleCard from './OrdererConsoleCard';
23 | import PeerConsoleCard from './PeerConsoleCard'
24 |
25 |
26 |
27 | class App extends React.Component {
28 | render() {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | // class App extends React.Component {
59 | // render() {
60 | // return (
61 | //
62 | // aaaaa
63 | //
64 | // );
65 | // }
66 | // }
67 |
68 | export default App;
--------------------------------------------------------------------------------
/app/components/OrganizationCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import JsonForm from './JsonForm';
4 | import {organizationSchema} from '../json_schema/organization'
5 | import { injectIntl } from 'react-intl';
6 |
7 |
8 | class OrganizationCard extends React.Component {
9 |
10 | constructor(props) {
11 | super(props);
12 | this.state = {
13 | formData:{},
14 | formMode:"edit"
15 | }
16 | }
17 |
18 | componentDidMount = () => {
19 | let that = this
20 | let data = JSON.parse(this.props.match.params.data);
21 | let {key,formMode} = data;
22 | this.setState({formMode:formMode});
23 | if(key){
24 | var url = 'api/entity/organizations/'+key;
25 | fetch(url,{
26 | method: 'get',
27 | mode: "cors",
28 | }).then(response=>{
29 | return response.json();
30 | }).then(function(data) {
31 | that.setState({formData:data});
32 | }).catch(function(e) {
33 | console.log("Oops, error");
34 | });
35 | }
36 | }
37 |
38 | render() {
39 | let that = this;
40 | const { intl } = this.props;
41 |
42 | const handleFormSubmit = ({formData}) => {
43 |
44 | var url = `api/entity/organizations/${formData.CommonName}`;
45 | fetch(url,{
46 | method: 'post',
47 | mode: "no-cors",
48 | body:JSON.stringify(formData)
49 | }).then(function(response) {
50 | return response;
51 | }).then(function(data) {
52 | that.props.history.push({
53 | pathname: '/organizations',
54 | });
55 | }).catch(function(e) {
56 | console.log("Oops, error");
57 | });
58 | }
59 |
60 |
61 |
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_organization'}) }
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | )
79 | }
80 | }
81 |
82 | export default injectIntl(OrganizationCard);
83 |
84 |
--------------------------------------------------------------------------------
/app/message/zh_CN.js:
--------------------------------------------------------------------------------
1 | const zh_CN = {
2 | able:"已启用",
3 | add_chaincode:"添加链码",
4 | add_channel:"添加通道",
5 | add_consortium:"添加联盟",
6 | add_orderer:"添加Orderer",
7 | add_organization:"添加组织",
8 | add_peer:"添加Peer",
9 | admin_msp:"管理员MSP",
10 | back:"返回",
11 | certificate:"证书",
12 | chaincode:"链码",
13 | chaincode_list:"链码清单",
14 | chaincode_manage:"链码管理",
15 | chaincode_name:"链码名称",
16 | channel:"通道",
17 | channel_desc:"通道描述",
18 | channel_list:"通道清单",
19 | channel_manage:"通道管理",
20 | channel_name:"通道名称",
21 | chaincode_port:"ChainCode端口",
22 | common_name:"公用名称",
23 | console_panel:"控制台",
24 | consortium_desc:"联盟描述",
25 | consortium_manage:"联盟管理",
26 | consortium_msps:"联盟MSP",
27 | consortium_name:"联盟名称",
28 | consortium_type:"联盟类型",
29 | consortium:"联盟",
30 | consortiums:"联盟",
31 | country:"国家",
32 | delete:"删除",
33 | desc_1:"最旧区块:-2 最新区块:-1 指定区块数:>=0",
34 | desc_2:"请先添加组织【{param1}】的Peer节点",
35 | desc_3:"请先启动orderer节点:【{param1}】",
36 | desc_4:"请先启动peer节点:【{param1}】",
37 | desc_5:"启用通道失败,请确认 1.对应Order节点是否已经启动。 2.已经存在同名通道",
38 | desc_6:"1 组织管理 MSP管理 证书管理 联盟管理 通道管理",
39 | desc_7:"2 链码管理 添加链码 启动链码 停止链码",
40 | desc_8:"3 Orderer管理 启动节点 停止节点 查看区块",
41 | desc_9:"4 Peer管理 启动节点 停止节点 通道清单 加入通道 获取通道信息 安装链码 链码清单 初始化链码 调用链码 查询链码",
42 | desc_10:"当前功能",
43 | desc_11:"3 多平台支持 windows,liunx,mac",
44 | desc_12:"2 轻松搭建环境,无需使用docker容器。目前仅支持单机环境。",
45 | desc_13:"1 学习超级账本(hyperledger fabric) 入门必备工具。",
46 | desc_14:"项目简介",
47 | desc_15:"超级账本管理端",
48 | disable:"未启用",
49 | enabled:"是否启用",
50 | enable_channel:"启用通道",
51 | event_port:"Event端口",
52 | get_channel_info:"获取通道信息",
53 | ip_address:"IP地址",
54 | init_chaincode:"初始化链码",
55 | init_code:"初始化语句",
56 | install_chaincode:"Install Chaincode",
57 | invoke_chaincode:"调用链码",
58 | invoke_code:"调用语句",
59 | is_enable:"是否启用",
60 | join_channel:"加入通道",
61 | keys:"密钥",
62 | ledger_type:"账本类型",
63 | language:"语言",
64 | locality:"城市",
65 | log:"日志",
66 | msp_id:"MSP ID",
67 | msp_name:"MSP名称",
68 | name:"名称",
69 | node_already_run:"节点已经启动",
70 | node_already_stop:"节点已经停止",
71 | node_stop_ok:"节点停止成功",
72 | node_must_run:"请先启动当前节点",
73 | node_name:"节点名称",
74 | node_type:"节点类型",
75 | operation:"操作",
76 | organization:"单位名称",
77 | organizations:"组织",
78 | organization_manage:"组织管理",
79 | orderer_manage:"Orderer节点",
80 | orderer_name:"Orderer名称",
81 | path:"路径",
82 | peer_manage:"Peer节点",
83 | peer_msp:"Peer Msp",
84 | peer_name:"Peer名称",
85 | peers:"Peer 节点",
86 | port:"端口",
87 | province:"省份",
88 | query_chaincode:"查询链码",
89 | query_code:"查询语句",
90 | role:"角色",
91 | run_chaincode:"运行链码",
92 | run_cmd:"执行命令",
93 | run_node:"启动节点",
94 | running:"运行中....",
95 | stop:"未运行",
96 | stop_chaincode:"停止链码",
97 | stop_node:"停止节点",
98 | submit:"提交",
99 | version:"版本",
100 | view:"查看",
101 | view_block:"查看区块",
102 | }
103 | export default zh_CN;
--------------------------------------------------------------------------------
/app/components/CertCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import JsonForm from './JsonForm';
4 | import {nameSchema,pemSchema,uiPemSchema,widgets} from '../json_schema/cert'
5 | import Tabs from '@material-ui/core/Tabs';
6 | import Tab from '@material-ui/core/Tab';
7 | import Typography from '@material-ui/core/Typography';
8 | import AppBar from '@material-ui/core/AppBar';
9 | import { injectIntl } from 'react-intl';
10 |
11 |
12 |
13 | class CertCard extends React.Component {
14 |
15 | constructor(props) {
16 | super(props);
17 | this.state = {
18 | activeTab:0,
19 | formData:{}
20 | }
21 | }
22 |
23 | componentDidMount = () => {
24 | let that = this
25 | let data = JSON.parse(this.props.match.params.data);
26 | let {commonName,caName} = data;
27 | if(commonName && caName){
28 | var url = 'api/organizations/'+commonName+"/"+caName;
29 | fetch(url,{
30 | method: 'get',
31 | }).then(response=>{
32 | return response.json();
33 | }).then(function(data) {
34 | let formData = {Cert:data.ca,Key:data.key}
35 | that.setState({formData:formData});
36 | }).catch(function(e) {
37 | console.log("Oops, error");
38 | });
39 | }
40 | }
41 |
42 | render() {
43 | const {intl} = this.props;
44 | const { activeTab } = this.state;
45 |
46 | let that = this;
47 | const handleFormSubmit = ({formData}) => {
48 | var url = 'api/certs';
49 | fetch(url,{
50 | method: 'post',
51 | body:JSON.stringify(formData)
52 | }).then(function(response) {
53 | return response;
54 | }).then(function(data) {
55 | that.props.history.push({
56 | pathname: '/certs',
57 | });
58 | }).catch(function(e) {
59 | console.log("Oops, error");
60 | });
61 | }
62 |
63 | const handleChange = (event, value) => {
64 | this.setState({ activeTab:value });
65 | };
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
73 |
74 |
{intl.formatMessage({id:"view"}) }
75 |
76 |
77 | this.props.history.goBack()} formData={this.state.formData} formMode="view" history={this.props.history} />
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | )
86 | }
87 | }
88 |
89 | export default injectIntl(CertCard);
90 |
91 |
--------------------------------------------------------------------------------
/app/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Card from '@material-ui/core/Card';
6 | import CardActions from '@material-ui/core/CardActions';
7 | import CardContent from '@material-ui/core/CardContent';
8 | import Button from '@material-ui/core/Button';
9 | import Typography from '@material-ui/core/Typography';
10 | import { injectIntl } from 'react-intl';
11 |
12 |
13 | const styles = {
14 | card: {
15 | minWidth: 275,
16 | },
17 | bullet: {
18 | display: 'inline-block',
19 | margin: '0 2px',
20 | transform: 'scale(0.8)',
21 | },
22 | title: {
23 | marginBottom: 16,
24 | fontSize: 14,
25 | },
26 | pos: {
27 | marginBottom: 12,
28 | },
29 | row: {
30 | marginTop: 12,
31 | }
32 | };
33 |
34 |
35 | class Home extends React.Component {
36 | constructor(props) {
37 | super(props);
38 |
39 | }
40 |
41 |
42 |
43 | render() {
44 | const { classes,intl } = this.props;
45 | const bull = •;
46 |
47 | return (
48 |
49 |
{intl.formatMessage({id:'desc_15'})}
50 |
51 |
52 |
53 |
54 | {intl.formatMessage({id:'desc_14'})}
55 |
56 |
57 | {intl.formatMessage({id:'desc_13'})}
58 |
59 |
60 | {intl.formatMessage({id:'desc_12'})}
61 |
62 |
63 | {intl.formatMessage({id:'desc_11'})}
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {intl.formatMessage({id:'desc_10'})}
73 |
74 |
75 |
76 | {intl.formatMessage({id:'desc_6'})}
77 |
78 |
79 | {intl.formatMessage({id:'desc_7'})}
80 |
81 |
82 | {intl.formatMessage({id:'desc_8'})}
83 |
84 |
85 | {intl.formatMessage({id:'desc_9'})}
86 |
87 |
88 |
89 |
90 |
91 | );
92 | }
93 | }
94 |
95 | Home.propTypes = {
96 | classes: PropTypes.object.isRequired,
97 | };
98 |
99 | export default withStyles(styles)(injectIntl(Home));
--------------------------------------------------------------------------------
/server/pkg/entity/peer.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "fmt"
5 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
7 | "io/ioutil"
8 | "path/filepath"
9 | "strings"
10 | )
11 |
12 | type Peer struct {
13 | Name string
14 | ListenAddress string
15 | ListenPort uint16
16 | ChainCodeListenPort uint16
17 | LocalMSPID string
18 | AdminMSPID string
19 | EventListenPort uint16
20 | }
21 |
22 | func (p *Peer) Create() error {
23 | Path(peerDir, p.Name)
24 | return nil
25 | }
26 |
27 | func (p *Peer) Exec(cmdInfo map[string]string) string {
28 | channelId := cmdInfo["ChannelId"]
29 | if channelId != "" {
30 |
31 | channel, err := getChannelByName(channelId)
32 | if err != nil {
33 | return err.Error()
34 | }
35 | cmdInfo["OrdererEndpoint"] = channel.OrdererEndpoint
36 | cmdInfo["OrdererName"] = channel.OrdererName
37 | }
38 |
39 | err := p.configCore(cmdInfo)
40 | if err != nil {
41 | return err.Error()
42 | }
43 | return ExecPeer(cmdInfo)
44 | }
45 |
46 | func (p *Peer) configCore(cmdInfo map[string]string) error {
47 | name := "core.yaml"
48 | cmd := cmdInfo["Cmd"]
49 | mspid := p.LocalMSPID
50 | if cmd == "CHANNEL_JOIN" || cmd == "CHAINCODE_INSTALL" || cmd == "CHAINCODE_INIT" {
51 | mspid = p.AdminMSPID
52 | }
53 | template := filepath.Join(templateDir, name)
54 | peerPath := filepath.Join(peerDir, p.Name)
55 |
56 | coreYml := filepath.Join(peerPath, name)
57 | b, err := ioutil.ReadFile(template)
58 | if err != nil {
59 | return err
60 | }
61 | str := string(b)
62 | str = strings.Replace(str, "$ID", p.Name, -1)
63 | listenAddress := fmt.Sprintf("%s:%d", p.ListenAddress, p.ListenPort)
64 | chainCodeListenPort := fmt.Sprintf("%s:%d", p.ListenAddress, p.ChainCodeListenPort)
65 | str = strings.Replace(str, "$LISTEN_ADDRESS", listenAddress, -1)
66 | str = strings.Replace(str, "$CHAINCODE_LISTEN_ADDRESS", chainCodeListenPort, -1)
67 | oname := strings.SplitN(p.LocalMSPID, ".", 2)[1]
68 | str = strings.Replace(str, "$LOCAL_MSP_ID", oname, -1)
69 | str = strings.Replace(str, "$FILE_SYSTEM_PATH", blockDir, -1)
70 | str = strings.Replace(str, "$LISTEN_PORT", fmt.Sprintf("%d",p.ListenPort), -1)
71 | str = strings.Replace(str, "$EVENT_LISTEN_PORT", fmt.Sprintf("%d",p.EventListenPort), -1)
72 |
73 | err = ioutil.WriteFile(coreYml, []byte(str), 0644)
74 | if err != nil {
75 | return err
76 | }
77 | msp, _ := getMspByName(mspid)
78 | err = util.Copy(msp.Path, peerPath)
79 | if err != nil {
80 | return err
81 | }
82 |
83 | return nil
84 | }
85 |
86 | func getPeerByName(cName string) (*Peer, error) {
87 | var p *Peer
88 | i, err := store.Bt.ViewByKey(peers, cName)
89 | if err != nil {
90 | return p, err
91 | }
92 | i = MapToEntity(i, peers)
93 | if p, ok := i.(*Peer); ok {
94 | return p, nil
95 | }
96 | return p, nil
97 | }
98 |
99 |
100 | func getPeerByLocalMSPId(localMSPID string) (*Peer, error) {
101 | var p *Peer
102 | is, err := store.Bt.View(peers)
103 | if err != nil {
104 | return p, err
105 | }
106 | for _, v := range is {
107 | v = MapToEntity(v, peers)
108 | if p, ok := v.(*Peer); ok {
109 | if(p.LocalMSPID == localMSPID){
110 | return p,nil
111 | }
112 | }
113 | }
114 | return nil,nil
115 | }
--------------------------------------------------------------------------------
/server/pkg/store/bolt.go:
--------------------------------------------------------------------------------
1 | package store
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "github.com/boltdb/bolt"
8 | "os"
9 | "path/filepath"
10 | )
11 |
12 | type Bolt struct {
13 | DB *bolt.DB
14 | }
15 |
16 | var Bt *Bolt
17 |
18 | func Init() *Bolt {
19 | config := "config"
20 | _, err := os.Stat(config)
21 | if err != nil {
22 | os.Mkdir(config,os.ModePerm)
23 | }
24 | db, err := bolt.Open(filepath.Join(config, "manager.db"), 0600, nil)
25 | if err == nil {
26 | Bt = &Bolt{DB: db}
27 | return Bt
28 | }
29 | return nil
30 | }
31 |
32 | func (b *Bolt) Add(root string, key string, data map[string]interface{}) error {
33 |
34 | return b.DB.Update(func(tx *bolt.Tx) error {
35 | _, kb, err := buckets(tx, root, key)
36 | if err != nil {
37 | return err
38 | }
39 | for k, v := range data {
40 | str, _ := v.(string)
41 | if err := kb.Put([]byte(k), []byte(str)); err != nil {
42 | return fmt.Errorf("failed puting %v key into %v keys bucket: %v", v, k, err)
43 | }
44 | }
45 | return nil
46 | })
47 | }
48 |
49 | func (b *Bolt) AddJson(root string, key string, json []byte) error {
50 |
51 | return b.DB.Update(func(tx *bolt.Tx) error {
52 | _, kb, err := buckets(tx, root, key)
53 | if err != nil {
54 | return err
55 | }
56 |
57 | if err := kb.Put([]byte(key), json); err != nil {
58 | return err
59 | }
60 |
61 | return nil
62 | })
63 | }
64 |
65 | func buckets(tx *bolt.Tx, root string, key string) (*bolt.Bucket, *bolt.Bucket, error) {
66 | rb, err := tx.CreateBucketIfNotExists([]byte(root))
67 | if err != nil {
68 | return nil, nil, fmt.Errorf("failed getting %v bucket: %v", root, err)
69 | }
70 | kb, err := rb.CreateBucketIfNotExists([]byte(key))
71 | if err != nil {
72 | return nil, nil, fmt.Errorf("failed getting %v key bucket: %v", root, err)
73 | }
74 |
75 | return rb, kb, nil
76 | }
77 |
78 | // func (b *Bolt) View(root string) ([]map[string]string,error) {
79 | // var records []map[string]string
80 | // err := b.DB.View(func(tx *bolt.Tx) error {
81 | // root := tx.Bucket([]byte(root))
82 | // c := root.Cursor()
83 |
84 | // for k, v := c.First(); k != nil; k, v = c.Next() {
85 | // record := make(map[string]string)
86 | // key := root.Bucket(k)
87 | // key.ForEach(func(k, v []byte) error {
88 | // record[string(k)] = string(v)
89 | // return nil
90 | // })
91 | // records = append(records,record)
92 | // fmt.Printf("key=%s, value=%s\n", k, v)
93 | // }
94 |
95 | // return nil
96 | // })
97 | // return records,err
98 | // }
99 |
100 | func (b *Bolt) View(root string) ([]interface{}, error) {
101 | records := make([]interface{}, 0)
102 | err := b.DB.View(func(tx *bolt.Tx) error {
103 | root := tx.Bucket([]byte(root))
104 | if root != nil {
105 | c := root.Cursor()
106 | for k, v := c.First(); k != nil; k, v = c.Next() {
107 | rbkt := root.Bucket(k)
108 | rbkt.ForEach(func(k, v []byte) error {
109 | var s interface{}
110 | json.Unmarshal(v, &s)
111 | records = append(records, s)
112 | return nil
113 | })
114 | fmt.Printf("key=%s, value=%s\n", k, v)
115 | }
116 | }
117 |
118 | return nil
119 | })
120 | return records, err
121 | }
122 |
123 | func (b *Bolt) ViewByKey(root string, key string) (interface{}, error) {
124 | var i interface{}
125 | err := b.DB.View(func(tx *bolt.Tx) error {
126 | rbkt := tx.Bucket([]byte(root))
127 | if rbkt != nil {
128 | bkt := rbkt.Bucket([]byte(key))
129 | if bkt != nil {
130 | _, v := bkt.Cursor().First()
131 | json.Unmarshal(v, &i)
132 | return nil
133 | }
134 | }
135 | return errors.New("Empty Entity")
136 |
137 | })
138 | return i, err
139 | }
140 |
141 | func (b *Bolt) DelByKey(root string, key string) error {
142 | return b.DB.Update(func(tx *bolt.Tx) error {
143 | root := tx.Bucket([]byte(root))
144 | return root.DeleteBucket([]byte(key))
145 | })
146 | }
147 |
--------------------------------------------------------------------------------
/server/pkg/entity/orderer.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "fmt"
5 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
7 | profileConfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
8 | ordererConfig "github.com/hyperledger/fabric/orderer/common/localconfig"
9 | yaml "gopkg.in/yaml.v2"
10 | "path/filepath"
11 | "strings"
12 | "time"
13 | )
14 |
15 | type Orderer struct {
16 | Name string
17 | LedgerType string
18 | OrdererType string
19 | ListenAddress string
20 | ListenPort uint16
21 | LocalMSPID string
22 | Consortiums []string
23 | }
24 |
25 | func (o *Orderer) Create() error {
26 | Path(OrdererDir, o.Name)
27 | return nil
28 | }
29 |
30 | func (o *Orderer) Exec(cmdInfo map[string]string) string {
31 | err := o.config(cmdInfo)
32 | if err != nil {
33 | return err.Error()
34 | }
35 | return ExecOrderer(cmdInfo)
36 | }
37 |
38 | func (o *Orderer) config(cmdInfo map[string]string) error {
39 |
40 | path := filepath.Join(OrdererDir, o.Name)
41 | profileBytes, err := o.configOrderProfile(path)
42 | if err != nil {
43 | return err
44 | }
45 |
46 | err = SimpleWrite(path, configtxyml, profileBytes)
47 | if err != nil {
48 | return err
49 | }
50 |
51 | orderBytes, err := o.configOrderer(path)
52 | if err != nil {
53 | return err
54 | }
55 | SimpleWrite(path, ordereryml, orderBytes)
56 |
57 | return nil
58 | }
59 |
60 | func (o *Orderer) configOrderProfile(path string) ([]byte, error) {
61 | ordererConfig := &profileConfig.Orderer{
62 | OrdererType: "solo",
63 | Addresses: []string{fmt.Sprintf("%s:%d", o.ListenAddress,o.ListenPort)},
64 | BatchTimeout: 2 * time.Second,
65 | BatchSize: profileConfig.BatchSize{
66 | MaxMessageCount: 10,
67 | AbsoluteMaxBytes: 10 * 1024 * 1024,
68 | PreferredMaxBytes: 512 * 1024,
69 | },
70 | }
71 | consortiumsConfig := make(map[string]*profileConfig.Consortium)
72 | for _, v := range o.Consortiums {
73 | orgs, err := configConsortiumOrgs(path, v)
74 | if err != nil {
75 | return nil, err
76 | }
77 | consortiumsConfig[v] = &profileConfig.Consortium{
78 | Organizations: orgs,
79 | }
80 |
81 | }
82 |
83 | profile := &profileConfig.Profile{
84 | Orderer: ordererConfig,
85 | Consortiums: consortiumsConfig,
86 | }
87 | Profiles := make(map[string]*profileConfig.Profile)
88 | Profiles["SampleSolo"] = profile
89 | topLevel := &profileConfig.TopLevel{Profiles: Profiles}
90 | b, err := yaml.Marshal(topLevel)
91 | if err != nil {
92 | return nil, err
93 | }
94 | s := string(b)
95 | s = strings.Replace(s, "MaxMessageSize", "MaxMessageCount", 1)
96 | return []byte(s), nil
97 | }
98 |
99 | func (o *Orderer) configOrderer(path string) ([]byte, error) {
100 | msp, err := getMspByName(o.LocalMSPID)
101 | if err != nil {
102 | return nil, err
103 | }
104 | util.Copy(msp.Path, path)
105 | general := ordererConfig.General{
106 | LedgerType: o.LedgerType,
107 | ListenAddress: o.ListenAddress,
108 | ListenPort: o.ListenPort,
109 | LocalMSPDir: defaultMspDir,
110 | LocalMSPID: o.LocalMSPID,
111 | SystemChannel: "system-channel",
112 | GenesisMethod: "provisional",
113 | GenesisProfile: "SampleSolo",
114 | LogLevel: "debug",
115 | }
116 | blockPath := filepath.Join(path, blockDir)
117 | fileLedger := ordererConfig.FileLedger{
118 | Location: blockPath,
119 | }
120 | topLevel := &ordererConfig.TopLevel{
121 | General: general,
122 | FileLedger: fileLedger,
123 | }
124 | return yaml.Marshal(topLevel)
125 | }
126 |
127 | func getOrderByName(oName string) (*Orderer, error) {
128 | var o *Orderer
129 | i, err := store.Bt.ViewByKey(orderers, oName)
130 | if err != nil {
131 | return o, err
132 | }
133 | i = MapToEntity(i, orderers)
134 | if o, ok := i.(*Orderer); ok {
135 | return o, nil
136 | }
137 | return o, nil
138 | }
139 |
--------------------------------------------------------------------------------
/app/components/ChainCodeCard.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import JsonForm from './JsonForm';
5 | import {schema,uiSchema,formData} from '../json_schema/chaincode'
6 | import { injectIntl } from 'react-intl';
7 |
8 |
9 | class ChainCodeCard extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | formData:{},
15 | formMode:"edit",
16 | schema:schema,
17 | isInit:false
18 | }
19 | }
20 |
21 | getData = () => {
22 | let that=this;
23 | var url = 'api/entity/peers';
24 | fetch(url,{
25 | method: 'get',
26 | }).then(response=>{
27 | return response.json();
28 | }).then(function(data) {
29 | let values = [];
30 | data.peers.forEach(function (peer) {
31 | values.push(peer.Name);
32 | });
33 | schema.properties.PeerName.enum = values;
34 | that.setState({schema:schema,isInit:true});
35 | }).catch(function(e) {
36 | console.log(e);
37 | });
38 | }
39 |
40 |
41 |
42 | componentWillMount = () => {
43 | this.getData()
44 | let that = this
45 | let data = JSON.parse(this.props.match.params.data);
46 | let {formMode,key} = data;
47 |
48 | if(key){
49 | var url = 'api/entity/chaincodes/'+key;
50 | fetch(url,{
51 | method: 'get',
52 | }).then(response=>{
53 | return response.json();
54 | }).then(function(data) {
55 | that.setState({formMode:formMode,formData:data});
56 | }).catch(function(e) {
57 | console.log("Oops, error");
58 | });
59 | }else{
60 | this.setState({formMode:formMode});
61 | }
62 | }
63 |
64 | render() {
65 | const {intl } = this.props;
66 |
67 | let that = this;
68 | const handleFormSubmit = ({formData}) => {
69 | // formData.OrderName = this.props.location.state.OrderName;
70 | // console.log(formData);
71 | let key = formData.Name+"."+formData.PeerName
72 | var url = `api/entity/chaincodes/${key}`;
73 | fetch(url,{
74 | method: 'post',
75 | mode: "no-cors",
76 | body:JSON.stringify(formData)
77 | }).then(function(response) {
78 | return response;
79 | }).then(function(data) {
80 | that.props.history.push({
81 | pathname: '/chaincode',
82 | });
83 | }).catch(function(e) {
84 | console.log("Oops, error");
85 | });
86 | }
87 | return (
88 |
89 |
90 |
91 |
92 |
93 |
94 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_chaincode'}) }
95 |
96 | { this.state.isInit && }
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | )
106 | }
107 | }
108 |
109 | export default injectIntl(ChainCodeCard);
110 |
111 |
--------------------------------------------------------------------------------
/server/pkg/entity/channel.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
7 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
8 | profileConfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
9 | yaml "gopkg.in/yaml.v2"
10 | "io/ioutil"
11 | "path/filepath"
12 | "strings"
13 | )
14 |
15 | type Channel struct {
16 | Name string
17 | Consortium string
18 | Desc string
19 | OrdererName string
20 | OrdererEndpoint string
21 | State string
22 | }
23 |
24 | func (c *Channel) Create() error {
25 | Path(channelDir, c.Name)
26 | o, err := getOrderByName(c.OrdererName)
27 | if err != nil {
28 | return err
29 | }
30 | c.OrdererEndpoint = fmt.Sprintf("%s:%d", o.ListenAddress, o.ListenPort)
31 | return nil
32 | }
33 |
34 | func (c *Channel) Exec(cmdInfo map[string]string) string {
35 | cmdInfo["ChannelId"] = c.Name
36 | cmdInfo["OrdererEndpoint"] = c.OrdererEndpoint
37 | err := c.config(cmdInfo)
38 | if err != nil {
39 | return err.Error()
40 | }
41 |
42 | str := ExecChannel(cmdInfo)
43 |
44 | //check output if exist "Error:",not accuracy,to do next
45 | index := strings.Index(str, "Error:")
46 | if index == -1 {
47 | c.State = "enable"
48 | } else {
49 | c.State = "disable"
50 | }
51 | b, err := json.Marshal(c)
52 | if err != nil {
53 | return err.Error()
54 | }
55 | err = store.Bt.AddJson(channels, c.Name, b)
56 | if err != nil {
57 | return err.Error()
58 | }
59 | return c.State
60 | }
61 |
62 | func (c *Channel) config(cmdInfo map[string]string) error {
63 |
64 | path := filepath.Join(channelDir, c.Name)
65 | profileBytes, err := c.configChannelProfile(path, c.Consortium)
66 | if err != nil {
67 | return err
68 | }
69 |
70 | err = SimpleWrite(path, configtxyml, profileBytes)
71 | if err != nil {
72 | return err
73 | }
74 |
75 | err = c.configCore(path)
76 | if err != nil {
77 | return err
78 | }
79 |
80 | return nil
81 | }
82 |
83 | func (c *Channel) configChannelProfile(path string, consortium string) ([]byte, error) {
84 | orgs, err := configConsortiumOrgs(path, consortium)
85 | if err != nil {
86 | return nil, err
87 | }
88 | application := &profileConfig.Application{
89 | Organizations: orgs,
90 | }
91 |
92 | profile := &profileConfig.Profile{
93 | Application: application,
94 | Consortium: consortium,
95 | }
96 | Profiles := make(map[string]*profileConfig.Profile)
97 | Profiles["SampleSingleMSPChannel"] = profile
98 | topLevel := &profileConfig.TopLevel{Profiles: Profiles}
99 | b, err := yaml.Marshal(topLevel)
100 | if err != nil {
101 | return nil, err
102 | }
103 | s := string(b)
104 | s = strings.Replace(s, "MaxMessageSize", "MaxMessageCount", 1)
105 | return []byte(s), nil
106 | }
107 |
108 | func (c *Channel) configCore(path string) error {
109 | consortium, err := getConsortiumByName(c.Consortium)
110 | if err != nil {
111 | return err
112 | }
113 | mspName := consortium.MspNames[0]
114 | oname := strings.SplitN(mspName, ".", 2)[1]
115 | localMSPID := fmt.Sprintf("%s.%s", "admin", oname)
116 | template := filepath.Join(templateDir, configyml)
117 | corepath := filepath.Join(path, coreYml)
118 | b, err := ioutil.ReadFile(template)
119 | if err != nil {
120 | return err
121 | }
122 | str := string(b)
123 | str = strings.Replace(str, "$LOCAL_MSP_ID", oname, -1)
124 | err = ioutil.WriteFile(corepath, []byte(str), 0644)
125 | if err != nil {
126 | return err
127 | }
128 | msp, _ := getMspByName(localMSPID)
129 | err = util.Copy(msp.Path, path)
130 | if err != nil {
131 | return err
132 | }
133 |
134 | return nil
135 | }
136 |
137 | func getChannelByName(cName string) (*Channel, error) {
138 | var c *Channel
139 | i, err := store.Bt.ViewByKey(channels, cName)
140 | if err != nil {
141 | return c, err
142 | }
143 | i = MapToEntity(i, channels)
144 | if c, ok := i.(*Channel); ok {
145 | return c, nil
146 | }
147 | return c, nil
148 | }
149 |
--------------------------------------------------------------------------------
/server/pkg/api.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/entity"
6 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
7 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
8 | "github.com/gin-gonic/gin"
9 | "strings"
10 | )
11 |
12 | func GetEntitys(c *gin.Context) {
13 | en := c.Param("entity")
14 | ens := strings.Split(en, ",")
15 | entitys := make(map[string][]interface{})
16 | for _, v := range ens {
17 | records, err := store.Bt.View(v)
18 | if err != nil {
19 | c.JSON(500, gin.H{"Error": err.Error()})
20 | return
21 | }
22 |
23 | entitys[v] = records
24 | var newRecords []interface{}
25 | for _, r := range records {
26 | e := entity.MapToEntity(r, v)
27 | if g, ok := e.(entity.Get); ok {
28 | g.GetEntity()
29 | newRecords = append(newRecords, g)
30 | }
31 | }
32 | if newRecords != nil {
33 | entitys[v] = newRecords
34 | }
35 | }
36 | c.JSON(200, entitys)
37 | }
38 |
39 | func GetEntity(c *gin.Context) {
40 | en := c.Param("entity")
41 | id := c.Param("id")
42 | record, err := store.Bt.ViewByKey(en, id)
43 | if err != nil {
44 | c.JSON(500, gin.H{"Error": err.Error()})
45 | return
46 | }
47 | c.JSON(200, record)
48 | return
49 |
50 | }
51 |
52 | func GetNodeState(c *gin.Context) {
53 | en := c.Param("entity")
54 | id := c.Param("id")
55 | cache := util.Caches.Get(en + "." + id)
56 | if cache != nil {
57 | c.JSON(200, gin.H{"state": "running"})
58 | return
59 | }
60 | c.JSON(200, gin.H{"state": "stop"})
61 | return
62 | }
63 |
64 | func CreateEntity(c *gin.Context) {
65 | en := c.Param("entity")
66 | id := c.Param("id")
67 | var i interface{}
68 | c.BindJSON(&i)
69 | e := entity.MapToEntity(i, en)
70 | if a, ok := e.(entity.Action); ok {
71 | a.Create()
72 | }
73 | b, _ := json.Marshal(e)
74 |
75 | err := store.Bt.AddJson(en, id, b)
76 | if err != nil {
77 | c.JSON(500, gin.H{"Error": err.Error()})
78 | return
79 | }
80 |
81 | c.JSON(200, gin.H{})
82 | }
83 |
84 | func DelEntity(c *gin.Context) {
85 | en := c.Param("entity")
86 | id := c.Param("id")
87 | err := store.Bt.DelByKey(en, id)
88 | if err != nil {
89 | c.JSON(500, gin.H{"Error": err.Error()})
90 | return
91 | }
92 | records, err := store.Bt.View(en)
93 | if err != nil {
94 | c.JSON(500, gin.H{"Error": err.Error()})
95 | return
96 | }
97 |
98 | c.JSON(200, records)
99 | }
100 |
101 | func ExecCMD(c *gin.Context) {
102 | en := c.Param("entity")
103 | id := c.Param("id")
104 | var cmd map[string]string
105 | c.BindJSON(&cmd)
106 |
107 | e, err := store.Bt.ViewByKey(en, id)
108 | if err != nil {
109 | c.JSON(500, gin.H{"Error": err.Error()})
110 | return
111 | }
112 | e = entity.MapToEntity(e, en)
113 |
114 | var res string
115 |
116 | if a, ok := e.(entity.CMD); ok {
117 | res = a.Exec(cmd)
118 | }
119 | cache := util.Caches.Get(en + "." + id)
120 | state := "stop"
121 | if cache != nil {
122 | state = "running"
123 | }
124 | c.JSON(200, gin.H{"msg": res, "state": state})
125 | }
126 |
127 | func GetCert(c *gin.Context) {
128 |
129 | id := c.Param("id")
130 | caName := c.Param("ca")
131 |
132 | e, err := store.Bt.ViewByKey("organizations", id)
133 | if err != nil {
134 | c.JSON(500, gin.H{"Error": err.Error()})
135 | return
136 | }
137 | e = entity.MapToEntity(e, "organizations")
138 | if o, ok := e.(*entity.Organization); ok {
139 |
140 | ca, key, err := entity.GetCA(caName, *o)
141 | if err != nil {
142 | c.JSON(500, gin.H{"Error": err.Error()})
143 | return
144 | }
145 | ca.SignCert.Raw = []byte{}
146 | ca.SignCert.RawTBSCertificate = []byte{}
147 | ca.SignCert.RawSubjectPublicKeyInfo = []byte{}
148 | ca.SignCert.RawSubject = []byte{}
149 | ca.SignCert.RawIssuer = []byte{}
150 | ca.SignCert.PublicKey = []byte{}
151 |
152 | cert, _ := json.Marshal(ca.SignCert)
153 | c.JSON(200, gin.H{
154 | "ca": string(cert),
155 | "key": key,
156 | })
157 | } else {
158 | if err != nil {
159 | c.JSON(500, gin.H{"Error": "error"})
160 | return
161 | }
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/app/components/ChannelCard.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import JsonForm from './JsonForm';
5 | import {schema,uiSchema,formData} from '../json_schema/channel'
6 | import { injectIntl } from 'react-intl';
7 |
8 |
9 | class ChannelCard extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | formData:{},
15 | formMode:"edit",
16 | schema:schema,
17 | isInit:false
18 | }
19 | }
20 |
21 | getData = () => {
22 | let that=this;
23 | var url = 'api/entity/orderers,consortiums';
24 | fetch(url,{
25 | method: 'get',
26 | }).then(response=>{
27 | return response.json();
28 | }).then(function(data) {
29 |
30 | let values = [];
31 | data.orderers.forEach(function (orderer) {
32 | values.push(orderer.Name);
33 | });
34 | schema.properties.OrdererName.enum = values;
35 | values = [];
36 | data.consortiums.forEach(function (consortium) {
37 | values.push(consortium.Name);
38 | });
39 | schema.properties.Consortium.enum = values;
40 | that.setState({schema:schema,isInit:true});
41 | }).catch(function(e) {
42 | console.log(e);
43 | });
44 | }
45 |
46 |
47 |
48 | componentWillMount = () => {
49 | this.getData();
50 | let that = this
51 | let data = JSON.parse(this.props.match.params.data);
52 | let {formMode,key} = data;
53 |
54 | if(key){
55 | var url = 'api/entity/channels/'+key;
56 | fetch(url,{
57 | method: 'get',
58 | }).then(response=>{
59 | return response.json();
60 | }).then(function(data) {
61 | that.setState({formMode:formMode,formData:data});
62 | }).catch(function(e) {
63 | console.log("Oops, error");
64 | });
65 | }else{
66 | this.setState({formMode:formMode});
67 | }
68 | }
69 |
70 | render() {
71 | const {intl } = this.props;
72 |
73 | let that = this;
74 | const handleFormSubmit = ({formData}) => {
75 | // formData.OrderName = this.props.location.state.OrderName;
76 | // console.log(formData);
77 | var url = `api/entity/channels/${formData.Name}`;
78 | fetch(url,{
79 | method: 'post',
80 | mode: "no-cors",
81 | body:JSON.stringify(formData)
82 | }).then(function(response) {
83 | return response;
84 | }).then(function(data) {
85 | that.props.history.push({
86 | pathname: '/channel',
87 | });
88 | }).catch(function(e) {
89 | console.log("Oops, error");
90 | });
91 | }
92 | return (
93 |
94 |
95 |
96 |
97 |
98 |
99 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_channel'}) }
100 |
101 | { this.state.isInit && }
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | )
111 | }
112 | }
113 |
114 | export default injectIntl(ChannelCard);
115 |
116 |
--------------------------------------------------------------------------------
/app/message/en_US.js:
--------------------------------------------------------------------------------
1 | const en_US = {
2 | able:"Yes",
3 | add_chaincode:"Add ChainCode",
4 | add_channel:"Add Channel",
5 | add_consortium:"Add Consortium",
6 | add_orderer:"Add Orderer",
7 | add_organization:"Add Organization",
8 | add_peer:"Add Peer",
9 | admin_msp:"Admin MSP",
10 | back:"Back",
11 | block_num:"Block Num",
12 | certificate:"Certificate",
13 | chaincode:"ChainCode",
14 | chaincode_list:"ChainCode List",
15 | chaincode_manage:"Chaincodes",
16 | chaincode_name:"ChainCode Name",
17 | channel:"Channels",
18 | channel_desc:"Channel Description",
19 | channel_list:"Channel List",
20 | channel_manage:"Channels",
21 | channel_name:"Channel Name",
22 | chaincode_port:"ChainCode Port",
23 | common_name:"Common Name",
24 | console_panel:"Console Panel",
25 | consortium_desc:"Consortium Description",
26 | consortium_manage:"Consortiums",
27 | consortium_msps:"Consortium Msp",
28 | consortium_name:"Consortium Name",
29 | consortium_type:"Consortium Type",
30 | consortium:"Consortium",
31 | consortiums:"Consortiums",
32 | country:"Country",
33 | delete:"Delete",
34 | desc_1:"Oldest:-2 Newest:-1 Specify:>=0",
35 | desc_2:"Please create peer node of {param1} first",
36 | desc_3:"Please run orderer node first:【{param1}】",
37 | desc_4:"Please run peer node first:【{param1}】",
38 | desc_5:"Fail to enble the channel,please check :1 whether the node has been run according to the order. 2 whether already exists a channel with the same name.",
39 | desc_6:"1 Organizations Manage,MSPs Auto Create,Certificates Manage,Consortiums Manage,Channels Manage.",
40 | desc_7:"2 Add ChainCode,Enable Chaincode,Stop Chaincode.",
41 | desc_8:"3 Orderers Manage,Run Orderer,Stop Orderer,View Block.",
42 | desc_9:"4 Peers Manage, Run Peer, Stop Peer, List Channel, Join Channel, Get Channel Info, Install ChainCode, List ChainCode, Init ChainCode, Invoke ChainCode, Query Chaincode.",
43 | desc_10:"Functionalities",
44 | desc_11:"3 Cross-platform for windows,liunx,mac.",
45 | desc_12:"2 User friendly building environment, docker container not needed. Single machine mode only.",
46 | desc_13:"1 Essential tool for you to start learning hyperledger fabric.",
47 | desc_14:"Introduction",
48 | desc_15:"Simple Hyperledger Fabric Manage Tool",
49 | disable:"No",
50 | enabled:"Enabled",
51 | enable_channel:"Enabled",
52 | event_port:"Event Port",
53 | get_channel_info:"Get Channel Info",
54 | ip_address:"IP Address",
55 | init_code:"Init Code",
56 | init_chaincode:"Init ChainCode",
57 | install_chaincode:"Install ChainCode",
58 | invoke_chaincode:"Invoke ChainCode",
59 | invoke_code:"Invoke Code",
60 | is_enable:"",
61 | join_channel:"Join Channel",
62 | keys:"Keys",
63 | ledger_type:"Ledger Type",
64 | language:"Language",
65 | locality:"Locality",
66 | log:"Log",
67 | msp_id:"MSP ID",
68 | msp_name:"MSP Name",
69 | name:"Name",
70 | node_already_run:"Node has been run",
71 | node_already_stop:"Node has been stopped",
72 | node_stop_ok:"Success Stop",
73 | node_must_run:"Current node must run first",
74 | node_name:"Node Name",
75 | node_type:"Node Type",
76 | operation:"Operation",
77 | organization:"Organization",
78 | organizations:"Organizations",
79 | organization_manage:"Organizations",
80 | orderer_manage:"Orderers",
81 | orderer_name:"Orderer Name",
82 | path:"Path",
83 | peer_manage:"Peers",
84 | peer_msp:"Peer Msp",
85 | peer_name:"Peer Name",
86 | peers:"Peers",
87 | port:"Port",
88 | province:"Province",
89 | query_chaincode:"Query ChainCode",
90 | query_code:"Query Code",
91 | role:"Role",
92 | run_chaincode:"Run ChainCode",
93 | run_cmd:"Run Cmd",
94 | run_node:"Run Node",
95 | running:"running....",
96 | stop:"not running...",
97 | stop_chaincode:"Stop ChainCode",
98 | stop_node:"Stop Node",
99 | submit:"Submit",
100 | version:"Version",
101 | view:"View",
102 | view_block:"View Block",
103 | }
104 | export default en_US;
--------------------------------------------------------------------------------
/app/components/PeerCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import JsonForm from './JsonForm';
4 | import {schema,uiSchema,formData} from '../json_schema/peer'
5 | import { injectIntl } from 'react-intl';
6 |
7 |
8 | class PeerCard extends React.Component {
9 |
10 | constructor(props) {
11 | super(props);
12 | this.state = {
13 | formData:{},
14 | formMode:"edit",
15 | schema:schema,
16 | isInit:false
17 | }
18 | }
19 |
20 | getMsp = () => {
21 | let that=this;
22 | var url = 'api/entity/organizations';
23 | fetch(url,{
24 | method: 'get',
25 | }).then(response=>{
26 | return response.json();
27 | }).then(function(data) {
28 |
29 | let localMsps = [];
30 | let adminMsps = [];
31 |
32 | data.organizations.forEach(function (organization) {
33 | if(organization.MSPs){
34 | organization.MSPs.forEach(
35 | function (msp) {
36 | if(msp.Type=="peer" && msp.Role=="peer"){
37 | localMsps.push(msp.Name);
38 | }else if(msp.Type=="peer" && msp.Role=="admin"){
39 | adminMsps.push(msp.Name);
40 | }
41 |
42 | }
43 | )
44 | }
45 | });
46 | schema.properties.LocalMSPID.enum = localMsps;
47 | schema.properties.AdminMSPID.enum = adminMsps;
48 | that.setState({schema:schema,isInit:true});
49 | }).catch(function(e) {
50 | console.log(e);
51 | });
52 | }
53 |
54 | componentDidMount = () => {
55 | this.getMsp();
56 | let that = this
57 | let data = JSON.parse(this.props.match.params.data);
58 | let {formMode,key} = data;
59 | if(key){
60 | var url = 'api/entity/peers/'+key;
61 | fetch(url,{
62 | method: 'get',
63 | }).then(response=>{
64 | return response.json();
65 | }).then(function(data) {
66 | that.setState({formData:data,formMode:formMode});
67 | }).catch(function(e) {
68 | console.log(e)
69 | });
70 | }else{
71 | this.setState({formMode:formMode});
72 |
73 | }
74 | }
75 |
76 | render() {
77 | let that = this;
78 | const { intl } = this.props;
79 |
80 | const handleFormSubmit = ({formData}) => {
81 | var url = `api/entity/peers/${formData.Name}`;
82 | fetch(url,{
83 | method: 'post',
84 | body:JSON.stringify(formData)
85 | }).then(function(response) {
86 | return response;
87 | }).then(function(data) {
88 | that.props.history.push({
89 | pathname: '/peer',
90 | });
91 | }).catch(function(e) {
92 | console.log("Oops, error");
93 | });
94 | }
95 | return (
96 |
97 |
98 |
99 |
100 |
101 |
102 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_peer'}) }
103 |
104 | { this.state.isInit && }
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | )
114 | }
115 | }
116 |
117 | export default injectIntl(PeerCard);
118 |
119 |
--------------------------------------------------------------------------------
/app/components/OrdererCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import JsonForm from './JsonForm';
4 | import {schema,uiSchema,formData} from '../json_schema/orderer'
5 | import { injectIntl } from 'react-intl';
6 |
7 |
8 | class OrdererCard extends React.Component {
9 |
10 | constructor(props) {
11 | super(props);
12 | this.state = {
13 | formData:{},
14 | formMode:"edit",
15 | schema:schema,
16 | isInit:false
17 | }
18 | }
19 |
20 | getData = () => {
21 | let that=this;
22 | var url = 'api/entity/organizations,consortiums';
23 | fetch(url,{
24 | method: 'get',
25 | }).then(response=>{
26 | return response.json();
27 | }).then(function(data) {
28 |
29 | let values = [];
30 | data.organizations.forEach(function (organization) {
31 | if(organization.MSPs){
32 | organization.MSPs.forEach(
33 | function (msp) {
34 | if(msp.Type=="orderer"){
35 | values.push(msp.Name);
36 | }
37 |
38 | }
39 | )
40 | }
41 | });
42 | schema.properties.LocalMSPID.enum = values;
43 | values = [];
44 | data.consortiums.forEach(function (consortium) {
45 | values.push(consortium.Name);
46 | });
47 | schema.properties.Consortiums.items.enum = values;
48 | that.setState({schema:schema,isInit:true});
49 | }).catch(function(e) {
50 | console.log(e);
51 | });
52 | }
53 |
54 |
55 |
56 | componentWillMount = () => {
57 | this.getData();
58 | let that = this
59 | let data = JSON.parse(this.props.match.params.data);
60 | let {formMode,key} = data;
61 |
62 | if(key){
63 | var url = 'api/entity/orderers/'+key;
64 | fetch(url,{
65 | method: 'get',
66 | }).then(response=>{
67 | return response.json();
68 | }).then(function(data) {
69 | that.setState({formData:data,formMode:formMode});
70 | }).catch(function(e) {
71 | console.log("Oops, error");
72 | });
73 | }else {
74 | this.setState({formMode:formMode});
75 | }
76 | }
77 |
78 | render() {
79 | let that = this;
80 | const { intl } = this.props;
81 | const handleFormSubmit = ({formData}) => {
82 | var url = `api/entity/orderers/${formData.Name}`;
83 | fetch(url,{
84 | method: 'post',
85 | body:JSON.stringify(formData)
86 | }).then(function(response) {
87 | return response;
88 | }).then(function(data) {
89 | that.props.history.push({
90 | pathname: '/orderer',
91 | });
92 | }).catch(function(e) {
93 | console.log("Oops, error");
94 | });
95 | }
96 | return (
97 |
98 |
99 |
100 |
101 |
102 |
103 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_orderer'}) }
104 |
105 | { this.state.isInit && }
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | )
115 | }
116 | }
117 |
118 | export default injectIntl(OrdererCard);
119 |
120 |
--------------------------------------------------------------------------------
/app/components/ConsortiumCard.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import JsonForm from './JsonForm';
5 | import {schema,uiSchema,formData} from '../json_schema/consortium'
6 | import { injectIntl } from 'react-intl';
7 |
8 |
9 | class ConsortiumCard extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | formData:{},
15 | formMode:"edit",
16 | schema:schema,
17 | isInit:false
18 | }
19 | }
20 |
21 | getMsp = () => {
22 | let that=this;
23 | var url = 'api/entity/organizations';
24 | fetch(url,{
25 | method: 'get',
26 | }).then(response=>{
27 | return response.json();
28 | }).then(function(data) {
29 | let names = [];
30 | let values = [];
31 | data.organizations.forEach(function (organization) {
32 | if(organization.MSPs){
33 | organization.MSPs.forEach(
34 | function (msp) {
35 | if(msp.Type=="peer" && msp.Role=="peer"){
36 | names.push(organization.Organization+" "+msp.Name);
37 | values.push(msp.Name);
38 | }
39 |
40 | }
41 | )
42 | }
43 | });
44 | schema.properties.MspNames.items.enum = values;
45 | schema.properties.MspNames.items.enumNames = names;
46 | that.setState({schema:schema,isInit:true});
47 | }).catch(function(e) {
48 | console.log("Oops, error");
49 | });
50 | }
51 |
52 | componentWillMount = () => {
53 | this.getMsp();
54 | let that = this
55 | let data = JSON.parse(this.props.match.params.data);
56 | let {formMode,key} = data;
57 |
58 | if(key){
59 | var url = 'api/entity/consortiums/'+key;
60 | fetch(url,{
61 | method: 'get',
62 | }).then(response=>{
63 | return response.json();
64 | }).then(function(data) {
65 | that.setState({formMode:formMode,formData:data});
66 | }).catch(function(e) {
67 | console.log("Oops, error");
68 | });
69 | }else{
70 | this.setState({formMode:formMode});
71 | }
72 | }
73 |
74 | render() {
75 | const { intl } = this.props;
76 | let that = this;
77 | const handleFormSubmit = ({formData}) => {
78 | // formData.OrderName = this.props.location.state.OrderName;
79 | // console.log(formData);
80 | var url = `api/entity/consortiums/${formData.Name}`;
81 | fetch(url,{
82 | method: 'post',
83 | body:JSON.stringify(formData)
84 | }).then(function(response) {
85 | return response;
86 | }).then(function(data) {
87 | that.props.history.push({
88 | pathname: '/consortium',
89 | });
90 | }).catch(function(e) {
91 | console.log("Oops, error");
92 | });
93 | }
94 | return (
95 |
96 |
97 |
98 |
99 |
100 |
101 |
{this.state.formMode=="view"?intl.formatMessage({id:'view'}):intl.formatMessage({id:'add_consortium'}) }
102 |
103 | { this.state.isInit && }
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | )
113 | }
114 | }
115 |
116 | export default injectIntl(ConsortiumCard);
117 |
118 |
--------------------------------------------------------------------------------
/app/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var gutil = require('gulp-util');
3 | var gulpif = require('gulp-if');
4 | var autoprefixer = require('gulp-autoprefixer');
5 | var cssmin = require('gulp-cssmin');
6 | var less = require('gulp-less');
7 | var concat = require('gulp-concat');
8 | var plumber = require('gulp-plumber');
9 | var buffer = require('vinyl-buffer');
10 | var source = require('vinyl-source-stream');
11 | var babelify = require('babelify');
12 | var browserify = require('browserify');
13 | var watchify = require('watchify');
14 | var uglify = require('gulp-uglify');
15 | var sourcemaps = require('gulp-sourcemaps');
16 |
17 | var production = process.env.NODE_ENV === 'production';
18 |
19 | var dependencies = [
20 | 'alt',
21 | 'react',
22 | 'react-dom',
23 | 'react-router',
24 | 'underscore'
25 | ];
26 |
27 | /*
28 | |--------------------------------------------------------------------------
29 | | Combine all JS libraries into a single file for fewer HTTP requests.
30 | |--------------------------------------------------------------------------
31 | */
32 | gulp.task('vendor', function() {
33 | return gulp.src([
34 | 'bower_components/jquery/dist/jquery.js',
35 | 'bower_components/bootstrap/dist/js/bootstrap.js',
36 | 'bower_components/magnific-popup/dist/jquery.magnific-popup.js',
37 | 'bower_components/toastr/toastr.js'
38 | ]).pipe(concat('vendor.js'))
39 | .pipe(gulpif(production, uglify({ mangle: false })))
40 | .pipe(gulp.dest('../server/static/js'));
41 | });
42 |
43 | /*
44 | |--------------------------------------------------------------------------
45 | | Compile third-party dependencies separately for faster performance.
46 | |--------------------------------------------------------------------------
47 | */
48 | gulp.task('browserify-vendor', function() {
49 | return browserify()
50 | .require(dependencies)
51 | .bundle()
52 | .pipe(source('vendor.bundle.js'))
53 | .pipe(buffer())
54 | .pipe(gulpif(production, uglify({ mangle: false })))
55 | .pipe(gulp.dest('../server/static/js'));
56 | });
57 |
58 | /*
59 | |--------------------------------------------------------------------------
60 | | Compile only project files, excluding all third-party dependencies.
61 | |--------------------------------------------------------------------------
62 | */
63 | gulp.task('browserify', ['browserify-vendor'], function() {
64 | return browserify({ entries: 'main.js', debug: true })
65 | .external(dependencies)
66 | .transform(babelify, { presets: ['es2015', 'react', "stage-1"] })
67 | .bundle()
68 | .pipe(source('bundle.js'))
69 | .pipe(buffer())
70 | .pipe(sourcemaps.init({ loadMaps: true }))
71 | .pipe(gulpif(production, uglify({ mangle: false })))
72 | .pipe(sourcemaps.write('.'))
73 | .pipe(gulp.dest('../server/static/js'));
74 | });
75 |
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Same as browserify task, but will also watch for changes and re-compile.
79 | |--------------------------------------------------------------------------
80 | */
81 | gulp.task('browserify-watch', ['browserify-vendor'], function() {
82 | var bundler = watchify(browserify({ entries: 'main.js', debug: true }, watchify.args));
83 | bundler.external(dependencies);
84 | bundler.transform(babelify, { presets: ['es2015', 'react','stage-1'] });
85 | bundler.on('update', rebundle);
86 | return rebundle();
87 |
88 | function rebundle() {
89 | var start = Date.now();
90 | return bundler.bundle()
91 | .on('error', function(err) {
92 | gutil.log(gutil.colors.red(err.toString()));
93 | })
94 | .on('end', function() {
95 | gutil.log(gutil.colors.green('Finished rebundling in', (Date.now() - start) + 'ms.'));
96 | })
97 | .pipe(source('bundle.js'))
98 | .pipe(buffer())
99 | .pipe(sourcemaps.init({ loadMaps: true }))
100 | .pipe(sourcemaps.write('.'))
101 | .pipe(gulp.dest('../server/static/js/'));
102 | }
103 | });
104 |
105 | /*
106 | |--------------------------------------------------------------------------
107 | | Compile LESS stylesheets.
108 | |--------------------------------------------------------------------------
109 | */
110 | gulp.task('styles', function() {
111 | return gulp.src('stylesheets/main.less')
112 | .pipe(plumber())
113 | .pipe(less())
114 | .pipe(autoprefixer())
115 | .pipe(gulpif(production, cssmin()))
116 | .pipe(gulp.dest('../server/static/css'));
117 | });
118 |
119 | gulp.task('watch', function() {
120 | gulp.watch('stylesheets/**/*.less', ['styles']);
121 | });
122 |
123 | gulp.task('default', ['styles', 'vendor', 'browserify-watch', 'watch']);
124 | gulp.task('build', ['styles', 'vendor', 'browserify']);
125 |
--------------------------------------------------------------------------------
/app/components/MspTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import { injectIntl } from 'react-intl';
25 |
26 |
27 | const columnData = [
28 | { id: 'Name', numeric: true, disablePadding: false, label: 'msp_name' },
29 | { id: 'Path', numeric: true, disablePadding: false, label: 'path' },
30 | { id: 'Type', numeric: true, disablePadding: false, label: 'node_type' },
31 | { id: 'Role', numeric: true, disablePadding: false, label: 'role' }
32 | ];
33 |
34 |
35 | const styles = theme => ({
36 | root: {
37 | width: '100%',
38 | marginTop: theme.spacing.unit * 3,
39 | },
40 | table: {
41 | minWidth: 1020,
42 | },
43 | tableWrapper: {
44 | overflowX: 'auto',
45 | },
46 | });
47 |
48 | class MspTable extends React.Component {
49 | constructor(props, context) {
50 | super(props, context);
51 |
52 | this.state = {
53 | order: 'asc',
54 | orderBy: 'Name',
55 | data: [],
56 | msps: [],
57 | };
58 | }
59 |
60 | componentWillReceiveProps(newProps) {
61 | if (this.state.data !== newProps.data||
62 | this.state.selected !== newProps.selected) {
63 | let data = newProps.data;
64 | let selected = newProps.selected;
65 | if (data[selected] != undefined) {
66 | let msps = data[selected].MSPs;
67 |
68 | this.setState({ data: newProps.data, msps: msps });
69 | } else {
70 | this.setState({ data: [], msps: [] });
71 | }
72 | }
73 | }
74 |
75 | handleRequestSort = (event, property) => {
76 | const orderBy = property;
77 | let order = 'desc';
78 |
79 | if (this.state.orderBy === property && this.state.order === 'desc') {
80 | order = 'asc';
81 | }
82 |
83 | const msps =
84 | order === 'desc'
85 | ? this.state.msps.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
86 | : this.state.msps.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
87 |
88 | this.setState({ msps, order, orderBy });
89 | };
90 |
91 | render() {
92 |
93 | const { classes, history, data, selected } = this.props;
94 | const { order, orderBy, msps } = this.state;
95 |
96 |
97 | return (
98 |
99 |
100 |
101 |
102 |
103 |
104 |
110 |
111 | {msps.map(n => {
112 | return (
113 |
116 | {n.Name}
117 | {n.Path}
118 | {n.Type}
119 | {n.Role}
120 |
121 |
122 |
123 | );
124 | })}
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | );
135 | }
136 | }
137 |
138 | MspTable.propTypes = {
139 | classes: PropTypes.object.isRequired,
140 | };
141 |
142 | export default withStyles(styles)(MspTable);
--------------------------------------------------------------------------------
/server/pkg/certificate/certificate_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package certificate
16 |
17 | import (
18 | "encoding/pem"
19 | "fmt"
20 | "reflect"
21 | "testing"
22 | )
23 |
24 | var (
25 | pemKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
26 | MIIEpQIBAAKCAQEAxGJ/tXy7ItxDpI9RLb0L/mkxGpw+BodijFoNixq0QEzxsc+t
27 | Lr+3czb00a0B/kzObV0AQYwXFqdJ+qb7jZXc3To6iALDw3ghWNgfaBeIKPueY6n7
28 | 7DU4ZpzjLy8DhlWO44Vx6CZjByr6qmGh3TnDqD8sVEFPZlattgHy4KfTmc0abk4b
29 | wlJ4lvpXBshl1SYsMgpyZrTC1mggcu+xw98EcvLollYKvW4WyYtTbwBo5S8c/hVt
30 | m2nbxLZgUui5wMPmEmtvxnfYeB4kL8LHNMtxT1Pw6JLd4vUL00k8GrUfEa+2J3FG
31 | euG9dCI1ZS1N5Z/UN0Odq4QCs8ILexAgj99XLwIDAQABAoIBAQCXqUnfKriKr3g9
32 | ucCDhh+hFjOpzUfJWvysT09uQe06SzHMlAm2tLBD9gkTdHy5my9AHjZ4aGvcPs1P
33 | GW3jZfzvjGxvZVMxvbBjIGUAykuI+ujTJw861876z+ZTJgee0qxK4V+aXSrU+kgj
34 | FMsgQd/sKv1dBCMBcactjEu5W2J6vyQ3N7kq9gA/+VQ6yPaPN6y+DTgyZTkG4oXU
35 | 5vetosdvm8MzuvGZlYf/LEtiYfFSpLmg1EzHO1Z6mJ2NOkT4GJ1JIltIWk0Mm3xl
36 | 4Co4zwlKebEGEN0jJ7Rs67ZIcWgcTfOd1xnwabEC04EnN6UhuE8fXZWOuqLFxVg1
37 | UKl8U7zBAoGBAOlBqcLr4P5GeqrH65P2HfkaKLI9V+fip3gO7taWRn7XxX5BMzfr
38 | vBJabRif+nd8eqKrLw+6l1cSBc/6kdunyINoYDfhgEFXfb1tKKc74x4k8xVdsVEU
39 | X+e2nM2+uGe5afnMX61qYNHLa8LDUI0q/Aj5813F0Akj9FxdNMU3T/jpAoGBANeI
40 | ehmT5zvtmFCXqAbb3U6kGjUv/eM016NbDn1l6QVhs4qqm1x1CRwP16qjvU9yLwDr
41 | /QTRWBBAQpR7vzHY+qE768FCJj0zPNU40R2uXXrVYyqm+WhzBuO1kZtlUNwZLW/m
42 | Ek6h0HJdCoNpTxBbUWIdI/TQPEr5hFTtdOf/C8BXAoGBALubU5XyMBFz0F+h0mk8
43 | L9lV39uUGSrpkraulAzF60dD9pVYjYBxut+sGUkQCtylouFI+94TvnuKhGBF8aCQ
44 | 72Y5wgHP/l8PppN/w43WThLFtzm9FMvYrlZo+u9EcX8DkygV5/JLuDmk+jQ48YXJ
45 | R9NUbhhC7NMdNwI++R2SImFZAoGBAI4tmV4GEyOVOETxxgXAQ9z8o80yO2kGErnP
46 | 918BOxYxvR5cLOBw0/GPAdWu7dLan+cbxWzILC+MNF9+wkE/wRVbUcnKuS7l/dsp
47 | /8h0nXXKDgC05RHhz0mnHMZFr3GBqleGjc0RMVA/0A+gCGfh1W3Di1STiTJsJr9f
48 | ZR8lP7tBAoGAVbnEDpvT9pNxKhXd3PzQqjsM+DUeqnpvxT4O5wKJ+M2Bn17VOHMj
49 | JXZMs55G8qITwK3q2BRs+hCvrfAM5oQClv3MwQ4ItqYH24Ed7rU3yA90/LXioXgZ
50 | 5zWBV7LaFFBReJlmsAmEf85eKPRG/OrfoxxHwpGTSnJgU/zuciNgLD8=
51 | -----END RSA PRIVATE KEY-----`)
52 |
53 | pemCert = []byte(`-----BEGIN CERTIFICATE-----
54 | MIIDuzCCAqOgAwIBAgIQBDsLj8sbZAngX2d7OwD1ZzANBgkqhkiG9w0BAQsFADBn
55 | MQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxDjAMBgNVBAcTBUFnbG9l
56 | MRYwFAYDVQQKEw1VbWJyZWxsYSBDb3JwMQswCQYDVQQLEwJJVDEQMA4GA1UEAxMH
57 | Um9vdCBDQTAeFw0xNzAyMTQxMjE5NTZaFw0xODAyMTQxMjE5NTZaMGcxCzAJBgNV
58 | BAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazEOMAwGA1UEBxMFQWdsb2UxFjAUBgNV
59 | BAoTDVVtYnJlbGxhIENvcnAxCzAJBgNVBAsTAklUMRAwDgYDVQQDEwdSb290IENB
60 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxGJ/tXy7ItxDpI9RLb0L
61 | /mkxGpw+BodijFoNixq0QEzxsc+tLr+3czb00a0B/kzObV0AQYwXFqdJ+qb7jZXc
62 | 3To6iALDw3ghWNgfaBeIKPueY6n77DU4ZpzjLy8DhlWO44Vx6CZjByr6qmGh3TnD
63 | qD8sVEFPZlattgHy4KfTmc0abk4bwlJ4lvpXBshl1SYsMgpyZrTC1mggcu+xw98E
64 | cvLollYKvW4WyYtTbwBo5S8c/hVtm2nbxLZgUui5wMPmEmtvxnfYeB4kL8LHNMtx
65 | T1Pw6JLd4vUL00k8GrUfEa+2J3FGeuG9dCI1ZS1N5Z/UN0Odq4QCs8ILexAgj99X
66 | LwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
67 | HQ4EFgQUqHqzyLJ/FARR2FjEK+KgEa1mm28wHwYDVR0jBBgwFoAUqHqzyLJ/FARR
68 | 2FjEK+KgEa1mm28wDQYJKoZIhvcNAQELBQADggEBAI8gE4wo13oMpBM5YgIIcUhI
69 | 0S1N4Z1F+n48aJU9JcQvHSpdOt1RkVhh6jUVkU9+HRv1Eu+4SMfIVswLF9y+jNzf
70 | vPIYGcrXh5jIWybte8PwPQ3AzRBoyxDXCkQyFSb/CLVT7oC+fQje1DeIJeNPNdav
71 | Cm+VrNZPVlFhD2Mbp+Jcn370xBoP5hLeq3JtO954UnBBM7ntN74n2J63JAuroqME
72 | SwXQznqZmQHv3oJZ6DU5JThzsSWKF7aarp6xMzD+UVX2T1vAq/qI6xGfPI8qAQyu
73 | PFM1z1LC4UMUTDN0IxOqRluF4z4GnqqODqrI6SAbt+raLZp3pcHhbtLQYs641iw=
74 | -----END CERTIFICATE-----`)
75 | )
76 |
77 | // pemToDer returns the private key and certificate key DER encoded.
78 | func pemToDER() ([]byte, []byte, error) {
79 | k, _ := pem.Decode(pemKey)
80 | if k == nil {
81 | return nil, nil, fmt.Errorf("no PEM data found for certificate")
82 | }
83 | c, _ := pem.Decode(pemCert)
84 | if c == nil {
85 | return nil, nil, fmt.Errorf("no PEM data found for certificate")
86 | }
87 | return k.Bytes, c.Bytes, nil
88 | }
89 |
90 | func TestRawToBundle(t *testing.T) {
91 | k, c, err := pemToDER()
92 | if err != nil {
93 | t.Fatalf("failed retrieving fake key and cert: %v", err)
94 | }
95 | bundleName := "fakeca"
96 | b, err := RawToBundle(bundleName, k, c)
97 | if err != nil {
98 | t.Fatalf("RawToBundle(%v, ...): got error %v != expected nil", bundleName, err)
99 | }
100 | if b.Name != bundleName {
101 | t.Errorf("RawToBundle(%v, ...): got bundle name %v != expected %v", bundleName, b.Name, bundleName)
102 | }
103 | sn := "5623491996784668439572849354101290343"
104 | if b.Cert.SerialNumber.String() != sn {
105 | t.Errorf("RawToBundle(%v, ...): got cert with serial number %v != expected %v", bundleName, b.Cert.SerialNumber, sn)
106 | }
107 |
108 | rk, rc := b.Raw()
109 | if !reflect.DeepEqual(k, rk) {
110 | t.Errorf("Raw(): raw private key != raw private key used to generate bundle")
111 | }
112 | if !reflect.DeepEqual(c, rc) {
113 | t.Errorf("Raw(): raw certificate != raw certificate used to generate bundle")
114 | }
115 |
116 | _, err = RawToBundle("badkey", k[1:], c)
117 | if err == nil {
118 | t.Error("RawToBundle(badkey, ...): got error nil != expected failed parsing private key...")
119 | }
120 | _, err = RawToBundle("badcert", k, c[1:])
121 | if err == nil {
122 | t.Error("RawToBundle(badcert, ...): got error nil != expected failed parsing certificate...")
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/app/components/CertTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableToolbar from './EnhancedTableToolbar';
23 | import EnhancedTableHead from './EnhancedTableHead'
24 | import { injectIntl } from 'react-intl';
25 |
26 |
27 | const columnData = [
28 | { id: 'Name', numeric: true, disablePadding: false, label: 'common_name' }
29 | ];
30 |
31 |
32 | const styles = theme => ({
33 | root: {
34 | width: '100%',
35 | marginTop: theme.spacing.unit * 3,
36 | },
37 | table: {
38 | minWidth: 1020,
39 | },
40 | tableWrapper: {
41 | overflowX: 'auto',
42 | },
43 | });
44 |
45 | class CertTable extends React.Component {
46 | constructor(props, context) {
47 | super(props, context);
48 | this.state = {
49 | order: 'asc',
50 | orderBy: 'Name',
51 | selected: [],
52 | commonName: '',
53 | pems: [],
54 | data: []
55 | };
56 | }
57 |
58 |
59 | componentWillReceiveProps(newProps) {
60 | if (this.state.data !== newProps.data ||
61 | this.state.selected !== newProps.selected) {
62 | let data = newProps.data;
63 | let selected = newProps.selected;
64 | if (data[selected] != undefined) {
65 | let pems = data[selected].PEMs;
66 | let commonName = data[selected].CommonName;
67 | this.setState({ data: newProps.data, pems: pems, commonName: commonName });
68 | } else {
69 | this.setState({ data: [], pems: [], commonName: '' });
70 | }
71 | }
72 | }
73 |
74 | handleRequestSort = (event, property) => {
75 | const orderBy = property;
76 | let order = 'desc';
77 |
78 | if (this.state.orderBy === property && this.state.order === 'desc') {
79 | order = 'asc';
80 | }
81 | const pems =
82 | order === 'desc'
83 | ? this.state.pems.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
84 | : this.state.pems.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
85 |
86 | this.setState({ pems, order, orderBy });
87 | };
88 |
89 | handleClick = (event, id) => {
90 | const { selected } = this.state;
91 | const selectedIndex = selected.indexOf(id);
92 | let newSelected = [];
93 |
94 | if (selectedIndex === -1) {
95 | newSelected = newSelected.concat(selected, id);
96 | } else if (selectedIndex === 0) {
97 | newSelected = newSelected.concat(selected.slice(1));
98 | } else if (selectedIndex === selected.length - 1) {
99 | newSelected = newSelected.concat(selected.slice(0, -1));
100 | } else if (selectedIndex > 0) {
101 | newSelected = newSelected.concat(
102 | selected.slice(0, selectedIndex),
103 | selected.slice(selectedIndex + 1),
104 | );
105 | }
106 |
107 | this.setState({ selected: newSelected });
108 | };
109 |
110 | handleViewClick = (event, caName, commonName) => {
111 | let data = JSON.stringify({ caName: caName, commonName: commonName });
112 | this.props.history.push({
113 | pathname: `/addcert/${data}`,
114 | });
115 | };
116 |
117 |
118 |
119 |
120 | render() {
121 |
122 | const { classes, history, data, selected,intl } = this.props;
123 | const { order, orderBy, pems, commonName } = this.state;
124 |
125 |
126 | return (
127 |
128 |
129 |
130 |
131 |
132 |
133 |
139 |
140 | {pems.map(n => {
141 | return (
142 |
145 | {n.Name}
146 |
147 |
148 |
149 |
150 | );
151 | })}
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | );
162 | }
163 | }
164 |
165 | CertTable.propTypes = {
166 | classes: PropTypes.object.isRequired,
167 | };
168 |
169 | export default withStyles(styles)(injectIntl(CertTable));
--------------------------------------------------------------------------------
/app/components/ConsortiumTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import { injectIntl } from 'react-intl';
25 |
26 |
27 |
28 | const columnData = [
29 | { id: 'Name', numeric: true, disablePadding: false, label: 'consortium_name' },
30 | { id: 'Desc', numeric: true, disablePadding: false, label: 'consortium_desc' },
31 | { id: 'MspNames', numeric: true, disablePadding: false, label: 'consortium_msps' },
32 | ];
33 |
34 |
35 | const styles = theme => ({
36 | root: {
37 | width: '100%',
38 | marginTop: theme.spacing.unit * 3,
39 | },
40 | table: {
41 | minWidth: 1020,
42 | },
43 | tableWrapper: {
44 | overflowX: 'auto',
45 | },
46 | button: {
47 | margin: theme.spacing.unit,
48 | },
49 | });
50 |
51 | class ConsortiumTable extends React.Component {
52 | constructor(props, context) {
53 | super(props, context);
54 |
55 | this.state = {
56 | order: 'asc',
57 | orderBy: 'Name',
58 | data: [],
59 | OrderName: '',
60 | };
61 | }
62 |
63 | componentWillReceiveProps(newProps) {
64 | if (this.state.data !== newProps.data) {
65 | this.setState({ data: newProps.data });
66 | }
67 | }
68 |
69 | handleRequestSort = (event, property) => {
70 | const orderBy = property;
71 | let order = 'desc';
72 |
73 | if (this.state.orderBy === property && this.state.order === 'desc') {
74 | order = 'asc';
75 | }
76 |
77 | const data =
78 | order === 'desc'
79 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
80 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
81 |
82 | this.setState({ data, order, orderBy });
83 | };
84 |
85 |
86 |
87 |
88 | handleViewClick = (event, key) => {
89 | let data = JSON.stringify({ key: key, formMode: "view" });
90 | this.props.history.push({
91 | pathname: `/consortiumcard/${data}`
92 | });
93 | };
94 |
95 | handleDelClick = (event, key) => {
96 | let that = this;
97 |
98 | var url = 'api/entity/consortiums/' + key;
99 | fetch(url, {
100 | method: 'delete',
101 | }).then(response => {
102 | return response.json();
103 | })
104 | .then(function (data) {
105 | that.props.callback({ data: data == null ? [] : data, selected: 0 });
106 | }).catch(function (e) {
107 | console.log(e);
108 | });
109 | };
110 |
111 |
112 | addConsortium = event => {
113 | let data = JSON.stringify({ formMode: "edit" });
114 | this.props.history.push({
115 | pathname: `/consortiumcard/${data}`
116 | });
117 |
118 |
119 | }
120 |
121 |
122 | render() {
123 | const { classes, history, data,intl } = this.props;
124 | const { order, orderBy } = this.state;
125 | const tooltip = (
126 |
130 |
131 | )
132 | return (
133 |
134 |
135 |
136 |
137 |
138 |
139 |
145 |
146 | {data.map((n, k) => {
147 | return (
148 |
150 |
151 | {n.Name}
152 | {n.Desc}
153 | {n.MspNames == null ? "" : n.MspNames.join()}
154 |
155 |
156 |
157 |
158 |
159 | );
160 | })}
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | );
171 | }
172 | }
173 |
174 | ConsortiumTable.propTypes = {
175 | classes: PropTypes.object.isRequired,
176 | };
177 |
178 | export default withStyles(styles)(injectIntl(ConsortiumTable));
--------------------------------------------------------------------------------
/server/pkg/client/order_deliver.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright IBM Corp. 2017 All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package client
18 |
19 | import (
20 | "bufio"
21 | "bytes"
22 | "context"
23 | "fmt"
24 | "github.com/hyperledger/fabric/common/flogging"
25 | "github.com/hyperledger/fabric/common/localmsp"
26 | "github.com/hyperledger/fabric/common/tools/protolator"
27 | "github.com/hyperledger/fabric/core/comm"
28 | mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
29 | ordererConfig "github.com/hyperledger/fabric/orderer/common/localconfig"
30 | "github.com/hyperledger/fabric/protos/common"
31 | ab "github.com/hyperledger/fabric/protos/orderer"
32 | "github.com/hyperledger/fabric/protos/utils"
33 | "github.com/pkg/errors"
34 | "time"
35 | )
36 |
37 | var logger = flogging.MustGetLogger("client")
38 |
39 | type deliverClientIntf interface {
40 | getSpecifiedBlock(num uint64) (*common.Block, error)
41 | getOldestBlock() (*common.Block, error)
42 | getNewestBlock() (*common.Block, error)
43 | Close() error
44 | }
45 |
46 | type DeliverClient struct {
47 | client ab.AtomicBroadcast_DeliverClient
48 | chainID string
49 | tlsCertHash []byte
50 | }
51 |
52 | func NewDeliverClient(chainID string) (*DeliverClient, error) {
53 | config, err := initConfig()
54 | if err != nil {
55 | return nil, err
56 | }
57 |
58 | clientConfig := comm.ClientConfig{}
59 | clientConfig.Timeout = time.Second * 3
60 | gClient, err := comm.NewGRPCClient(clientConfig)
61 | address := fmt.Sprintf("%s:%d", config.General.ListenAddress, config.General.ListenPort)
62 | conn, err := gClient.NewConnection(address, "")
63 | if err != nil {
64 | return nil, err
65 | }
66 | dc, err := ab.NewAtomicBroadcastClient(conn).Deliver(context.TODO())
67 | if err != nil {
68 | return nil, errors.WithMessage(err, "failed to create deliver client")
69 | }
70 |
71 | return &DeliverClient{client: dc, chainID: chainID}, nil
72 | }
73 |
74 | func seekHelper(
75 | chainID string,
76 | position *ab.SeekPosition,
77 | tlsCertHash []byte,
78 | ) *common.Envelope {
79 |
80 | seekInfo := &ab.SeekInfo{
81 | Start: position,
82 | Stop: position,
83 | Behavior: ab.SeekInfo_BLOCK_UNTIL_READY,
84 | }
85 |
86 | env, err := utils.CreateSignedEnvelopeWithTLSBinding(
87 | common.HeaderType_CONFIG_UPDATE, chainID, localmsp.NewSigner(),
88 | seekInfo, int32(0), uint64(0), tlsCertHash)
89 | if err != nil {
90 | logger.Errorf("Error signing envelope: %s", err)
91 | return nil
92 | }
93 | return env
94 | }
95 |
96 | func (r *DeliverClient) seekSpecified(blockNumber uint64) error {
97 | return r.client.Send(seekHelper(r.chainID, &ab.SeekPosition{
98 | Type: &ab.SeekPosition_Specified{
99 | Specified: &ab.SeekSpecified{
100 | Number: blockNumber}}}, r.tlsCertHash))
101 | }
102 |
103 | func (r *DeliverClient) seekOldest() error {
104 | return r.client.Send(seekHelper(r.chainID,
105 | &ab.SeekPosition{Type: &ab.SeekPosition_Oldest{
106 | Oldest: &ab.SeekOldest{}}}, r.tlsCertHash))
107 | }
108 |
109 | func (r *DeliverClient) seekNewest() error {
110 | return r.client.Send(seekHelper(r.chainID,
111 | &ab.SeekPosition{Type: &ab.SeekPosition_Newest{
112 | Newest: &ab.SeekNewest{}}}, r.tlsCertHash))
113 | }
114 |
115 | func (r *DeliverClient) readBlock() (*common.Block, error) {
116 | msg, err := r.client.Recv()
117 | if err != nil {
118 | return nil, fmt.Errorf("Error receiving: %s", err)
119 | }
120 |
121 | switch t := msg.Type.(type) {
122 | case *ab.DeliverResponse_Status:
123 | logger.Debugf("Got status: %v", t)
124 | return nil, fmt.Errorf("can't read the block: %v", t)
125 | case *ab.DeliverResponse_Block:
126 | logger.Debugf("Received block: %v", t.Block.Header.Number)
127 | r.client.Recv() // Flush the success message
128 | return t.Block, nil
129 | default:
130 | return nil, fmt.Errorf("response error: unknown type %T", t)
131 | }
132 | }
133 |
134 | func (r *DeliverClient) GetSpecifiedBlock(num uint64) (string, error) {
135 | err := r.seekSpecified(num)
136 | if err != nil {
137 | logger.Errorf("Received error: %s", err)
138 | return "", err
139 | }
140 | var b bytes.Buffer
141 | writer := bufio.NewWriter(&b)
142 | block, err := r.readBlock()
143 | if err != nil {
144 | return "", err
145 | }
146 | protolator.DeepMarshalJSON(writer, block)
147 | if err != nil {
148 | return "", err
149 | }
150 | return string(b.Bytes()), nil
151 | }
152 |
153 | func (r *DeliverClient) GetOldestBlock() (string, error) {
154 | err := r.seekOldest()
155 | if err != nil {
156 | logger.Errorf("Received error: %s", err)
157 | return "", err
158 | }
159 | var b bytes.Buffer
160 | writer := bufio.NewWriter(&b)
161 | block, err := r.readBlock()
162 | if err != nil {
163 | return "", err
164 | }
165 | protolator.DeepMarshalJSON(writer, block)
166 | if err != nil {
167 | return "", err
168 | }
169 | return string(b.Bytes()), nil
170 | }
171 |
172 | func (r *DeliverClient) GetNewestBlock() (string, error) {
173 | err := r.seekNewest()
174 | if err != nil {
175 | logger.Errorf("Received error: %s", err)
176 | return "", err
177 | }
178 | var b bytes.Buffer
179 | writer := bufio.NewWriter(&b)
180 | block, err := r.readBlock()
181 | if err != nil {
182 | return "", err
183 | }
184 | protolator.DeepMarshalJSON(writer, block)
185 | if err != nil {
186 | return "", err
187 | }
188 | return string(b.Bytes()), nil
189 | }
190 |
191 | func (r *DeliverClient) Close() error {
192 | return r.client.CloseSend()
193 | }
194 |
195 | func initConfig() (*ordererConfig.TopLevel, error) {
196 | config, err := ordererConfig.Load()
197 | if err != nil {
198 | return nil, err
199 | }
200 | // Load local MSP
201 | err = mspmgmt.LoadLocalMsp(config.General.LocalMSPDir, config.General.BCCSP, config.General.LocalMSPID)
202 | if err != nil { // Handle errors reading the config file
203 | return nil, err
204 | }
205 | return config, nil
206 | }
207 |
--------------------------------------------------------------------------------
/app/components/OrganizationTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import Typography from '@material-ui/core/Typography';
11 | import Paper from '@material-ui/core/Paper';
12 | import IconButton from '@material-ui/core/IconButton';
13 | import Tooltip from '@material-ui/core/Tooltip';
14 | import DeleteIcon from '@material-ui/icons/Delete';
15 | import FilterListIcon from '@material-ui/icons/FilterList';
16 | import AddIcon from '@material-ui/icons/Add';
17 | import Button from '@material-ui/core/Button';
18 | import Icon from '@material-ui/core/Icon';
19 | import EnhancedTableHead from './EnhancedTableHead';
20 | import EnhancedTableToolbar from './EnhancedTableToolbar';
21 | import { injectIntl } from 'react-intl';
22 |
23 |
24 |
25 | const columnData = [
26 | { id: 'Country', numeric: true, disablePadding: false, label: 'country' },
27 | { id: 'Province', numeric: true, disablePadding: false, label: 'province' },
28 | { id: 'Locality', numeric: true, disablePadding: false, label: 'locality' },
29 | { id: 'Organization', numeric: true, disablePadding: false, label: 'organization' },
30 | { id: 'CommonName', numeric: true, disablePadding: false, label:'common_name' }
31 | ];
32 |
33 | const styles = theme => ({
34 | root: {
35 | width: '100%',
36 | marginTop: theme.spacing.unit * 3,
37 | },
38 | table: {
39 | minWidth: 1020,
40 | },
41 | tableWrapper: {
42 | overflowX: 'auto',
43 | },
44 | button: {
45 | margin: theme.spacing.unit,
46 | },
47 | });
48 |
49 | class OrganizationTable extends React.Component {
50 | constructor(props, context) {
51 | super(props, context);
52 |
53 | this.state = {
54 | order: 'asc',
55 | orderBy: 'CommonName',
56 | selected: [],
57 | data: []
58 | };
59 | }
60 |
61 | handleClick = (event, id) => {
62 | this.props.callback({ selected: id })
63 | };
64 |
65 | componentWillReceiveProps(newProps) {
66 | if (this.state.data !== newProps.data) {
67 | this.setState({data: newProps.data});
68 | }
69 | }
70 |
71 | handleViewClick = (event, key) => {
72 | let data = JSON.stringify({ key: key, formMode: "view" });
73 | this.props.history.push({
74 | pathname: `/organizationcard/${data}`,
75 | });
76 | };
77 |
78 | handleDelClick = (event, key) => {
79 | let that = this;
80 | var url = 'api/entity/organizations/' + key;
81 | fetch(url, {
82 | method: 'delete',
83 | }).then(response => {
84 | return response.json();
85 | }).then(function (data) {
86 | that.props.callback({ data: data == null ? [] : data, selected: 0 });
87 | }).catch(function (e) {
88 | console.log(e);
89 | });
90 | };
91 |
92 | handleRequestSort = (event, property) => {
93 | const orderBy = property;
94 | let order = 'desc';
95 |
96 | if (this.state.orderBy === property && this.state.order === 'desc') {
97 | order = 'asc';
98 | }
99 |
100 | const data =
101 | order === 'desc'
102 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
103 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
104 |
105 | this.setState({ data, order, orderBy });
106 | };
107 |
108 | addOrganization = event => {
109 | let data = JSON.stringify({ formMode: "edit" });
110 | this.props.history.push({
111 | pathname: `/organizationcard/${data}`
112 |
113 | });
114 | }
115 |
116 |
117 | render() {
118 | const { classes, history, data,intl } = this.props;
119 | const { order, orderBy, selected } = this.state;
120 | const tooltip = (
121 |
125 |
126 | )
127 |
128 | return (
129 |
130 |
131 |
132 |
133 |
134 |
135 |
141 |
142 | {data.map((n, k) => {
143 | return (
144 | this.handleClick(event, k)}
147 | key ={n.CommonName}
148 | >
149 |
150 | {n.Country}
151 | {n.Province}
152 | {n.Locality}
153 | {n.Organization}
154 | {n.CommonName}
155 |
156 |
157 |
158 |
159 |
160 | );
161 | })}
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | );
172 | }
173 | }
174 |
175 | OrganizationTable.propTypes = {
176 | classes: PropTypes.object.isRequired,
177 | };
178 |
179 | export default withStyles(styles)(injectIntl(OrganizationTable));
--------------------------------------------------------------------------------
/app/components/OrdererTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import { injectIntl } from 'react-intl';
25 |
26 |
27 |
28 | const columnData = [
29 | { id: 'Name', numeric: true, disablePadding: false, label: 'node_name' },
30 | { id: 'LedgerType', numeric: true, disablePadding: false, label: 'ledger_type' },
31 | { id: 'ListenAddress', numeric: true, disablePadding: false, label: 'ip_address' },
32 | { id: 'ListenPort', numeric: true, disablePadding: false, label: 'port' },
33 | { id: 'LocalMSPID', numeric: true, disablePadding: false, label: 'msp_id' },
34 | { id: 'Consortiums', numeric: true, disablePadding: false, label: 'consortium' },
35 |
36 | ];
37 |
38 | const styles = theme => ({
39 | root: {
40 | width: '100%',
41 | marginTop: theme.spacing.unit * 3,
42 | },
43 | button: {
44 | margin: theme.spacing.unit,
45 | },
46 | table: {
47 | minWidth: 1020,
48 | },
49 | tableWrapper: {
50 | overflowX: 'auto',
51 | },
52 | });
53 |
54 | class OrdererTable extends React.Component {
55 | constructor(props, context) {
56 | super(props, context);
57 |
58 | this.state = {
59 | order: 'asc',
60 | orderBy: 'calories',
61 | selected: [],
62 | data: []
63 | };
64 | }
65 |
66 |
67 |
68 | handleRequestSort = (event, property) => {
69 | const orderBy = property;
70 | let order = 'desc';
71 |
72 | if (this.state.orderBy === property && this.state.order === 'desc') {
73 | order = 'asc';
74 | }
75 |
76 | const data =
77 | order === 'desc'
78 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
79 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
80 |
81 | this.setState({ data, order, orderBy });
82 | };
83 |
84 |
85 |
86 | handleViewClick = (event, key) => {
87 | let data = JSON.stringify({ key: key,formMode:"view" });
88 | this.props.history.push({
89 | pathname: `/orderercard/${data}`
90 | });
91 | };
92 |
93 | handleConsoleClick = (event, key) => {
94 | let data = JSON.stringify({ key: key });
95 | this.props.history.push({
96 | pathname: `/ordererconsolecard/${data}`,
97 | });
98 | };
99 |
100 | handleDelClick = (event, key) => {
101 | let that = this;
102 |
103 | var url = 'api/entity/orderers/' + key;
104 | fetch(url, {
105 | method: 'delete',
106 | }).then(response => {
107 | return response.json();
108 | })
109 | .then(function (data) {
110 | that.props.callback({ data: data==null?[]:data ,selected:0});
111 | }).catch(function (e) {
112 | console.log(e);
113 | });
114 | };
115 |
116 |
117 | addOrderer = event => {
118 | let data = JSON.stringify({ formMode: "edit" });
119 | this.props.history.push({
120 | pathname: `/orderercard/${data}`,
121 | });
122 | }
123 |
124 | render() {
125 | const { classes, history,data,intl } = this.props;
126 | const { order, orderBy, selected } = this.state;
127 | const tooltip = (
128 |
132 |
133 | )
134 | return (
135 |
136 |
137 |
138 |
139 |
140 |
141 |
147 |
148 | {data.map((n,k) => {
149 | return (
150 |
152 |
153 | {n.Name}
154 | {n.LedgerType}
155 | {n.ListenAddress}
156 | {n.ListenPort}
157 | {n.LocalMSPID}
158 | {n.Consortiums==null?"":n.Consortiums.join()}
159 |
160 |
161 |
162 |
163 |
164 |
165 | );
166 | })}
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | );
177 | }
178 | }
179 |
180 | OrdererTable.propTypes = {
181 | classes: PropTypes.object.isRequired,
182 | };
183 |
184 | export default withStyles(styles)(injectIntl(OrdererTable));
--------------------------------------------------------------------------------
/app/components/PeerTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import { injectIntl } from 'react-intl';
25 |
26 |
27 |
28 | const columnData = [
29 | { id: 'Name', numeric: true, disablePadding: false, label: 'node_name' },
30 | { id: 'ListenAddress', numeric: true, disablePadding: false, label: 'ip_address' },
31 | { id: 'ListenPort', numeric: true, disablePadding: false, label: 'port' },
32 | { id: 'ChainCodeListenPort', numeric: true, disablePadding: false, label: 'chaincode_name' },
33 | { id: 'EventListenPort', numeric: true, disablePadding: false, label: 'event_port' },
34 | { id: 'LocalMSPID', numeric: true, disablePadding: false, label: 'peer_msp' },
35 | { id: 'AdminMSPID', numeric: true, disablePadding: false, label: 'admin_msp' },
36 |
37 | ];
38 |
39 |
40 | const styles = theme => ({
41 | root: {
42 | width: '100%',
43 | marginTop: theme.spacing.unit * 3,
44 | },
45 | table: {
46 | minWidth: 1020,
47 | },
48 | button: {
49 | margin: theme.spacing.unit,
50 | },
51 | tableWrapper: {
52 | overflowX: 'auto',
53 | },
54 | });
55 |
56 | class PeerTable extends React.Component {
57 | constructor(props, context) {
58 | super(props, context);
59 |
60 | this.state = {
61 | order: 'asc',
62 | orderBy: 'calories',
63 | selected: [],
64 | data: []
65 | };
66 | }
67 |
68 |
69 |
70 | handleRequestSort = (event, property) => {
71 | const orderBy = property;
72 | let order = 'desc';
73 |
74 | if (this.state.orderBy === property && this.state.order === 'desc') {
75 | order = 'asc';
76 | }
77 |
78 | const data =
79 | order === 'desc'
80 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
81 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
82 |
83 | this.setState({ data, order, orderBy });
84 | };
85 |
86 |
87 | handleViewClick = (event, key) => {
88 | let data = JSON.stringify({ key: key,formMode:"view" });
89 | this.props.history.push({
90 | pathname: `/peercard/${data}`
91 | });
92 | };
93 |
94 | handleConsoleClick = (event, key) => {
95 | let data = JSON.stringify({ key: key });
96 | this.props.history.push({
97 | pathname: `/peerconsolecard/${data}`
98 | });
99 | };
100 |
101 | handleDelClick = (event, key) => {
102 | let that = this;
103 |
104 | var url = `api/entity/peers/${key}`;
105 | fetch(url, {
106 | method: 'delete',
107 | mode: "cors"
108 | }).then(response => {
109 | return response.json();
110 | })
111 | .then(function (data) {
112 | that.props.callback({ data: data == null ? [] : data, selected: 0 });
113 | }).catch(function (e) {
114 | console.log(e);
115 | });
116 | };
117 |
118 |
119 | addPeer = event => {
120 | let data = JSON.stringify({ formMode:"edit" });
121 | this.props.history.push({
122 | pathname: `/peercard/${data}`
123 | });
124 | }
125 |
126 | render() {
127 | const { classes, history, data,intl } = this.props;
128 | const { order, orderBy, selected } = this.state;
129 | const tooltip = (
130 |
134 |
135 | )
136 | return (
137 |
138 |
139 |
140 |
141 |
142 |
143 |
149 |
150 | {data.map((n, k) => {
151 | return (
152 |
153 | {n.Name}
154 | {n.ListenAddress}
155 | {n.ListenPort}
156 | {n.ChainCodeListenPort}
157 | {n.EventListenPort}
158 | {n.LocalMSPID}
159 | {n.AdminMSPID}
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | );
168 | })}
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | );
179 | }
180 | }
181 |
182 | PeerTable.propTypes = {
183 | classes: PropTypes.object.isRequired,
184 | };
185 |
186 | export default withStyles(styles)(injectIntl(PeerTable));
--------------------------------------------------------------------------------
/app/components/ChannelTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import LinearProgress from '@material-ui/core/LinearProgress';
25 | import { injectIntl } from 'react-intl';
26 |
27 |
28 | const columnData = [
29 | { id: 'Name', numeric: true, disablePadding: false, label: 'channel_name' },
30 | { id: 'Consortium', numeric: true, disablePadding: false, label: 'consortium' },
31 | { id: 'OrdererName', numeric: true, disablePadding: false, label: 'orderer_name' },
32 | { id: 'State', numeric: true, disablePadding: false, label: 'enabled' },
33 | { id: 'Desc', numeric: true, disablePadding: false, label: 'channel_desc' },
34 |
35 | ];
36 |
37 |
38 | const styles = theme => ({
39 | root: {
40 | width: '100%',
41 | marginTop: theme.spacing.unit * 3,
42 | },
43 | table: {
44 | minWidth: 1020,
45 | },
46 | tableWrapper: {
47 | overflowX: 'auto',
48 | },
49 | button: {
50 | margin: theme.spacing.unit,
51 | },
52 | });
53 |
54 | class ChannelTable extends React.Component {
55 | constructor(props, context) {
56 | super(props, context);
57 |
58 | this.state = {
59 | order: 'asc',
60 | orderBy: 'Name',
61 | data: [],
62 | OrderName: '',
63 | loading:false
64 | };
65 | }
66 |
67 | componentWillReceiveProps(newProps) {
68 | if (this.state.data !== newProps.data) {
69 | this.setState({ data: newProps.data });
70 | }
71 | }
72 |
73 | handleRequestSort = (event, property) => {
74 | const orderBy = property;
75 | let order = 'desc';
76 |
77 | if (this.state.orderBy === property && this.state.order === 'desc') {
78 | order = 'asc';
79 | }
80 |
81 | const data =
82 | order === 'desc'
83 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
84 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
85 |
86 | this.setState({ data, order, orderBy });
87 | };
88 |
89 | handleCmdClick = (event, key) => {
90 | let that = this;
91 | let data = {}
92 | data.Cmd = "CHANNEL_CREATE";
93 | data.NodeName = key;
94 | var url = `api/entity/channels/${key}/cmd`;
95 | this.setState({ loading: true });
96 | fetch(url,{
97 | method: 'put',
98 | body:JSON.stringify(data)
99 | }).then(function(response) {
100 | return response.json();
101 | }).then(function(res) {
102 | if(res.msg=="enable"){
103 | let datas = that.state.data;
104 | let newDatas = [];
105 | datas.forEach(function(data){
106 | if(data.Name == key){
107 | data.State = "enable";
108 | }
109 | newDatas.push(data)
110 | });
111 | console.log(newDatas);
112 | that.setState({ loading: false });
113 | that.props.callback({ data: newDatas, selected: 0 });
114 | }else{
115 | that.setState({ loading: false });
116 | alert(that.props.intl.formatMessage({id:'desc_5'}));
117 | }
118 |
119 | }).catch(function(e) {
120 | console.log(e);
121 | });
122 |
123 |
124 | };
125 |
126 |
127 | handleViewClick = (event, key) => {
128 | let data = JSON.stringify({ key: key, formMode: "view" });
129 | this.props.history.push({
130 | pathname: `/channelcard/${data}`
131 | });
132 | };
133 |
134 | handleDelClick = (event, key) => {
135 | let that = this;
136 |
137 | var url = 'api/entity/channels/' + key;
138 | fetch(url, {
139 | method: 'delete',
140 | }).then(response => {
141 | return response.json();
142 | })
143 | .then(function (data) {
144 | that.props.callback({ data: data == null ? [] : data, selected: 0 });
145 | }).catch(function (e) {
146 | console.log(e);
147 | });
148 | };
149 |
150 |
151 | addConsortium = event => {
152 | let data = JSON.stringify({ formMode: "edit" });
153 | this.props.history.push({
154 | pathname: `/channelcard/${data}`
155 | });
156 |
157 |
158 | }
159 |
160 |
161 | render() {
162 | const { classes, history, data,intl } = this.props;
163 | const { order, orderBy } = this.state;
164 | const tooltip = (
165 |
169 |
170 | )
171 | return (
172 |
173 |
174 |
175 | {this.state.loading && }
176 |
177 |
178 |
179 |
185 |
186 | {data.map((n, k) => {
187 | return (
188 |
190 |
191 | {n.Name}
192 | {n.Consortium}
193 | {n.OrdererName}
194 | {n.State=="enable"? intl.formatMessage({id:'able'}): intl.formatMessage({id:'disable'})}
195 | {n.Desc}
196 |
197 | {n.State!="enable" && }
198 |
199 | {n.State!="enable" &&}
200 |
201 |
202 | );
203 | })}
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | );
214 | }
215 | }
216 |
217 | ChannelTable.propTypes = {
218 | classes: PropTypes.object.isRequired,
219 | };
220 |
221 | export default withStyles(styles)(injectIntl(ChannelTable));
--------------------------------------------------------------------------------
/app/components/OrdererConsoleCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import AppBar from '@material-ui/core/AppBar';
4 | import Button from '@material-ui/core/Button';
5 | import { withStyles } from '@material-ui/core/styles';
6 | import Tabs from '@material-ui/core/Tabs';
7 | import Tab from '@material-ui/core/Tab';
8 | import Paper from '@material-ui/core/Paper';
9 | import Typography from '@material-ui/core/Typography';
10 | import MenuItem from '@material-ui/core/MenuItem';
11 | import Select from '@material-ui/core/Select';
12 | import TextField from '@material-ui/core/TextField';
13 | import LinearProgress from '@material-ui/core/LinearProgress';
14 | import ReactJson from 'react-json-view';
15 | import { injectIntl } from 'react-intl';
16 | import {msgToObj} from '../util'
17 |
18 | const styles = theme => ({
19 | button: {
20 | margin: theme.spacing.unit,
21 | },
22 | input: {
23 | display: 'none',
24 | },
25 | tab: {
26 | flexGrow: 1,
27 | backgroundColor: theme.palette.background.paper,
28 | },
29 | select: {
30 | margin: theme.spacing.unit,
31 | width: "100%"
32 | },
33 | textField: {
34 | margin: theme.spacing.unit,
35 | width: "100%"
36 | },
37 | paper: {
38 | ...theme.mixins.gutters(),
39 | paddingTop: theme.spacing.unit * 2,
40 | paddingBottom: theme.spacing.unit * 2,
41 | },
42 | panel:{
43 | whiteSpace:"nowrap",
44 | height: window.screen.height-200 ,
45 | overflowX: "scroll",
46 | overflowY: "scroll",
47 | }
48 | });
49 |
50 |
51 | class OrdererConsoleCard extends React.Component {
52 |
53 | constructor(props) {
54 | super(props);
55 | this.state = {
56 | cmd: "NODE_START",
57 | log: "",
58 | state: "stop",
59 | nodeName: "",
60 | loading: false,
61 | channelId:"system-channel",
62 | blockNum:"-1",
63 | channels:[]
64 |
65 | };
66 | }
67 |
68 | componentDidMount = () => {
69 |
70 | let that = this;
71 | let params = JSON.parse(this.props.match.params.data);
72 | let { key } = params;
73 | this.getData(key)
74 | that.setState({ nodeName: key });
75 | var url = `api/entity/orderers/${key}/state`;
76 |
77 | fetch(url, {
78 | method: 'get',
79 | }).then(response => {
80 | return response.json();
81 | })
82 | .then(function (data) {
83 | that.setState({ state: data.state });
84 | }).catch(function (e) {
85 | console.log(e);
86 | });
87 | }
88 |
89 | handleChange =(name)=> event => {
90 | this.setState({ [name]: event.target.value });
91 | };
92 |
93 | getData = (key) => {
94 | let that=this;
95 | var url = 'api/entity/channels';
96 | fetch(url,{
97 | method: 'get',
98 | }).then(response=>{
99 | return response.json();
100 | }).then(function(data) {
101 | let channels = [];
102 | data.channels.forEach(function (channel){
103 | if(channel.OrdererName == key && channel.State == "enable"){
104 | channels.push(channel);
105 | }
106 | })
107 | that.setState({channels:channels})
108 | }).catch(function(e) {
109 | console.log(e);
110 | });
111 | }
112 |
113 |
114 | execCmd = (cmd) => {
115 |
116 | let that = this;
117 | let data = {}
118 | data.Cmd = this.state.cmd;
119 | data.NodeName = this.state.nodeName;
120 | if (data.Cmd == "SEEK") {
121 | if (this.state.channelId == "" || this.state.blockNum == "") {
122 | that.setState({ log: "Some param is required" })
123 | return "";
124 | }
125 | data.ChannelId = this.state.channelId;
126 | data.Seek = this.state.blockNum;
127 | }
128 | that.setState({ loading: true, log: this.props.intl.formatMessage({id:'running'})} );
129 | var url = `api/entity/orderers/${this.state.nodeName}/cmd`;
130 | fetch(url, {
131 | method: 'put',
132 | body: JSON.stringify(data)
133 | }).then(function (response) {
134 | return response.json()
135 | }).then(function (data) {
136 | let msgObj = msgToObj(data.msg)
137 | let msg = that.props.intl.formatMessage(msgObj[0],msgObj[1]);
138 | that.setState({ log: msg, state: data.state, loading: false });
139 | }).catch(function (e) {
140 | console.log(e);
141 | });
142 | };
143 |
144 |
145 | render() {
146 | let that = this;
147 | const { classes,intl } = this.props;
148 | const { cmd, log, nodeName, state,channelId,blockNum,channels } = this.state;
149 | let items = [];
150 | items.push()
151 | channels.forEach(function(channel){
152 | items.push()
153 | })
154 | let isJson = false;
155 | try { var json = JSON.parse(log); isJson = true; } catch (e) { isJson = false; }
156 | return (
157 |
158 |
159 |
160 |
161 |
{nodeName} {intl.formatMessage({id:state})}
162 |
163 |
172 | {
173 | cmd === "SEEK" &&
174 | }
175 | {
176 | cmd === "SEEK" &&
177 | }
178 | {
179 | cmd==="SEEK" && {intl.formatMessage({id:'desc_1'})}
180 | }
181 |
182 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | {this.state.loading &&
}
192 |
{intl.formatMessage({id:'log'})}
193 |
194 | {!isJson && }
195 | {isJson && }
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | )
206 | }
207 | }
208 |
209 | OrdererConsoleCard.propTypes = {
210 | classes: PropTypes.object.isRequired,
211 | };
212 |
213 | export default withStyles(styles)(injectIntl(OrdererConsoleCard));
214 |
215 |
216 |
--------------------------------------------------------------------------------
/app/components/ChainCodeTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classNames from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import Table from '@material-ui/core/Table';
6 | import TableBody from '@material-ui/core/TableBody';
7 | import TableCell from '@material-ui/core/TableCell';
8 | import TableHead from '@material-ui/core/TableHead';
9 | import TableRow from '@material-ui/core/TableRow';
10 | import TableSortLabel from '@material-ui/core/TableSortLabel';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Paper from '@material-ui/core/Paper';
14 | import IconButton from '@material-ui/core/IconButton';
15 | import Tooltip from '@material-ui/core/Tooltip';
16 | import DeleteIcon from '@material-ui/icons/Delete';
17 | import FilterListIcon from '@material-ui/icons/FilterList';
18 | import { lighten } from '@material-ui/core/styles/colorManipulator';
19 | import AddIcon from '@material-ui/icons/Add';
20 | import Button from '@material-ui/core/Button';
21 | import Icon from '@material-ui/core/Icon';
22 | import EnhancedTableHead from './EnhancedTableHead';
23 | import EnhancedTableToolbar from './EnhancedTableToolbar';
24 | import LinearProgress from '@material-ui/core/LinearProgress';
25 | import { injectIntl } from 'react-intl';
26 | import {msgToObj} from '../util'
27 |
28 | const columnData = [
29 | { id: 'Name', numeric: true, disablePadding: false, label: 'chaincode_name' },
30 | { id: 'Path', numeric: true, disablePadding: false, label: 'path' },
31 | { id: 'Lang', numeric: true, disablePadding: false, label: 'language' },
32 | { id: 'Version', numeric: true, disablePadding: false, label: 'version' },
33 | { id: 'PeerName', numeric: true, disablePadding: false, label: 'peer_manage' },
34 | { id: 'State', numeric: true, disablePadding: false, label: 'enabled' },
35 | ];
36 |
37 |
38 | const styles = theme => ({
39 | root: {
40 | width: '100%',
41 | marginTop: theme.spacing.unit * 3,
42 | },
43 | table: {
44 | minWidth: 1020,
45 | },
46 | tableWrapper: {
47 | overflowX: 'auto',
48 | },
49 | button: {
50 | margin: theme.spacing.unit,
51 | },
52 | });
53 |
54 | class ChainCodeTable extends React.Component {
55 | constructor(props, context) {
56 | super(props, context);
57 |
58 | this.state = {
59 | order: 'asc',
60 | orderBy: 'Name',
61 | data: [],
62 | OrderName: '',
63 | loading:false
64 | };
65 | }
66 |
67 | componentWillReceiveProps(newProps) {
68 | if (this.state.data !== newProps.data) {
69 | this.setState({ data: newProps.data });
70 | }
71 | }
72 |
73 | handleRequestSort = (event, property) => {
74 | const orderBy = property;
75 | let order = 'desc';
76 |
77 | if (this.state.orderBy === property && this.state.order === 'desc') {
78 | order = 'asc';
79 | }
80 |
81 | const data =
82 | order === 'desc'
83 | ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
84 | : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
85 |
86 | this.setState({ data, order, orderBy });
87 | };
88 |
89 | handleConsoleClick = (event, key) => {
90 | let data = JSON.stringify({ key: key });
91 | this.props.history.push({
92 | pathname: `/chainCodeconsolecard/${data}`
93 | });
94 | };
95 |
96 |
97 | handleViewClick = (event, key) => {
98 | let data = JSON.stringify({ key: key, formMode: "view" });
99 | this.props.history.push({
100 | pathname: `/chaincodecard/${data}`
101 | });
102 | };
103 |
104 | handleDelClick = (event, key) => {
105 | let that = this;
106 |
107 | var url = 'api/entity/chaincodes/' + key;
108 | fetch(url, {
109 | method: 'delete',
110 | }).then(response => {
111 | return response.json();
112 | })
113 | .then(function (data) {
114 | that.props.callback({ data: data == null ? [] : data, selected: 0 });
115 | }).catch(function (e) {
116 | console.log(e);
117 | });
118 | };
119 |
120 |
121 | handleCmdClick = (event,cmd,nodeName,peerName) => {
122 | let that = this;
123 | let data = {}
124 | data.Cmd = cmd;
125 | data.NodeName = nodeName;
126 | data.Peer = peerName;
127 | let key = nodeName+"."+peerName
128 | this.setState({ loading: true });
129 | var url = `api/entity/chaincodes/${key}/cmd`;
130 | fetch(url,{
131 | method: 'put',
132 | body:JSON.stringify(data)
133 | }).then(function(response) {
134 | return response.json()
135 | }).then(function(res) {
136 | if(res.msg=="ok"){
137 | let datas = that.state.data;
138 | let newDatas = [];
139 | datas.forEach(function(data){
140 | if(data.Name+"."+data.PeerName == key){
141 | if(cmd == "NODE_START"){
142 | data.State = "enable";
143 | }else{
144 | data.State = "disable";
145 | }
146 |
147 | }
148 | newDatas.push(data)
149 | });
150 | console.log(newDatas);
151 | that.setState({ loading: false });
152 | that.props.callback({ data: newDatas });
153 | }else{
154 | that.setState({ loading: false });
155 | let msgObj = msgToObj(res.msg);
156 | console.log(msgObj);
157 | let msg = that.props.intl.formatMessage(msgObj[0],msgObj[1]);
158 | alert(msg);
159 | }
160 | }).catch(function(e) {
161 | console.log(e);
162 | });
163 | };
164 |
165 | addChainCode = event => {
166 | let data = JSON.stringify({ formMode: "edit" });
167 | this.props.history.push({
168 | pathname: `/chaincodecard/${data}`
169 | });
170 |
171 |
172 | }
173 |
174 |
175 | render() {
176 | const { classes, history, data,intl } = this.props;
177 | const { order, orderBy } = this.state;
178 | const tooltip = (
179 |
183 |
184 | )
185 | return (
186 |
187 |
188 |
189 | {this.state.loading && }
190 |
191 |
192 |
193 |
199 |
200 | {data.map((n, k) => {
201 | return (
202 |
204 |
205 | {n.Name}
206 | {n.Path}
207 | {n.Lang}
208 | {n.Version}
209 | {n.PeerName}
210 | {n.State=="enable"?intl.formatMessage({id:'able'}):intl.formatMessage({id:'disable'})}
211 |
212 |
213 | {n.State !="enable" && }
214 |
215 | {n.State =="disable" && }
216 | {n.State =="enable" && }
217 |
218 |
219 | );
220 | })}
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | );
231 | }
232 | }
233 |
234 | ChainCodeTable.propTypes = {
235 | classes: PropTypes.object.isRequired,
236 | };
237 |
238 | export default withStyles(styles)(injectIntl(ChainCodeTable));
--------------------------------------------------------------------------------
/server/pkg/entity/cmd.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "strings"
8 | // "io/ioutil"
9 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/client"
10 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/util"
11 | "os"
12 | "os/exec"
13 | "path/filepath"
14 | "strconv"
15 | )
16 |
17 | const (
18 | ALLINFO = iota
19 | OUTINFO
20 | ERRINFO
21 | )
22 |
23 | type CMD interface {
24 | Exec(map[string]string) string
25 | }
26 |
27 | func ExecChannel(cmdInfo map[string]string) string {
28 | cmd := cmdInfo["Cmd"]
29 | channelId := cmdInfo["ChannelId"]
30 | ordererEndpoint := cmdInfo["OrdererEndpoint"]
31 | //seek :=cmdInfo["Seek"]
32 | channelPath := filepath.Join(channelDir, channelId)
33 | peerBin := WindowsBin(peerBin)
34 | switch cmd {
35 | case "CHANNEL_CREATE":
36 | genesisBlock := channelId + ".block"
37 | dest := filepath.Join(channelPath, genesisBlock)
38 | cmd := exec.Command(peerBin, "channel", "create", "-c", channelId, "-o", ordererEndpoint)
39 | msg := run(true, ALLINFO, cmd, channelPath)
40 | util.Copy(genesisBlock,dest)
41 | os.RemoveAll(genesisBlock)
42 | return msg
43 | }
44 | return ""
45 | }
46 |
47 | func ExecPeer(cmdInfo map[string]string) string {
48 | cmd := cmdInfo["Cmd"]
49 | nodeName := cmdInfo["NodeName"]
50 | channelId := cmdInfo["ChannelId"]
51 | version := cmdInfo["Version"]
52 | lang := cmdInfo["Lang"]
53 | path := cmdInfo["Path"]
54 | name := cmdInfo["Name"]
55 | json := cmdInfo["Json"]
56 | ordererEndpoint := cmdInfo["OrdererEndpoint"]
57 | ordererName := cmdInfo["OrdererName"]
58 | peerPath := filepath.Join(peerDir, nodeName)
59 | channelPath := filepath.Join(channelDir, channelId)
60 | cacheNodeName := peers + "." + nodeName
61 | cache := util.Caches.Get(cacheNodeName)
62 |
63 | if cache != nil && cmd == "NODE_START" {
64 | return "node_already_run"
65 | } else if cache == nil && cmd == "NODE_STOP" {
66 | return "node_already_stop"
67 | } else if cache == nil && cmd != "NODE_START" && cmd != "NODE_STOP" {
68 | return "node_must_run"
69 | }
70 | ordererCacheNodeName := orderers + "." + ordererName
71 | orderCache := util.Caches.Get(ordererCacheNodeName)
72 | peerBin := WindowsBin(peerBin)
73 | switch cmd {
74 | case "NODE_START":
75 | cmd := exec.Command(peerBin, "node", "start", "--peer-chaincodedev=true")
76 | util.Caches.Set(cacheNodeName, cmd)
77 | return run(false, ALLINFO, cmd, peerPath)
78 | case "NODE_STOP":
79 | v := cache.Value
80 | if _, ok := v.(*exec.Cmd); ok {
81 | err := v.(*exec.Cmd).Process.Kill()
82 | if err != nil {
83 | return err.Error()
84 | }
85 | }
86 | util.Caches.Delete(cacheNodeName)
87 | return "node_stop_ok"
88 | case "CHANNEL_LIST":
89 | cmd := exec.Command(peerBin, "channel", "list")
90 | return run(true, ALLINFO, cmd, peerPath)
91 | case "CHANNEL_JOIN":
92 |
93 | genesisBlock := channelId + ".block"
94 | genesisBlock = filepath.Join(channelPath, genesisBlock)
95 | cmd := exec.Command(peerBin, "channel", "join", "-b", genesisBlock)
96 | msg := run(true, ALLINFO, cmd, peerPath)
97 | return msg
98 | case "CHANNEL_GETINFO":
99 | cmd := exec.Command(peerBin, "channel", "getinfo", "-c", channelId)
100 | msg := run(true, ALLINFO, cmd, peerPath)
101 | return msg
102 | case "CHAINCODE_INSTALL":
103 | dir := filepath.Dir(path)
104 | cmd := exec.Command(peerBin, "chaincode", "install", "-n", name, "-v", version, "-l", lang, "-p", dir)
105 | msg := run(true, ALLINFO, cmd, peerPath)
106 | return msg
107 | case "CHAINCODE_LIST":
108 | cmd := exec.Command(peerBin, "chaincode", "list", "--installed")
109 | msg := run(true, ALLINFO, cmd, peerPath)
110 | return msg
111 | case "CHAINCODE_INIT":
112 | if orderCache == nil {
113 | return "desc_3"+"|" + ordererName
114 | }
115 | cmd := exec.Command(peerBin, "chaincode", "instantiate", "-n", name, "-v", version, "-c", json, "-C", channelId, "-o", ordererEndpoint)
116 | msg := run(true, ALLINFO, cmd, peerPath)
117 | return msg
118 | case "CHAINCODE_INVOKE":
119 | if orderCache == nil {
120 | return "desc_3"+"|" + ordererName
121 | }
122 | cmd := exec.Command(peerBin, "chaincode", "invoke", "-n", name, "-c", json, "-C", channelId, "-o", ordererEndpoint)
123 | msg := run(true, ALLINFO, cmd, peerPath)
124 | return msg
125 | case "CHAINCODE_QUERY":
126 | if orderCache == nil {
127 | return "desc_3"+"|" + ordererName
128 | }
129 | cmd := exec.Command(peerBin, "chaincode", "query", "-n", name, "-c", json, "-C", channelId, "-o", ordererEndpoint)
130 | msg := run(true, ALLINFO, cmd, peerPath)
131 | return msg
132 | }
133 | return ""
134 | }
135 |
136 | func ExecOrderer(cmdInfo map[string]string) string {
137 | cmd := cmdInfo["Cmd"]
138 | nodeName := cmdInfo["NodeName"]
139 | cacheNodeName := orderers + "." + nodeName
140 | ordererPath := filepath.Join(OrdererDir, nodeName)
141 | ordererBin := WindowsBin(ordererBin)
142 | switch cmd {
143 | case "NODE_START":
144 | cache := util.Caches.Get(cacheNodeName)
145 | if cache != nil {
146 | return "node_already_run"
147 | }
148 |
149 | cmd := exec.Command(ordererBin, "start")
150 | util.Caches.Set(cacheNodeName, cmd)
151 | return run(false, ALLINFO, cmd, ordererPath)
152 | case "NODE_STOP":
153 | cache := util.Caches.Get(cacheNodeName)
154 | if cache == nil {
155 | return "node_already_stop"
156 | }
157 | v := cache.Value
158 | if _, ok := v.(*exec.Cmd); ok {
159 | err := v.(*exec.Cmd).Process.Kill()
160 | if err != nil {
161 | return err.Error()
162 | }
163 | }
164 | util.Caches.Delete(cacheNodeName)
165 | return "node_stop_ok"
166 | case "SEEK":
167 | cache := util.Caches.Get(cacheNodeName)
168 | if cache == nil {
169 | return "node_must_run"
170 | }
171 | return Seek(cmdInfo)
172 | }
173 | return ""
174 | }
175 |
176 | func ExecChainCode(cmdInfo map[string]string) string {
177 | cmd := cmdInfo["Cmd"]
178 | nodeName := cmdInfo["NodeName"]
179 | path := cmdInfo["Path"]
180 | peerEndPoint := cmdInfo["PeerEndPoint"]
181 | name := cmdInfo["Name"]
182 | peerNodeName := cmdInfo["PeerNodeName"]
183 | peerCacheNodeName := peers + "." + peerNodeName
184 | cacheNodeName := chaincodes + "." + nodeName+"."+peerNodeName
185 | switch cmd {
186 | case "NODE_START":
187 | cache := util.Caches.Get(peerCacheNodeName)
188 | if cache == nil {
189 | return "desc_4"+"|" + peerNodeName
190 | }
191 | cache = util.Caches.Get(cacheNodeName)
192 | if cache != nil {
193 | return "node_already_run"
194 | }
195 | path = filepath.Join(os.Getenv("GOPATH"), "src", path)
196 | cmd := exec.Command(path)
197 | env := "CORE_CHAINCODE_LOGLEVEL=debug"
198 | cmd.Env = append(os.Environ(), env)
199 | env = fmt.Sprintf("CORE_PEER_ADDRESS=%s", peerEndPoint)
200 | cmd.Env = append(cmd.Env, env)
201 | env = fmt.Sprintf("CORE_CHAINCODE_ID_NAME=%s", name)
202 | cmd.Env = append(cmd.Env, env)
203 |
204 | msg := run(false, OUTINFO, cmd, "")
205 | if msg == "" {
206 | util.Caches.Set(cacheNodeName, cmd)
207 | return "ok"
208 | }
209 | return msg
210 | case "NODE_STOP":
211 | cache := util.Caches.Get(cacheNodeName)
212 | if cache == nil {
213 | return "node_already_stop"
214 | }
215 | v := cache.Value
216 | util.Caches.Delete(cacheNodeName)
217 | if _, ok := v.(*exec.Cmd); ok {
218 | err := v.(*exec.Cmd).Process.Kill()
219 | if err != nil {
220 | return err.Error()
221 | }
222 | }
223 |
224 | return "ok"
225 | }
226 | return ""
227 | }
228 |
229 | func run(isSycn bool, outType int, cmd *exec.Cmd, config string) string {
230 |
231 | var stdoutBuf, stderrBuf bytes.Buffer
232 | stdoutIn, _ := cmd.StdoutPipe()
233 | stderrIn, _ := cmd.StderrPipe()
234 | var errStdout, errStderr error
235 | stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
236 | stderr := io.MultiWriter(os.Stderr, &stderrBuf)
237 |
238 | cmd.Env = append(cmd.Env, os.Environ()...)
239 | fabricCFGPath := "FABRIC_CFG_PATH=" + config
240 | cmd.Env = append(cmd.Env, fabricCFGPath)
241 |
242 | err := cmd.Start()
243 | if err != nil {
244 | return err.Error()
245 | }
246 | go func() {
247 | _, errStdout = io.Copy(stdout, stdoutIn)
248 | }()
249 | go func() {
250 | _, errStderr = io.Copy(stderr, stderrIn)
251 | }()
252 |
253 | if isSycn {
254 | cmd.Wait()
255 | } else {
256 | go func() {
257 | cmd.Wait()
258 | }()
259 |
260 | }
261 | outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
262 | if outType == OUTINFO {
263 | return outStr
264 | } else if outType == ERRINFO {
265 | return errStr
266 | }
267 | return fmt.Sprintf("Environment:%s\nCommand:%s\n\n\n%s\n\n\n%s", fabricCFGPath, strings.Join(cmd.Args, " "), errStr, outStr)
268 | }
269 |
270 | func Seek(cmdInfo map[string]string) string {
271 | nodeName := cmdInfo["NodeName"]
272 | channelId := cmdInfo["ChannelId"]
273 | seek := cmdInfo["Seek"]
274 | ordererPath := filepath.Join(OrdererDir, nodeName)
275 | os.Setenv("FABRIC_CFG_PATH", ordererPath)
276 | deliverClient, err := client.NewDeliverClient(channelId)
277 | if err != nil {
278 | return err.Error()
279 | }
280 | var block string
281 | if seek == "-2" {
282 | block, err = deliverClient.GetOldestBlock()
283 | if err != nil {
284 | return err.Error()
285 | }
286 | } else if seek == "-1" {
287 | block, err = deliverClient.GetNewestBlock()
288 | if err != nil {
289 | return err.Error()
290 | }
291 | } else {
292 | i, err := strconv.ParseUint(seek, 10, 64)
293 | if err != nil {
294 | return err.Error()
295 | }
296 | block, err = deliverClient.GetSpecifiedBlock(i)
297 | if err != nil {
298 | return err.Error()
299 | }
300 | }
301 | return block
302 |
303 | }
304 |
--------------------------------------------------------------------------------
/server/pkg/entity/organization.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "crypto"
5 | "crypto/x509"
6 | "encoding/pem"
7 | "errors"
8 | "github.com/hyperledger/fabric/bccsp"
9 | "github.com/hyperledger/fabric/bccsp/factory"
10 | "github.com/hyperledger/fabric/bccsp/signer"
11 | "github.com/hyperledger/fabric/common/tools/cryptogen/ca"
12 | "github.com/hyperledger/fabric/common/tools/cryptogen/csp"
13 | "github.com/hyperledger/fabric/common/tools/cryptogen/msp"
14 | "io/ioutil"
15 | "os"
16 | "path/filepath"
17 | "strings"
18 |
19 | "github.com/fabric-lab/hyperledger-fabric-manager/server/pkg/store"
20 | )
21 |
22 | type Organization struct {
23 | Country string
24 | Province string
25 | Locality string
26 | Organization string
27 | CommonName string
28 | OrganizationalUnit string
29 | StreetAddress string
30 | PostalCode string
31 | PEMs []PEM
32 | MSPs []MSP
33 | }
34 |
35 | type PEM struct {
36 | Name string
37 | Key string
38 | Cert string
39 | Type string
40 | }
41 |
42 | type MSP struct {
43 | Name string
44 | Path string
45 | Type string
46 | Role string
47 | }
48 |
49 | var (
50 | deliver = "./bin/deliver_stdout"
51 | )
52 |
53 | func (o *Organization) Create() error {
54 |
55 | o.generateRootCa()
56 | nodeName, mspPath, err := o.initMsp(msp.ORDERER)
57 | if err != nil {
58 | return err
59 | }
60 | o.MSPs = append(o.MSPs, MSP{Name: nodeName, Path: mspPath, Type: "orderer", Role: "orderer"})
61 |
62 | nodeName, mspPath, err = o.initMsp(msp.PEER)
63 | if err != nil {
64 | return err
65 | }
66 | o.MSPs = append(o.MSPs, MSP{Name: nodeName, Path: mspPath, Type: "peer", Role: "peer"})
67 |
68 | nodeName, mspPath, err = o.initAdminMsp()
69 | if err != nil {
70 | return err
71 | }
72 | o.MSPs = append(o.MSPs, MSP{Name: nodeName, Path: mspPath, Type: "peer", Role: "admin"})
73 |
74 | return nil
75 | }
76 |
77 | func (o *Organization) generateRootCa() error {
78 | os.RemoveAll(tempDir)
79 |
80 | // generate ROOT CA
81 | rootCA, err := ca.NewCA(tempDir, o.Organization, "ca."+o.CommonName, o.Country, o.Province, o.Locality, o.OrganizationalUnit, o.StreetAddress, o.PostalCode)
82 | if err != nil {
83 | return err
84 | }
85 | pem, _ := getPEM(rootCA.Name, "root")
86 | o.PEMs = append(o.PEMs, *pem)
87 |
88 | // generate TLS CA
89 | os.RemoveAll(tempDir)
90 | tlsCA, err := ca.NewCA(tempDir, o.Organization, "tlsca."+o.CommonName, o.Country, o.Province, o.Locality, o.OrganizationalUnit, o.StreetAddress, o.PostalCode)
91 | if err != nil {
92 | return err
93 | }
94 | pem, _ = getPEM(tlsCA.Name, "tls")
95 | o.PEMs = append(o.PEMs, *pem)
96 | // generate Admin CA
97 | o.generateAdminCa(rootCA)
98 |
99 | return nil
100 | }
101 |
102 | func (o *Organization) generateAdminCa(signCA *ca.CA) error {
103 | os.RemoveAll(tempDir)
104 | // generate private key
105 | priv, _, err := csp.GeneratePrivateKey(tempDir)
106 | if err != nil {
107 | return err
108 | }
109 |
110 | // get public key
111 | ecPubKey, err := csp.GetECPublicKey(priv)
112 | if err != nil {
113 | return err
114 | }
115 |
116 | var ous []string
117 |
118 | _, err = signCA.SignCertificate(tempDir,
119 | "admin@"+o.CommonName, ous, nil, ecPubKey, x509.KeyUsageDigitalSignature, []x509.ExtKeyUsage{})
120 | if err != nil {
121 | return err
122 | }
123 | pem, _ := getPEM("admin@"+o.CommonName, "admin")
124 | o.PEMs = append(o.PEMs, *pem)
125 | return nil
126 | }
127 |
128 | func (o *Organization) initMsp(nodeType int) (string, string, error) {
129 | var (
130 | node string
131 | nodeName string
132 | )
133 |
134 | if nodeType == msp.ORDERER {
135 | node = "order"
136 | nodeName = "order." + o.CommonName
137 | } else {
138 | node = "peer"
139 | nodeName = "peer0." + o.CommonName
140 | }
141 |
142 | signCA, _, err := GetCA("ca."+o.CommonName, *o)
143 | if err != nil {
144 | return "", "", err
145 | }
146 | tlsCA, _, err := GetCA("tlsca."+o.CommonName, *o)
147 | if err != nil {
148 | return "", "", err
149 | }
150 | mspPath := Path(mspDir, o.CommonName, node+"s", nodeName)
151 | if err != nil {
152 | return "", "", err
153 | }
154 | os.RemoveAll(mspPath)
155 | err = msp.GenerateLocalMSP(mspPath, nodeName, []string{}, signCA, tlsCA, nodeType, false)
156 | if err != nil {
157 | return "", "", err
158 | }
159 |
160 | //copy admin cert
161 | adminCA, _, err := GetCA("admin@"+o.CommonName, *o)
162 | if err != nil {
163 | return "", "", err
164 | }
165 | adminPath := filepath.Join(mspPath, "msp", "admincerts")
166 | os.RemoveAll(adminPath)
167 | adminCertPath := filepath.Join(adminPath)
168 | os.Mkdir(adminCertPath, 0755)
169 | adminCertPath = filepath.Join(adminCertPath, adminCA.Name+"-cert.pem")
170 | pemExport(adminCertPath, "CERTIFICATE", adminCA.SignCert.Raw)
171 | return nodeName, mspPath, nil
172 |
173 | }
174 |
175 | func (o *Organization) initAdminMsp() (string, string, error) {
176 | node := "admin"
177 | nodeName := "admin." + o.CommonName
178 | signCA, _, err := GetCA("ca."+o.CommonName, *o)
179 | if err != nil {
180 | return "", "", err
181 | }
182 | tlsCA, _, err := GetCA("tlsca."+o.CommonName, *o)
183 | if err != nil {
184 | return "", "", err
185 | }
186 | mspPath := Path(mspDir, o.CommonName, node+"s", nodeName)
187 | if err != nil {
188 | return "", "", err
189 | }
190 | os.RemoveAll(mspPath)
191 | err = msp.GenerateLocalMSP(mspPath, nodeName, []string{}, signCA, tlsCA, msp.PEER, false)
192 | if err != nil {
193 | return "", "", err
194 | }
195 |
196 | //copy admin cert
197 | adminCA, key, err := GetCA("admin@"+o.CommonName, *o)
198 | if err != nil {
199 | return "", "", err
200 | }
201 | adminPath := filepath.Join(mspPath, "msp", "admincerts")
202 | os.RemoveAll(adminPath)
203 | adminCertPath := filepath.Join(adminPath)
204 | os.Mkdir(adminCertPath, 0755)
205 | adminCertPath = filepath.Join(adminCertPath, adminCA.Name+"-cert.pem")
206 | pemExport(adminCertPath, "CERTIFICATE", adminCA.SignCert.Raw)
207 |
208 | signPath := filepath.Join(mspPath, "msp", "signcerts")
209 | os.RemoveAll(signPath)
210 | signCertPath := filepath.Join(signPath)
211 | os.Mkdir(signCertPath, 0755)
212 | signCertPath = filepath.Join(signCertPath, nodeName+"-cert.pem")
213 | pemExport(signCertPath, "CERTIFICATE", adminCA.SignCert.Raw)
214 |
215 | //key
216 | keyPath := filepath.Join(mspPath, "msp", "keystore")
217 | writeAdminKey(key, keyPath)
218 | if err != nil {
219 | return "", "", err
220 | }
221 | return nodeName, mspPath, nil
222 |
223 | }
224 |
225 | func pemExport(path, pemType string, bytes []byte) error {
226 | //write pem out to file
227 | file, err := os.Create(path)
228 | if err != nil {
229 | return err
230 | }
231 | defer file.Close()
232 |
233 | return pem.Encode(file, &pem.Block{Type: pemType, Bytes: bytes})
234 | }
235 |
236 | func getPEM(name string, caType string) (*PEM, error) {
237 | pem := &PEM{Name: name, Type: caType}
238 | files, err := ioutil.ReadDir(tempDir)
239 | if err != nil {
240 | return pem, err
241 | }
242 | for _, f := range files {
243 | b, err := ioutil.ReadFile(filepath.Join(tempDir,f.Name()))
244 | if err != nil {
245 | return pem, err
246 | }
247 | if strings.Index(f.Name(), "cert.pem") != -1 {
248 | pem.Cert = string(b)
249 | } else {
250 | pem.Key = string(b)
251 | }
252 | }
253 | return pem, nil
254 | }
255 |
256 | func writeAdminKey(key string, path string) error {
257 | files, err := ioutil.ReadDir(path)
258 | if err != nil {
259 | return err
260 | }
261 | for _, f := range files {
262 | file, err := os.Create(filepath.Join(path, f.Name()))
263 | if err != nil {
264 | return err
265 | }
266 | defer file.Close()
267 | _, err = file.WriteString(string(key))
268 | if err != nil {
269 | return err
270 | }
271 | return nil
272 | }
273 | return nil
274 | }
275 |
276 | func GetCA(commonName string, organization Organization) (*ca.CA, string, error) {
277 | for _, v := range organization.PEMs {
278 | if v.Name == commonName {
279 | cBlock, _ := pem.Decode([]byte(v.Cert))
280 | if cBlock == nil {
281 | return nil, "", errors.New("no PEM data found for certificate")
282 | }
283 | cert, err := x509.ParseCertificate(cBlock.Bytes)
284 | if err != nil {
285 | return nil, "", err
286 | }
287 | _, signer, err := LoadSigner(v.Key)
288 | if err != nil {
289 | return nil, "", err
290 | }
291 | ca := &ca.CA{
292 | Name: commonName,
293 | Signer: signer,
294 | SignCert: cert,
295 | Country: organization.Country,
296 | Province: organization.Province,
297 | Locality: organization.Locality,
298 | OrganizationalUnit: organization.OrganizationalUnit,
299 | StreetAddress: organization.StreetAddress,
300 | PostalCode: organization.PostalCode,
301 | }
302 |
303 | return ca, v.Key, nil
304 | }
305 | }
306 | return nil, "", nil
307 | }
308 |
309 | func getCAName(caType string, commonName string) string {
310 | if caType == "ca" {
311 | return "ca." + commonName
312 | } else if caType == "tls" {
313 | return "tlsca." + commonName
314 | }
315 | return "ca." + commonName
316 | }
317 |
318 | func LoadSigner(rawKey string) (bccsp.Key, crypto.Signer, error) {
319 | var err error
320 | var priv bccsp.Key
321 | var s crypto.Signer
322 |
323 | opts := &factory.FactoryOpts{
324 | ProviderName: "SW",
325 | SwOpts: &factory.SwOpts{
326 | HashFamily: "SHA2",
327 | SecLevel: 256,
328 | },
329 | }
330 |
331 | csp, err := factory.GetBCCSPFromOpts(opts)
332 | if err != nil {
333 | return nil, nil, err
334 | }
335 |
336 | block, _ := pem.Decode([]byte(rawKey))
337 | priv, err = csp.KeyImport(block.Bytes, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: true})
338 | if err != nil {
339 | return nil, nil, err
340 | }
341 |
342 | s, err = signer.New(csp, priv)
343 | if err != nil {
344 | return nil, nil, err
345 | }
346 |
347 | return priv, s, err
348 | }
349 |
350 | func getMspByName(mspName string) (MSP, error) {
351 |
352 | var m MSP
353 | records, err := store.Bt.View(organizations)
354 | if err != nil {
355 | return m, err
356 | }
357 | for _, v := range records {
358 | i := MapToEntity(v, organizations)
359 | if o, ok := i.(*Organization); ok {
360 | for _, m := range o.MSPs {
361 | if mspName == m.Name {
362 | return m, nil
363 | }
364 | }
365 | }
366 |
367 | }
368 | return m, errors.New("Not find Msp")
369 | }
370 |
371 | func getOrgByName(oName string) (*Organization, error) {
372 | var o *Organization
373 | i, err := store.Bt.ViewByKey(organizations, oName)
374 | if err != nil {
375 | return o, err
376 | }
377 | i = MapToEntity(i, organizations)
378 | if o, ok := i.(*Organization); ok {
379 | return o, nil
380 | }
381 | return o, nil
382 | }
383 |
--------------------------------------------------------------------------------