1 | <!DOCTYPE html>
|
2 | <html>
|
3 | <body>
|
4 | <style>
|
5 | .node{
|
6 | display: inline-block;
|
7 | float: left;
|
8 | margin:10px;
|
9 | }
|
10 | </style>
|
11 | <div id="react"></div>
|
12 | <script src="./js/react.min.js"></script>
|
13 | <script src="./js/reactdom.min.js"></script>
|
14 | <script src="./js/babel.min.js"></script>
|
15 | <script type="text/babel">
|
16 | class Node extends React.Component {
|
17 | constructor(props) {
|
18 | super(props);
|
19 | this.onStart = this.onStart.bind(this);
|
20 | this.onStop = this.onStop.bind(this);
|
21 | }
|
22 | onStart() {
|
23 | this.props.onStart(this.props.ip, this.props.port);
|
24 | }
|
25 | onStop() {
|
26 | this.props.onStop(this.props.ip, this.props.port);
|
27 | }
|
28 | render() {
|
29 | const { ip, port, hash, isStart } = this.props;
|
30 | return (
|
31 | <div className="node">
|
32 | <div>{ip}</div>
|
33 | <div>{port}</div>
|
34 | <div>{hash}</div>
|
35 | <button onClick={this.onStart} disabled={isStart}>
|
36 | Start
|
37 | </button>
|
38 | <button onClick={this.onStop} disabled={!isStart}>
|
39 | Stop
|
40 | </button>
|
41 | </div>
|
42 | );
|
43 | }
|
44 | }
|
45 | class NodeList extends React.Component {
|
46 | render() {
|
47 | var { nodes } = this.props;
|
48 | return (
|
49 | <div>
|
50 | {nodes.map(node => {
|
51 | return (
|
52 | <Node
|
53 | {...node}
|
54 | onStart={this.props.onStart}
|
55 | onStop={this.props.onStop}
|
56 | />
|
57 | );
|
58 | })}
|
59 | </div>
|
60 | );
|
61 | }
|
62 | }
|
63 | class Deploy extends React.Component {
|
64 | constructor(props) {
|
65 | super(props);
|
66 | this.onDeploy = this.onDeploy.bind(this);
|
67 | }
|
68 | onDeploy() {
|
69 | var deployJson = this.deployJson.value;
|
70 | if (deployJson != '') {
|
71 | var deployObj = null;
|
72 | try {
|
73 | deployObj = JSON.parse(deployJson);
|
74 | } catch (e) {
|
75 | console.log('JSON parse error');
|
76 | }
|
77 | if (deployObj) {
|
78 | this.props.onDeploy(deployObj);
|
79 | }
|
80 | }
|
81 | }
|
82 | render() {
|
83 | return (
|
84 | <div
|
85 | style={{
|
86 | margin: '0 auto',
|
87 | display: 'block',
|
88 | width: '300px'
|
89 | }}
|
90 | >
|
91 | <textarea
|
92 | placeholder="Deploy json here...."
|
93 | style={{
|
94 | height: '300px',
|
95 | width: '300px'
|
96 | }}
|
97 | ref={t => (this.deployJson = t)}
|
98 | />
|
99 | <button onClick={this.onDeploy}>Deploy</button>
|
100 | </div>
|
101 | );
|
102 | }
|
103 | }
|
104 | class NodeContainer extends React.Component {
|
105 | constructor(props) {
|
106 | super(props);
|
107 | this.state = {
|
108 | nodes: []
|
109 | };
|
110 | this.onStart = this.onStart.bind(this);
|
111 | this.onStop = this.onStop.bind(this);
|
112 | this.onDeploy = this.onDeploy.bind(this);
|
113 | }
|
114 | componentDidMount() {
|
115 | fetch('/clusterhub/nodes')
|
116 | .then(res => res.json())
|
117 | .then(res => {
|
118 | this.setState({ nodes: res });
|
119 | res.map(node => {
|
120 | fetch(`http://${node.ip}:${parseInt(node.port) + 1}/node/isStart`)
|
121 | .then(res => res.text())
|
122 | .then(res => {
|
123 | if (res == 'true') {
|
124 | var newNodes = [];
|
125 | this.state.nodes.forEach(node1 => {
|
126 | if (node1.ip == node.ip && node1.port == node.port) {
|
127 | newNodes.push(Object.assign({}, node1, { isStart: true }));
|
128 | } else {
|
129 | newNodes.push(node1);
|
130 | }
|
131 | });
|
132 | this.setState({ nodes: newNodes });
|
133 | } else {
|
134 | var newNodes = [];
|
135 | this.state.nodes.forEach(node1 => {
|
136 | if (node1.ip == node.ip && node1.port == node.port) {
|
137 | newNodes.push(Object.assign({}, node1, { isStart: false }));
|
138 | } else {
|
139 | newNodes.push(node1);
|
140 | }
|
141 | });
|
142 | this.setState({ nodes: newNodes });
|
143 | }
|
144 | });
|
145 | });
|
146 | });
|
147 | }
|
148 | onStop(ip, port) {
|
149 | fetch(`http://${ip}:${parseInt(port) + 1}/node/stop`)
|
150 | .then(res => res.text())
|
151 | .then(res => {
|
152 | var newNodes = [];
|
153 | this.state.nodes.forEach(node => {
|
154 | console.log(res);
|
155 | if (node.ip == ip && node.port == port && res == 'Stopped') {
|
156 | newNodes.push(Object.assign({}, node, { isStart: false }));
|
157 | } else {
|
158 | newNodes.push(node);
|
159 | }
|
160 | });
|
161 | this.setState({ nodes: newNodes });
|
162 | });
|
163 | }
|
164 | onStart(ip, port) {
|
165 | fetch(`http://${ip}:${parseInt(port) + 1}/node/start`)
|
166 | .then(res => res.text())
|
167 | .then(res => {
|
168 | var newNodes = [];
|
169 | this.state.nodes.forEach(node => {
|
170 | console.log(res);
|
171 | if (node.ip == ip && node.port == port && res == 'Server start') {
|
172 | newNodes.push(Object.assign({}, node, { isStart: true }));
|
173 | } else {
|
174 | newNodes.push(node);
|
175 | }
|
176 | });
|
177 | this.setState({ nodes: newNodes });
|
178 | });
|
179 | }
|
180 | onDeploy(obj) {
|
181 | var myHeaders = new Headers();
|
182 | myHeaders.append('Content-Type', 'application/json');
|
183 | if (this.state.nodes.length > 1) {
|
184 | var first = fetch(
|
185 | `http://${this.state.nodes[0].ip}:${parseInt(this.state.nodes[0].port) +
|
186 | 1}/node/deploy`,
|
187 | {
|
188 | method: 'POST',
|
189 | headers: myHeaders,
|
190 | body: JSON.stringify(obj)
|
191 | }
|
192 | ).then(res => res.text());
|
193 | var remainingNodes = this.state.nodes.slice(1);
|
194 | console.log(this.state.nodes, remainingNodes);
|
195 | remainingNodes.reduce((pre, next) => {
|
196 | return pre.then(res => {
|
197 | return fetch(
|
198 | `http://${next.ip}:${parseInt(next.port) + 1}/node/deploy`,
|
199 | {
|
200 | method: 'POST',
|
201 | headers: myHeaders,
|
202 | body: JSON.stringify(obj)
|
203 | }
|
204 | ).then(res => res.text());
|
205 | });
|
206 | }, first);
|
207 | }
|
208 | }
|
209 | render() {
|
210 | var { nodes } = this.state;
|
211 | return (
|
212 | <div>
|
213 | <Deploy onDeploy={this.onDeploy} />
|
214 | <NodeList nodes={nodes} onStart={this.onStart} onStop={this.onStop} />
|
215 | </div>
|
216 | );
|
217 | }
|
218 | }
|
219 | ReactDOM.render(<NodeContainer />, document.getElementById('react'));
|
220 | </script>
|
221 | </body>
|
222 |
|
223 | </html>
|