UserCustom.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. import React, {Component} from 'react';
  2. import {Link} from 'react-router-dom';
  3. import {deployAll, graphqlUrl, storeFile, copyCos} from "../../../config";
  4. import {ADD_APIGROUP, ADD_APIGWPATH, ADD_DEPLOY, ADD_PROJECT, SHOW_CLOUD} from "../../../gql";
  5. import {CloudConfig} from "../../../login/CloudConfig";
  6. import {Layout, Button, message, Modal, Icon, Steps, Row, Col, Radio, Input} from 'antd';
  7. import {FormattedMessage} from 'react-intl';
  8. import {request} from 'graphql-request'
  9. import {idGen} from "../../../func";
  10. import axios from 'axios';
  11. import './index.css';
  12. const {Content} = Layout;
  13. const Step = Steps.Step;
  14. const RadioGroup = Radio.Group;
  15. axios.defaults.withCredentials = true;
  16. class UserCustom extends Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. userID: props.userID,
  21. bucketName: props.bucketName,
  22. cloudName: 'tencent',
  23. disableDeployButton: false,
  24. deployFailed: false,
  25. cloudID: '',
  26. secretID: '',
  27. secretKey: '',
  28. appId: ''
  29. };
  30. console.log(this.state, 'userCustom state')
  31. }
  32. getCloudDetail = (cloudID, secretID, secretKey, appId) => {
  33. this.setState({
  34. cloudID,
  35. secretID,
  36. secretKey,
  37. appId
  38. })
  39. };
  40. componentWillMount() {
  41. this._isMounted = true;
  42. }
  43. componentWillUnmount() {
  44. this._isMounted = false;
  45. }
  46. render() {
  47. let {disableDeployButton, userID, cloudID, bucketName, deployFailed, appId, secretID, secretKey} = this.state;
  48. return (
  49. <Content className="content">
  50. <div>
  51. <div className="column-menu" onClick={() => {
  52. this.props.backToMe()
  53. }}>
  54. <Icon type="left" style={{color: '#3187FA'}}/>
  55. <FormattedMessage id="back to case show"/>
  56. </div>
  57. <Row>
  58. <Col span={9} offset={2}>
  59. <div className='step-kind'>
  60. 发布体验
  61. </div>
  62. <Steps direction="vertical" current={2}>
  63. <Step title=<Step00/> />
  64. <Step title=<Step01/> />
  65. </Steps>
  66. </Col>
  67. <Col span={9} offset={2}>
  68. <div className='step-kind'>
  69. 发布上线
  70. </div>
  71. <Steps direction="vertical" current={5}>
  72. <Step title=<Step10/> />
  73. <Step title=<Step11 getCloudDetail={this.getCloudDetail} userID={userID}/> />
  74. <Step title=<Step12/> />
  75. <Step title=<Step13/> />
  76. <Step title=
  77. <Step14
  78. getPrimaryConfigDetail={this.getPrimaryConfigDetail}
  79. userID={userID}
  80. bucketName={bucketName}
  81. secretID={secretID}
  82. secretKey={secretKey}
  83. appId={appId}
  84. cloudID={cloudID}
  85. />
  86. />
  87. </Steps>
  88. </Col>
  89. </Row>
  90. </div>
  91. </Content>
  92. )
  93. }
  94. }
  95. export default UserCustom;
  96. class CloudQueryAndConfig extends Component {
  97. constructor(props) {
  98. super(props);
  99. this.state = {
  100. cloudName: props.cloudName,
  101. userID: props.userID,
  102. cloudID: '',
  103. secretID: '',
  104. secretKey: '',
  105. appId: '',
  106. visible: false,
  107. confirmLoading: false,
  108. }
  109. }
  110. searchCloud = () => {
  111. this._isMounted = true;
  112. let {userID, cloudName} = this.state;
  113. // 如果登录,查询该用户是否设置 cloud
  114. request(graphqlUrl, SHOW_CLOUD, {user_id: userID}).then(data => {
  115. let clouds = data.cloud_by_props.filter(cloud => cloud.cloudName === cloudName);
  116. // 如果限制一个云服务商一个 cloud,那么就是clouds[0]
  117. if (clouds.length === 1) {
  118. let cloud = clouds[0];
  119. let {id, secretId, secretKey, appId} = cloud;
  120. if (this._isMounted) {
  121. this.setState({
  122. cloudID: id,
  123. secretID: secretId,
  124. secretKey,
  125. appId,
  126. });
  127. }
  128. this.props.getCloudDetail(id, secretId, secretKey, appId);
  129. } else if (clouds.length > 1) {
  130. console.log('数据库有多个同一云服务商的 key');
  131. } else {
  132. if (this._isMounted) {
  133. console.log('数据库没有云服务商的 key');
  134. }
  135. }
  136. }
  137. )
  138. };
  139. showModal = () => {
  140. if (this.state.userID) {
  141. this.setState({
  142. visible: true,
  143. });
  144. } else {
  145. message.warning('请先登录');
  146. }
  147. };
  148. handleCancel = () => {
  149. this.setState({
  150. visible: false,
  151. });
  152. this.props.reCheck();
  153. };
  154. componentWillMount() {
  155. this.searchCloud();
  156. this._isMounted = true;
  157. }
  158. componentWillUnmount() {
  159. this._isMounted = false;
  160. }
  161. componentWillReceiveProps(next) {
  162. this.setState({
  163. cloudName: next.cloudName,
  164. userID: next.userID,
  165. }, this.searchCloud);
  166. }
  167. render() {
  168. let {visible, confirmLoading, cloudName} = this.state;
  169. return (
  170. <div>
  171. <Button type='primary' onClick={this.showModal}>填写秘钥</Button>
  172. <Modal
  173. title="云服务商秘钥设置"
  174. visible={visible}
  175. confirmLoading={confirmLoading}
  176. footer={null}
  177. onCancel={this.handleCancel}
  178. >
  179. <CloudConfig cloudName={cloudName}/>
  180. </Modal>
  181. </div>
  182. )
  183. }
  184. }
  185. class NameAndDB extends Component {
  186. constructor(props) {
  187. super(props);
  188. this.state = {
  189. userID: props.userID,
  190. visible: false,
  191. confirmLoading: false,
  192. customName: props.bucketName + '-' + props.userID,
  193. dbKind: 'fc-db',
  194. host: '',
  195. db: '',
  196. username: '',
  197. password: '',
  198. admin: '',
  199. disableDeployButton: false
  200. }
  201. }
  202. showModal = () => {
  203. if (this.state.userID) {
  204. this.setState({
  205. visible: true,
  206. });
  207. } else {
  208. message.warning('请先登录');
  209. }
  210. };
  211. handleCancel = () => {
  212. this.setState({
  213. visible: false,
  214. });
  215. };
  216. componentWillMount() {
  217. this._isMounted = true;
  218. }
  219. componentWillUnmount() {
  220. this._isMounted = false;
  221. }
  222. deploy = (userID, dbKind, bucketName, customName, cloudID) => {
  223. this._isMounted = true;
  224. let _this = this;
  225. if (bucketName === '') {
  226. console.log('state, 没有传值');
  227. } else {
  228. console.log('开始调用');
  229. this.setState({
  230. disableDeployButton: true
  231. });
  232. let now = new Date().getTime(),
  233. functionName = userID + '_' + customName,
  234. serviceName = userID + '_' + customName,
  235. resources = [`${bucketName}/schema.edn`, `${bucketName}/resolve-map.edn`, `${bucketName}/${dbKind}/${userID}/deploy-conf.edn`, `${bucketName}/html/index.html`, `${bucketName}/wx-config.edn`];
  236. if (dbKind === 'mongodb') {
  237. resources.push(`${bucketName}/${dbKind}/${userID}/mongo-config.edn`)
  238. }
  239. console.log('now', now, 'functionName', functionName, 'serviceName', serviceName, 'resources', resources);
  240. axios.post(deployAll,
  241. {
  242. 'fc-name': functionName,
  243. 'bucket': dbKind === 'mongodb' ? 'native-fc' : 'fcdb-deploy',
  244. 'object-file': 'fc-only.zip',
  245. 'res-bucket': 'case',
  246. 'resources': resources,
  247. 'service-name': serviceName,
  248. 'path': "/*"
  249. })
  250. .then(function (response) {
  251. console.log('response', response);
  252. if (response['data']['apigw-result'] && response['data']['fc-result']) {
  253. // 处理数据
  254. let result = response['data']['apigw-result'];
  255. let apiData = result['api-info'];
  256. let serviceData = result['service-info'];
  257. // 存数据
  258. let pathID = idGen('path'),
  259. groupID = idGen('group'),
  260. deployID = idGen('deploy'),
  261. projectID = idGen('project');
  262. let {apiId, path, method} = apiData;
  263. let {serviceName, serviceId, subDomain} = serviceData;
  264. let pathVarObj = {
  265. id: idGen('path'),
  266. user_id: userID,
  267. apiGWGroup_id: pathID,
  268. deploy_id: deployID,
  269. apiGWName: functionName,
  270. requestMethod: method,
  271. apiGWPath: path,
  272. apiId: apiId,
  273. apiGWDesc: '',
  274. serviceType: 'SCF',
  275. timeout: 300,
  276. createdAt: now,
  277. updatedAt: ''
  278. };
  279. let projectVarObj = {
  280. id: projectID,
  281. projectType: 'case',
  282. cloud_id: cloudID,
  283. user_id: userID,
  284. projectName: functionName,
  285. database_id: '',
  286. apiGWGroup_id: '',
  287. deploy_id: '',
  288. case_id: '',
  289. wxConfig_id: '',
  290. schema_id: '',
  291. createdAt: now,
  292. updatedAt: '',
  293. projectStatus: 'deployed'
  294. };
  295. let groupVarObj = {
  296. id: groupID,
  297. cloud_id: cloudID,
  298. user_id: userID,
  299. userStatus: '',
  300. userDomain: '',
  301. groupName: serviceName,
  302. frontType: '',
  303. region: '',
  304. environmentName: '',
  305. defaultDomain: subDomain,
  306. status: '',
  307. serviceId: serviceId,
  308. createdAt: now,
  309. updatedAt: ''
  310. };
  311. let deployVarObj = {
  312. id: deployID,
  313. cloud_id: cloudID,
  314. functionName,
  315. cosObjectName: '',
  316. region: '',
  317. cosBucketRegion: '',
  318. description: '',
  319. cosBucketName: '',
  320. vpcId: '',
  321. subnetId: '',
  322. memorySize: 512,
  323. timeout: 300,
  324. handler: '',
  325. serviceName: "",
  326. fc_id: '',
  327. createdAt: now,
  328. updatedAt: ''
  329. };
  330. let add_apigwpath = request(graphqlUrl, ADD_APIGWPATH, pathVarObj),
  331. add_project = request(graphqlUrl, ADD_PROJECT, projectVarObj),
  332. add_apigroup = request(graphqlUrl, ADD_APIGROUP, groupVarObj),
  333. add_deploy = request(graphqlUrl, ADD_DEPLOY, deployVarObj);
  334. Promise.all([add_apigwpath, add_project, add_apigroup, add_deploy])
  335. .then(value => {
  336. console.log(value);
  337. // 展示数据
  338. if (_this._isMounted) {
  339. _this.setState({
  340. disableDeployButton: false
  341. });
  342. }
  343. _this.props.history.push({
  344. pathname: `/common/deploy`,
  345. state: {
  346. // 处理传回数据,直接拼接
  347. url: `http://${subDomain}/test/`
  348. }
  349. });
  350. })
  351. .catch(err => {
  352. console.log(err);
  353. });
  354. } else {
  355. console.log('deployAll 失败');
  356. _this.setState({
  357. deployFailed: true,
  358. disableDeployButton: false
  359. })
  360. }
  361. })
  362. .catch(function (error) {
  363. console.log('axios error', error);
  364. });
  365. }
  366. };
  367. storeEdnAndDeploy = (secretID='AKIDkYBvY0LOJ2bzCDmnMjz4xgFertmVJlVE', secretKey='zwjKk29TdcYP8g2FG5kCSWmz3wcH92lN', appId='1254337200', bucketName, dbKind, userID, admin, username, password, db, host, customName, cloudID) => {
  368. // store *.edn to cos
  369. let deployConf = dbKind === 'mongodb' ?
  370. ` {:secretId "${secretID}"\n` +
  371. ` :secretKey "${secretKey}"\n` +
  372. ` :appId "${appId}"\n` +
  373. ' :region "ap-beijing" \n' +
  374. ' }\n'
  375. :
  376. ` {:secretId "${secretID}"\n` +
  377. ` :secretKey "${secretKey}"\n` +
  378. ` :appId "${appId}"\n` +
  379. ' :region "ap-beijing"\n' +
  380. ' :bucket "fc-db"\n' +
  381. ' :trustStore "/etc/ssl/certs/java/cacerts"\n' +
  382. ` :fc-db-store "save/${bucketName}.dat"\n` +
  383. ' :fc-db-dir "fc-db"\n' +
  384. ' :local-tmp-dir "/tmp"\n' +
  385. ' :local-db-file "fcdb.dat"\n' +
  386. ' :update-tx? true \n' +
  387. ' :force-down? true\n' +
  388. ' }';
  389. let a = axios.post(storeFile, {
  390. 'file-name': `${bucketName}/${dbKind}/${userID}/deploy-conf.edn`,
  391. bucket: 'case',
  392. cont: deployConf
  393. });
  394. let cont = '{:uri {\n' +
  395. `:auth {:admin-db "${admin}"\n` +
  396. ` :u "${username}"\n` +
  397. ` :p "${password}"\n` +
  398. ` :host "${host}"}}\n` +
  399. ` :db-name "${db}"}`;
  400. let b = dbKind === 'mongodb' ?
  401. axios.post(storeFile, {
  402. 'file-name': `${bucketName}/${dbKind}/${userID}/mongo-config.edn`,
  403. bucket: 'case',
  404. cont
  405. })
  406. :
  407. Promise.resolve({status: 200});
  408. Promise.all([a, b]).then(value => {
  409. if (value.every(res => res.status === 200)) {
  410. console.log('store file success , start deploying');
  411. this.deploy(userID, dbKind, bucketName, customName, cloudID);
  412. }
  413. });
  414. }
  415. render() {
  416. let {
  417. visible,
  418. confirmLoading,
  419. userID,
  420. dbKind,
  421. host,
  422. db,
  423. username,
  424. password,
  425. admin,
  426. customName,
  427. disableDeployButton
  428. } = this.state;
  429. let {secretID, secretKey, appId, bucketName, cloudID} = this.props
  430. return (
  431. <div>
  432. <Button type='primary' onClick={this.showModal}>开始部署</Button>
  433. <Modal
  434. title="名称和数据库配置"
  435. visible={visible}
  436. confirmLoading={confirmLoading}
  437. footer={null}
  438. onCancel={this.handleCancel}
  439. >
  440. <div>
  441. <div>
  442. <div className={'schema-name'}><FormattedMessage id='Name'/></div>
  443. <div>
  444. <span className='item-title-cloud'><FormattedMessage id='name'/>:</span>
  445. <Input style={{width: 250}} value={this.state.customName}
  446. onChange={(e) => {
  447. this.setState({customName: e.target.value})
  448. }}/>
  449. </div>
  450. </div>
  451. <div style={{marginTop: 20}}>
  452. <div className={'schema-name'}><FormattedMessage id='DB Choose'/></div>
  453. <RadioGroup onChange={(e) => {
  454. this.setState({dbKind: e.target.value})
  455. }} value={this.state.dbKind}>
  456. <Radio value='fc-db'>fc-db</Radio>
  457. <Radio value='mongodb'>mongodb</Radio>
  458. </RadioGroup>
  459. </div>
  460. {
  461. this.state.dbKind === 'mongodb' ?
  462. <div>
  463. <div>
  464. <span className='item-title-cloud'>地址</span>
  465. <Input style={{width: 250}} value={this.state.host}
  466. onChange={(e) => {
  467. this.setState({host: e.target.value})
  468. }}
  469. />
  470. </div>
  471. <div>
  472. <span className='item-title-cloud'>数据库名称</span>
  473. <Input style={{width: 250}} value={this.state.db}
  474. onChange={(e) => {
  475. this.setState({db: e.target.value})
  476. }}
  477. />
  478. </div>
  479. <div>
  480. <span className='item-title-cloud'>验证数据库</span>
  481. <Input style={{width: 250}} value={this.state.admin}
  482. onChange={(e) => {
  483. this.setState({admin: e.target.value})
  484. }}
  485. />
  486. </div>
  487. <div>
  488. <span className='item-title-cloud'>用户名</span>
  489. <Input style={{width: 250}} value={this.state.username}
  490. onChange={(e) => {
  491. this.setState({username: e.target.value})
  492. }}
  493. />
  494. </div>
  495. <div>
  496. <span className='item-title-cloud'>密码</span>
  497. <Input type='password' style={{width: 250}} value={this.state.password}
  498. onChange={(e) => {
  499. this.setState({password: e.target.value})
  500. }}
  501. />
  502. </div>
  503. </div>
  504. :
  505. ''
  506. }
  507. {
  508. disableDeployButton?
  509. '正在部署...'
  510. :
  511. <Button type='primary' onClick={() => {
  512. this.storeEdnAndDeploy(secretID, secretKey, appId, bucketName, dbKind, userID, admin, username, password, db, host, customName, cloudID)
  513. }}>开始部署</Button>
  514. }
  515. </div>
  516. </Modal>
  517. </div>
  518. )
  519. }
  520. }
  521. const Step00 = (props) => (
  522. <div className='step-block'>
  523. <Button type="primary" size='large' style={{borderRadius: 10}}>一键部署</Button>
  524. <div style={{fontSize: 16}}>
  525. 默认部署至腾讯云. 亚马逊,阿里云等请联系我们
  526. </div>
  527. </div>
  528. );
  529. const Step01 = (props) => (
  530. <div className='step-block'>
  531. 扫码查看结果
  532. </div>
  533. );
  534. const Step10 = (props) => (
  535. <div className='step-block'>
  536. 第一步:注册腾讯云账户
  537. <Button style={{marginLeft: 20}}>使用帮助</Button>
  538. </div>
  539. );
  540. class Step11 extends Component {
  541. constructor(props) {
  542. super(props);
  543. this.state = {
  544. check: 0
  545. }
  546. }
  547. render() {
  548. let {userID, getCloudDetail} = this.props;
  549. let {check} = this.state;
  550. return (
  551. <div className='step-block'>
  552. 第二步:填写腾讯云秘钥,一键部署
  553. <Button style={{marginLeft: 20}}>使用帮助</Button>
  554. <div>
  555. <CloudQueryAndConfig
  556. userID={userID}
  557. getCloudDetail={getCloudDetail}
  558. cloudName='tencent'
  559. check={check}
  560. reCheck={() => {
  561. this.setState({check: check + 1})
  562. }}
  563. />
  564. </div>
  565. </div>
  566. )
  567. }
  568. }
  569. const Step12 = (props) => (
  570. <div className='step-block'>
  571. 第三步:注册微信公众号/小程序
  572. <Button style={{marginLeft: 20}}>使用帮助</Button>
  573. </div>
  574. );
  575. const Step13 = (props) => (
  576. <div className='step-block'>
  577. 第四步:微信公众号/小程序 后台填写配置
  578. <Button style={{marginLeft: 20}}>使用帮助</Button>
  579. <div>
  580. <Button type='primary'>填写配置</Button>
  581. </div>
  582. </div>
  583. );
  584. const Step14 = (props) => (
  585. <div className='step-block'>
  586. 开始使用
  587. <div>
  588. <NameAndDB
  589. userID={props.userID}
  590. bucketName={props.bucketName}
  591. secretID={props.secretID}
  592. secretKey={props.secretKey}
  593. appId={props.appId}
  594. cloudID={props.cloudID}
  595. />
  596. </div>
  597. </div>
  598. );