├── .gitattributes ├── server ├── static │ ├── fonts │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2 │ │ ├── 6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdu.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwkxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmRduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmhduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2 │ │ ├── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2 │ │ └── 6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2 │ └── index.html ├── main.go └── pkg │ ├── entity │ ├── entity.go │ ├── chainCode.go │ ├── common.go │ ├── consortium.go │ ├── peer.go │ ├── orderer.go │ ├── channel.go │ ├── cmd.go │ └── organization.go │ ├── util │ ├── cache.go │ └── copy.go │ ├── store │ ├── common.go │ └── bolt.go │ ├── certificate │ ├── certificate.go │ └── certificate_test.go │ ├── api.go │ └── client │ └── order_deliver.go ├── app ├── bower.json ├── routes.js ├── util.js ├── json_schema │ ├── JsonWidget.js │ ├── channel.js │ ├── organization.js │ ├── consortium.js │ ├── chaincode.js │ ├── peer.js │ ├── orderer.js │ └── cert.js ├── components │ ├── Footer.js │ ├── Peer.js │ ├── Channel.js │ ├── Orderer.js │ ├── ChainCode.js │ ├── Consortium.js │ ├── Organization.js │ ├── Navbar.js │ ├── EnhancedTableToolbar.js │ ├── EnhancedTableHead.js │ ├── JsonForm.js │ ├── App.js │ ├── OrganizationCard.js │ ├── CertCard.js │ ├── Home.js │ ├── ChainCodeCard.js │ ├── ChannelCard.js │ ├── PeerCard.js │ ├── OrdererCard.js │ ├── ConsortiumCard.js │ ├── MspTable.js │ ├── CertTable.js │ ├── ConsortiumTable.js │ ├── OrganizationTable.js │ ├── OrdererTable.js │ ├── PeerTable.js │ ├── ChannelTable.js │ ├── OrdererConsoleCard.js │ └── ChainCodeTable.js ├── main.js ├── package.json ├── message │ ├── zh_CN.js │ └── en_US.js └── gulpfile.js ├── .gitignore ├── README-zh.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Go 2 | *.css linguist-language=Go 3 | *.html linguist-language=Go 4 | -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdu.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdu.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwkxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwkxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmRduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmRduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmhduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmhduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwmxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2 -------------------------------------------------------------------------------- /server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabric-lab/hyperledger-fabric-manager/HEAD/server/static/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2 -------------------------------------------------------------------------------- /app/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fabric-manager", 3 | "dependencies": { 4 | "jquery": "^2.1.4", 5 | "bootstrap": "^3.3.5", 6 | "magnific-popup": "^1.0.0", 7 | "toastr": "^2.1.1" 8 | } 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | server/vendor 2 | server/fabric_config.db 3 | server/config/ 4 | server/.gopmfile 5 | app/node_modules/ 6 | app/bower_components/ 7 | server/bin 8 | server/static/css/ 9 | server/static/js/ 10 | server/server 11 | server/dist 12 | -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route,Switch} from 'react-router-dom'; 3 | import App from './components/App'; 4 | import Home from './components/Home'; 5 | 6 | 7 | export default ( 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /app/util.js: -------------------------------------------------------------------------------- 1 | export function msgToObj(msg){ 2 | let msgs = msg.split("|"); 3 | let id={} 4 | let values={} 5 | for(let i=0;i { 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 |
18 |
19 |
20 |
21 |

Information and Copyright

22 |

Powered by golang, bolt and React with Flux architecture and server-side rendering.

23 |

You may view the Source Code behind this project on GitHub.

24 |
25 | 26 |
27 |
28 |
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 |
44 | 45 |
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 |
53 | {bt} 54 |
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( system-channel ) 151 | channels.forEach(function(channel){ 152 | items.push( {channel.Name} ) 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 | --------------------------------------------------------------------------------