|
|
@@ -0,0 +1,998 @@
|
|
|
+import React, {Component} from 'react';
|
|
|
+import {Link} from 'react-router-dom';
|
|
|
+import {deployAll, graphqlUrl, storeFile, deployFC} from "../../../config";
|
|
|
+import {ADD_APIGROUP, ADD_APIGWPATH, ADD_DEPLOY, ADD_PROJECT, SHOW_CLOUD} from "../../../gql";
|
|
|
+import {CloudConfig} from "../../../login/CloudConfig";
|
|
|
+import {Layout, Button, message, Modal, Icon, Steps, Row, Col, Radio, Input} from 'antd';
|
|
|
+import {FormattedMessage} from 'react-intl';
|
|
|
+import {request} from 'graphql-request'
|
|
|
+import {idGen, convert_} from "../../../func";
|
|
|
+import axios from 'axios';
|
|
|
+import './index.css';
|
|
|
+import User from "../../user/User";
|
|
|
+
|
|
|
+const {Content} = Layout;
|
|
|
+const Step = Steps.Step;
|
|
|
+const RadioGroup = Radio.Group;
|
|
|
+axios.defaults.withCredentials = true;
|
|
|
+
|
|
|
+class DeployConfig extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state = {
|
|
|
+ userID: props.userID,
|
|
|
+ cloudName: 'tencent',
|
|
|
+ disableDeployButton: false,
|
|
|
+ deployFailed: false,
|
|
|
+ cloudID: '',
|
|
|
+ secretID: '',
|
|
|
+ secretKey: '',
|
|
|
+ appId: '',
|
|
|
+ bucketName: ''
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log(this.state, 'userCustom state')
|
|
|
+ }
|
|
|
+
|
|
|
+ getCloudDetail = (cloudID, secretID, secretKey, appId) => {
|
|
|
+ this.setState({
|
|
|
+ cloudID,
|
|
|
+ secretID,
|
|
|
+ secretKey,
|
|
|
+ appId
|
|
|
+ })
|
|
|
+ };
|
|
|
+
|
|
|
+ componentWillMount() {
|
|
|
+ this._isMounted = true;
|
|
|
+ let {bucketName} = this.props
|
|
|
+ bucketName.then(res => {
|
|
|
+ console.log(res, 'bucketname')
|
|
|
+ this.setState({
|
|
|
+ bucketName: res
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ this._isMounted = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillReceiveProps(next) {
|
|
|
+ if(next.bucketName !== this.props.bucketName) {
|
|
|
+ let {bucketName} = this.props
|
|
|
+ bucketName.then(res => {
|
|
|
+ console.log(res, 'bucketname')
|
|
|
+ this.setState({
|
|
|
+ bucketName: res
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {userID, cloudID, deployFailed, appId, secretID, secretKey, bucketName} = this.state;
|
|
|
+ let {history} = this.props
|
|
|
+ return (
|
|
|
+ <Content className="content">
|
|
|
+ <div>
|
|
|
+ <div className="column-menu" onClick={() => {
|
|
|
+ this.props.backToMe()
|
|
|
+ }}>
|
|
|
+ <Icon type="left" style={{color: '#3187FA'}}/>
|
|
|
+ <FormattedMessage id="back to case show"/>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Row>
|
|
|
+ <Col span={9} offset={8}>
|
|
|
+ <div className='step-kind'>
|
|
|
+ 发布上线
|
|
|
+ </div>
|
|
|
+ <Steps direction="vertical" current={6}>
|
|
|
+ <Step title=<Step1/> />
|
|
|
+ <Step title=<Step2 getCloudDetail={this.getCloudDetail} userID={userID}/> />
|
|
|
+ <Step title=
|
|
|
+ <Step3
|
|
|
+ getPrimaryConfigDetail={this.getPrimaryConfigDetail}
|
|
|
+ userID={userID}
|
|
|
+ bucketName={bucketName}
|
|
|
+ secretID={secretID}
|
|
|
+ secretKey={secretKey}
|
|
|
+ appId={appId}
|
|
|
+ cloudID={cloudID}
|
|
|
+ history={history}
|
|
|
+ />
|
|
|
+ />
|
|
|
+ <Step title=<Step4/> />
|
|
|
+ <Step title=<Step5 bucketName={bucketName} userID={userID}/> />
|
|
|
+ <Step title=<Step6 bucketName={bucketName} userID={userID}/> />
|
|
|
+
|
|
|
+ </Steps>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </div>
|
|
|
+ </Content>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default DeployConfig;
|
|
|
+
|
|
|
+const Step1 = (props) => (
|
|
|
+ <div className='step-block'>
|
|
|
+ 第一步:注册腾讯云账户
|
|
|
+ <Button style={{marginLeft: 20}}>使用帮助</Button>
|
|
|
+ </div>
|
|
|
+);
|
|
|
+
|
|
|
+class Step2 extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state = {
|
|
|
+ check: 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {userID, getCloudDetail} = this.props;
|
|
|
+ let {check} = this.state;
|
|
|
+ // recheck 的作用在于当取消或者保存后重新检测 cloud, 然后通过getCloudDetail传值到父组件
|
|
|
+ return (
|
|
|
+ <div className='step-block'>
|
|
|
+ 第二步:填写腾讯云秘钥,一键部署
|
|
|
+ <Button style={{marginLeft: 20}}>使用帮助</Button>
|
|
|
+ <div>
|
|
|
+ <CloudQueryAndConfig
|
|
|
+ userID={userID}
|
|
|
+ getCloudDetail={getCloudDetail}
|
|
|
+ cloudName='tencent'
|
|
|
+ check={check}
|
|
|
+ reCheck={() => {
|
|
|
+ this.setState({check: check + 1})
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class CloudQueryAndConfig extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state = {
|
|
|
+ cloudName: props.cloudName,
|
|
|
+ userID: props.userID,
|
|
|
+ cloudID: '',
|
|
|
+ secretID: '',
|
|
|
+ secretKey: '',
|
|
|
+ appId: '',
|
|
|
+ visible: false,
|
|
|
+ confirmLoading: false,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ searchCloud = () => {
|
|
|
+ this._isMounted = true;
|
|
|
+ let {userID, cloudName} = this.state;
|
|
|
+ // 如果登录,查询该用户是否设置 cloud
|
|
|
+ request(graphqlUrl, SHOW_CLOUD, {user_id: userID}).then(data => {
|
|
|
+ let clouds = data.cloud_by_props.filter(cloud => cloud.cloudName === cloudName);
|
|
|
+ // 如果限制一个云服务商一个 cloud,那么就是clouds[0]
|
|
|
+ if (clouds.length === 1) {
|
|
|
+ let cloud = clouds[0];
|
|
|
+ let {id, secretId, secretKey, appId} = cloud;
|
|
|
+ if (this._isMounted) {
|
|
|
+ this.setState({
|
|
|
+ cloudID: id,
|
|
|
+ secretID: secretId,
|
|
|
+ secretKey,
|
|
|
+ appId,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.props.getCloudDetail(id, secretId, secretKey, appId);
|
|
|
+ } else if (clouds.length > 1) {
|
|
|
+ console.log('数据库有多个同一云服务商的 key');
|
|
|
+ } else {
|
|
|
+ if (this._isMounted) {
|
|
|
+ console.log('数据库没有云服务商的 key');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ };
|
|
|
+
|
|
|
+ showModal = () => {
|
|
|
+ if (this.state.userID) {
|
|
|
+ this.setState({
|
|
|
+ visible: true,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ message.warning('请先登录');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ handleCancel = () => {
|
|
|
+ this.setState({
|
|
|
+ visible: false,
|
|
|
+ });
|
|
|
+ this.props.reCheck();
|
|
|
+ };
|
|
|
+
|
|
|
+ componentWillMount() {
|
|
|
+ this.searchCloud();
|
|
|
+ this._isMounted = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ this._isMounted = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillReceiveProps(next) {
|
|
|
+ console.log(111)
|
|
|
+ if (this.props.check !== next.check) {
|
|
|
+ this.setState({
|
|
|
+ cloudName: next.cloudName,
|
|
|
+ userID: next.userID,
|
|
|
+ }, this.searchCloud);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {visible, confirmLoading, cloudName} = this.state;
|
|
|
+ // cloudconfig 组件引用外层组件公用,修改时应注意
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <Button type='primary' onClick={this.showModal}>填写秘钥</Button>
|
|
|
+
|
|
|
+ <Modal
|
|
|
+ title="云服务商秘钥设置"
|
|
|
+ visible={visible}
|
|
|
+ confirmLoading={confirmLoading}
|
|
|
+ footer={null}
|
|
|
+ onCancel={this.handleCancel}
|
|
|
+ >
|
|
|
+ <CloudConfig cloudName={cloudName} handleCancel={this.handleCancel}/>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const Step3 = (props) => (
|
|
|
+ <div className='step-block'>
|
|
|
+ 开始部署
|
|
|
+ <div>
|
|
|
+ <NameAndDB
|
|
|
+ userID={props.userID}
|
|
|
+ bucketName={props.bucketName}
|
|
|
+ secretID={props.secretID}
|
|
|
+ secretKey={props.secretKey}
|
|
|
+ appId={props.appId}
|
|
|
+ cloudID={props.cloudID}
|
|
|
+ history={props.history}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+);
|
|
|
+
|
|
|
+class NameAndDB extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state = {
|
|
|
+ userID: props.userID,
|
|
|
+ visible: false,
|
|
|
+ confirmLoading: false,
|
|
|
+ customName: props.bucketName,
|
|
|
+ dbKind: 'fc-db',
|
|
|
+ host: '',
|
|
|
+ db: '',
|
|
|
+ username: '',
|
|
|
+ password: '',
|
|
|
+ disableDeployButton: false,
|
|
|
+ deployFailed: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillReceiveProps(next) {
|
|
|
+ if(next.bucketName !== this.props.bucketName) {
|
|
|
+ this.setState({
|
|
|
+ customName: next.bucketName
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ showModal = () => {
|
|
|
+ if (this.state.userID) {
|
|
|
+ this.setState({
|
|
|
+ visible: true,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ message.warning('请先登录');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ handleCancel = () => {
|
|
|
+ this.setState({
|
|
|
+ visible: false,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ componentWillMount() {
|
|
|
+ this._isMounted = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ this._isMounted = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 首先存储 .edn 文件到 ioobot 的 cos,然后调用下面的 deploy 函数
|
|
|
+ storeEdnAndDeploy = (secretID, secretKey, appId, bucketName, dbKind, userID, admin, username, password, db, host, customName, cloudID) => {
|
|
|
+ // store *.edn to cos
|
|
|
+ secretID = secretID ? secretID : 'AKIDkYBvY0LOJ2bzCDmnMjz4xgFertmVJlVE'
|
|
|
+ secretKey = secretKey ? secretKey : 'zwjKk29TdcYP8g2FG5kCSWmz3wcH92lN'
|
|
|
+ cloudID = cloudID ? cloudID : 'tencent_CloudID'
|
|
|
+ appId = appId ? appId : '1254337200'
|
|
|
+ // mongodb 和 fc-db 区分配置
|
|
|
+ let deployConf = dbKind === 'mongodb' ?
|
|
|
+ ` {:secretId "${secretID}"\n` +
|
|
|
+ ` :secretKey "${secretKey}"\n` +
|
|
|
+ ` :appId "${appId}"\n` +
|
|
|
+ ' :region "ap-beijing" \n' +
|
|
|
+ ' }\n'
|
|
|
+ :
|
|
|
+ ` {:secretId "${secretID}"\n` +
|
|
|
+ ` :secretKey "${secretKey}"\n` +
|
|
|
+ ` :appId "${appId}"\n` +
|
|
|
+ ' :region "ap-beijing"\n' +
|
|
|
+ ' :bucket "fc-db"\n' +
|
|
|
+ ' :trustStore "/etc/ssl/certs/java/cacerts"\n' +
|
|
|
+ ` :fc-db-store "save/${bucketName}.dat"\n` +
|
|
|
+ ' :fc-db-dir "fc-db"\n' +
|
|
|
+ ' :local-tmp-dir "/tmp"\n' +
|
|
|
+ ' :local-db-file "fcdb.dat"\n' +
|
|
|
+ ' :update-tx? true \n' +
|
|
|
+ ' :force-down? true\n' +
|
|
|
+ ' }';
|
|
|
+
|
|
|
+ let a = axios.post(storeFile, {
|
|
|
+ 'file-name': `${bucketName}/${dbKind}/${userID}/deploy-conf.edn`,
|
|
|
+ bucket: 'case',
|
|
|
+ cont: deployConf
|
|
|
+ });
|
|
|
+
|
|
|
+ let cont = '{:uri {\n' +
|
|
|
+ `:auth {:admin-db "${admin}"\n` +
|
|
|
+ ` :u "${username}"\n` +
|
|
|
+ ` :p "${password}"\n` +
|
|
|
+ ` :host "${host}"}}\n` +
|
|
|
+ ` :db-name "${db}"}`;
|
|
|
+
|
|
|
+ // fc-db 不用存此文件,故直接返回 status: 200
|
|
|
+ let b = dbKind === 'mongodb' ?
|
|
|
+ axios.post(storeFile, {
|
|
|
+ 'file-name': `${bucketName}/${dbKind}/${userID}/mongo-config.edn`,
|
|
|
+ bucket: 'case',
|
|
|
+ cont
|
|
|
+ })
|
|
|
+ :
|
|
|
+ Promise.resolve({status: 200});
|
|
|
+
|
|
|
+ Promise.all([a, b]).then(value => {
|
|
|
+ if (value.every(res => res.status === 200)) {
|
|
|
+ console.log('store file success , start deploying');
|
|
|
+ this.deploy(userID, dbKind, bucketName, customName, cloudID);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ deploy = (userID, dbKind, bucketName, customName, cloudID) => {
|
|
|
+ this._isMounted = true;
|
|
|
+ let _this = this;
|
|
|
+
|
|
|
+ if (bucketName === '') {
|
|
|
+ console.log('state, 没有传值');
|
|
|
+ } else {
|
|
|
+ console.log('开始调用');
|
|
|
+ this.setState({
|
|
|
+ disableDeployButton: true
|
|
|
+ });
|
|
|
+
|
|
|
+ let now = new Date().getTime(),
|
|
|
+ functionName = convert_(userID + '_' + customName),
|
|
|
+ serviceName = functionName,
|
|
|
+ resources = [`${bucketName}/schema.edn`, `${bucketName}/resolve-map.edn`, `${bucketName}/${dbKind}/${userID}/deploy-conf.edn`, `${bucketName}/html/index.html`, `${bucketName}/wx-config.edn`];
|
|
|
+
|
|
|
+ if (dbKind === 'mongodb') {
|
|
|
+ resources.push(`${bucketName}/${dbKind}/${userID}/mongo-config.edn`)
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('now', now, 'functionName', functionName, 'serviceName', serviceName, 'resources', resources);
|
|
|
+ axios.post(deployAll,
|
|
|
+ {
|
|
|
+ 'fc-name': functionName,
|
|
|
+ 'bucket': dbKind === 'mongodb' ? 'native-fc' : 'fcdb-deploy',
|
|
|
+ 'object-file': 'fc-only.zip',
|
|
|
+ 'res-bucket': 'case',
|
|
|
+ 'resources': resources,
|
|
|
+ 'service-name': serviceName,
|
|
|
+ 'path': "/*"
|
|
|
+ })
|
|
|
+ .then(function (response) {
|
|
|
+ // 以下操作为写入数据库
|
|
|
+ console.log('response', response);
|
|
|
+ if (response['data']['apigw-result'] && response['data']['fc-result']) {
|
|
|
+ // 处理数据
|
|
|
+ let result = response['data']['apigw-result'];
|
|
|
+ let apiData = result['api-info'];
|
|
|
+ let serviceData = result['service-info'];
|
|
|
+
|
|
|
+ // 存数据
|
|
|
+ let pathID = idGen('path'),
|
|
|
+ groupID = idGen('group'),
|
|
|
+ deployID = idGen('deploy'),
|
|
|
+ projectID = idGen('project');
|
|
|
+
|
|
|
+ let {apiId, path, method} = apiData;
|
|
|
+ let {serviceName, serviceId, subDomain} = serviceData;
|
|
|
+
|
|
|
+ let pathVarObj = {
|
|
|
+ id: idGen('path'),
|
|
|
+ user_id: userID,
|
|
|
+ apiGWGroup_id: pathID,
|
|
|
+ deploy_id: deployID,
|
|
|
+ apiGWName: functionName,
|
|
|
+ requestMethod: method,
|
|
|
+ apiGWPath: path,
|
|
|
+ apiId: apiId,
|
|
|
+ apiGWDesc: '',
|
|
|
+ serviceType: 'SCF',
|
|
|
+ timeout: 300,
|
|
|
+ createdAt: now,
|
|
|
+ updatedAt: ''
|
|
|
+ };
|
|
|
+
|
|
|
+ let projectVarObj = {
|
|
|
+ id: projectID,
|
|
|
+ projectType: 'case',
|
|
|
+ cloud_id: cloudID,
|
|
|
+ user_id: userID,
|
|
|
+ projectName: functionName,
|
|
|
+ database_id: '',
|
|
|
+ apiGWGroup_id: '',
|
|
|
+ deploy_id: '',
|
|
|
+ case_id: '',
|
|
|
+ wxConfig_id: '',
|
|
|
+ schema_id: '',
|
|
|
+ createdAt: now,
|
|
|
+ updatedAt: '',
|
|
|
+ projectStatus: 'deployed'
|
|
|
+ };
|
|
|
+
|
|
|
+ let groupVarObj = {
|
|
|
+ id: groupID,
|
|
|
+ cloud_id: cloudID,
|
|
|
+ user_id: userID,
|
|
|
+ userStatus: '',
|
|
|
+ userDomain: '',
|
|
|
+ groupName: serviceName,
|
|
|
+ frontType: '',
|
|
|
+ region: '',
|
|
|
+ environmentName: '',
|
|
|
+ defaultDomain: subDomain,
|
|
|
+ status: '',
|
|
|
+ serviceId: serviceId,
|
|
|
+ createdAt: now,
|
|
|
+ updatedAt: ''
|
|
|
+ };
|
|
|
+
|
|
|
+ let deployVarObj = {
|
|
|
+ id: deployID,
|
|
|
+ cloud_id: cloudID,
|
|
|
+ functionName,
|
|
|
+ cosObjectName: '',
|
|
|
+ region: '',
|
|
|
+ cosBucketRegion: '',
|
|
|
+ description: '',
|
|
|
+ cosBucketName: '',
|
|
|
+ vpcId: '',
|
|
|
+ subnetId: '',
|
|
|
+ memorySize: 512,
|
|
|
+ timeout: 300,
|
|
|
+ handler: '',
|
|
|
+ serviceName: "",
|
|
|
+ fc_id: '',
|
|
|
+ createdAt: now,
|
|
|
+ updatedAt: ''
|
|
|
+ };
|
|
|
+
|
|
|
+ let add_apigwpath = request(graphqlUrl, ADD_APIGWPATH, pathVarObj),
|
|
|
+ add_project = request(graphqlUrl, ADD_PROJECT, projectVarObj),
|
|
|
+ add_apigroup = request(graphqlUrl, ADD_APIGROUP, groupVarObj),
|
|
|
+ add_deploy = request(graphqlUrl, ADD_DEPLOY, deployVarObj);
|
|
|
+
|
|
|
+ Promise.all([add_apigwpath, add_project, add_apigroup, add_deploy])
|
|
|
+ .then(value => {
|
|
|
+ console.log(value);
|
|
|
+
|
|
|
+ // 展示数据
|
|
|
+ if (_this._isMounted) {
|
|
|
+ _this.setState({
|
|
|
+ disableDeployButton: false
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ _this.props.history.push({
|
|
|
+ pathname: `/common/deploy`,
|
|
|
+ state: {
|
|
|
+ // 处理传回数据,直接拼接
|
|
|
+ url: `http://${subDomain}/test/`
|
|
|
+ }
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ console.log(err);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.log('deployAll 失败');
|
|
|
+ _this.setState({
|
|
|
+ deployFailed: true,
|
|
|
+ disableDeployButton: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ })
|
|
|
+ .catch(function (error) {
|
|
|
+ console.log('axios error', error);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {
|
|
|
+ visible,
|
|
|
+ confirmLoading,
|
|
|
+ userID,
|
|
|
+ dbKind,
|
|
|
+ host,
|
|
|
+ db,
|
|
|
+ username,
|
|
|
+ password,
|
|
|
+ customName,
|
|
|
+ disableDeployButton,
|
|
|
+ deployFailed
|
|
|
+ } = this.state;
|
|
|
+ let {secretID, secretKey, appId, bucketName, cloudID} = this.props
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <Button type='primary' onClick={this.showModal}>开始部署</Button>
|
|
|
+
|
|
|
+ <Modal
|
|
|
+ title="名称和数据库配置"
|
|
|
+ visible={visible}
|
|
|
+ confirmLoading={confirmLoading}
|
|
|
+ footer={null}
|
|
|
+ onCancel={this.handleCancel}
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ <div>
|
|
|
+ <div className={'schema-name'}><FormattedMessage id='Name'/></div>
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'><FormattedMessage id='name'/>:</span>
|
|
|
+ <Input style={{width: 250}} value={this.state.customName}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({customName: e.target.value})
|
|
|
+ }}/>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style={{marginTop: 20}}>
|
|
|
+ <div className={'schema-name'}><FormattedMessage id='DB Choose'/></div>
|
|
|
+ <RadioGroup onChange={(e) => {
|
|
|
+ this.setState({dbKind: e.target.value})
|
|
|
+ }} value={this.state.dbKind}>
|
|
|
+ <Radio value='fc-db'>fc-db</Radio>
|
|
|
+ <Radio value='mongodb'>mongodb</Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {
|
|
|
+ this.state.dbKind === 'mongodb' ?
|
|
|
+ <div>
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'>地址</span>
|
|
|
+ <Input style={{width: 250}} value={this.state.host}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({host: e.target.value})
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'>数据库名称</span>
|
|
|
+ <Input style={{width: 250}} value={this.state.db}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({db: e.target.value})
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'>用户名</span>
|
|
|
+ <Input style={{width: 250}} value={this.state.username}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({username: e.target.value})
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'>密码</span>
|
|
|
+ <Input type='password' style={{width: 250}} value={this.state.password}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({password: e.target.value})
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ :
|
|
|
+ ''
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ disableDeployButton ?
|
|
|
+ '正在部署...'
|
|
|
+ :
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ this.setState({
|
|
|
+ deployFailed: false
|
|
|
+ })
|
|
|
+ this.storeEdnAndDeploy(secretID, secretKey, appId, bucketName, dbKind, userID, db, username, password, db, host, customName, cloudID)
|
|
|
+ }}>开始部署</Button>
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ deployFailed?
|
|
|
+ '部署失败,请稍后重试':''
|
|
|
+ }
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const Step4 = (props) => (
|
|
|
+ <div className='step-block'>
|
|
|
+ 第三步:注册微信公众号/小程序
|
|
|
+ <Button style={{marginLeft: 20}}>使用帮助</Button>
|
|
|
+ </div>
|
|
|
+);
|
|
|
+
|
|
|
+class Step5 extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props)
|
|
|
+ this.state = {
|
|
|
+ visible: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleCancel = () => {
|
|
|
+ this.setState({
|
|
|
+ visible: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {visible} = this.state
|
|
|
+ let {userID, bucketName} = this.props
|
|
|
+ return (
|
|
|
+ <div className='step-block'>
|
|
|
+ 第四步:微信公众号/小程序 后台填写配置
|
|
|
+ <Button style={{marginLeft: 20}}>使用帮助</Button>
|
|
|
+ <div>
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ this.setState({visible: true})
|
|
|
+ }}>填写配置</Button>
|
|
|
+ <Modal
|
|
|
+ title="公众号/小程序配置"
|
|
|
+ visible={visible}
|
|
|
+ footer={null}
|
|
|
+ onCancel={this.handleCancel}
|
|
|
+ >
|
|
|
+ <Wechat
|
|
|
+ userID={userID}
|
|
|
+ bucketName={bucketName}
|
|
|
+ />
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 该组件作用在于 store wx.edn to cos
|
|
|
+class Wechat extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props)
|
|
|
+ this.state = {
|
|
|
+ appID: '',
|
|
|
+ appSecret: '',
|
|
|
+ token: '',
|
|
|
+ url: '',
|
|
|
+ key: '',
|
|
|
+ appid: '',
|
|
|
+ mch: '',
|
|
|
+ nonce: '',
|
|
|
+ body: '',
|
|
|
+ spbill: '',
|
|
|
+ notify: '',
|
|
|
+ attach: '',
|
|
|
+ dbKind: 'fc-db',
|
|
|
+ saving: false,
|
|
|
+ savingCompleted: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inputChange = (kind) => {
|
|
|
+ return (e) => {
|
|
|
+ this.setState({
|
|
|
+ [kind]: e.target.value
|
|
|
+ })
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ storeEdn = () => {
|
|
|
+ // store *.edn to cos
|
|
|
+ let {appID, appSecret, token, url, key, appid, mch, nonce, body, spbill, notify, attach, dbKind} = this.state
|
|
|
+ let {userID, bucketName} = this.props
|
|
|
+ let deployConf = `{
|
|
|
+ :wx-server {
|
|
|
+ :appID "${appID}"
|
|
|
+ :appsecret "${appSecret}"
|
|
|
+ :enter-url "${url}"
|
|
|
+ :token "${token}"
|
|
|
+ }
|
|
|
+ :wx-pay {
|
|
|
+ :PAY-API-KEY "${key}"
|
|
|
+ :appid "${appid}"
|
|
|
+ :attach "${attach}"
|
|
|
+ :mch_id "${mch}"
|
|
|
+ :nonce_str "${nonce}"
|
|
|
+ :body "${body}"
|
|
|
+ :spbill_create_ip "${spbill}"
|
|
|
+ :notify_url "${notify}"
|
|
|
+ }
|
|
|
+ }`
|
|
|
+
|
|
|
+ axios.post(storeFile, {
|
|
|
+ 'file-name': `${bucketName}/${dbKind}/${userID}/wx-config.edn`,
|
|
|
+ bucket: 'case',
|
|
|
+ cont: deployConf
|
|
|
+ }).then(value => {
|
|
|
+ if (value.status === 200) {
|
|
|
+ console.log('store wx file success');
|
|
|
+ this.setState({
|
|
|
+ saving: false,
|
|
|
+ savingCompleted: true
|
|
|
+ })
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {appID, appSecret, token, url, key, appid, mch, nonce, body, spbill, notify, attach, dbKind, saving, savingCompleted} = this.state
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <div style={{marginTop: 15}}>
|
|
|
+ <div style={{marginBottom: 15}}>
|
|
|
+ <div>
|
|
|
+ <span className='cloud-name'><FormattedMessage id='DB Choose'/></span>
|
|
|
+ <RadioGroup onChange={(e) => {
|
|
|
+ this.setState({dbKind: e.target.value})
|
|
|
+ }} value={dbKind}>
|
|
|
+ <Radio value='fc-db'>fc-db</Radio>
|
|
|
+ <Radio value='mongodb'>mongodb</Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ <span className='cloud-name'>微信开发者配置</span>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>appID:</span>
|
|
|
+ <Input style={{width: 250}} value={appID} onChange={this.inputChange('appID')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>appSecret:</span>
|
|
|
+ <Input type='password' style={{width: 250}} value={appSecret}
|
|
|
+ onChange={this.inputChange('appSecret')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>token:</span>
|
|
|
+ <Input type='password' style={{width: 250}} value={token}
|
|
|
+ onChange={this.inputChange('token')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>回调域名:</span>
|
|
|
+ <Input style={{width: 250}} value={url} onChange={this.inputChange('url')}/>
|
|
|
+ </div>
|
|
|
+ <span className='cloud-name'>微信支付配置</span>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>key:</span>
|
|
|
+ <Input style={{width: 250}} value={key} onChange={this.inputChange('key')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>appid:</span>
|
|
|
+ <Input style={{width: 250}} value={appid} onChange={this.inputChange('appid')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>attach:</span>
|
|
|
+ <Input style={{width: 250}} value={attach} onChange={this.inputChange('attach')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>商户 id:</span>
|
|
|
+ <Input style={{width: 250}} value={mch} onChange={this.inputChange('mch')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>nonce:</span>
|
|
|
+ <Input style={{width: 250}} value={nonce} onChange={this.inputChange('nonce')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>body:</span>
|
|
|
+ <Input style={{width: 250}} value={body} onChange={this.inputChange('body')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>机器IP:</span>
|
|
|
+ <Input style={{width: 250}} value={spbill} onChange={this.inputChange('spbill')}/>
|
|
|
+ </div>
|
|
|
+ <div style={{marginBottom: 20}}>
|
|
|
+ <span className='item-title-cloud'>通知域名:</span>
|
|
|
+ <Input style={{width: 250}} value={notify} onChange={this.inputChange('notify')}/>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ {
|
|
|
+ saving ?
|
|
|
+ '保存中,请稍后。。。' :
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ this.storeEdn()
|
|
|
+ this.setState({
|
|
|
+ saving: true
|
|
|
+ })
|
|
|
+ }}>
|
|
|
+ <FormattedMessage id="save"/>
|
|
|
+ </Button>
|
|
|
+ }
|
|
|
+ {
|
|
|
+ savingCompleted ?
|
|
|
+ <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/> : ''
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class Step6 extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props)
|
|
|
+ this.state = {
|
|
|
+ visible: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleCancel = () => {
|
|
|
+ this.setState({
|
|
|
+ visible: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {visible} = this.state
|
|
|
+ let {userID, bucketName} = this.props
|
|
|
+ return (
|
|
|
+ <div className='step-block'>
|
|
|
+ 开始使用
|
|
|
+ <div>
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ this.setState({visible: true})
|
|
|
+ }}>使用</Button>
|
|
|
+ <Modal
|
|
|
+ title="公众号/小程序二次部署"
|
|
|
+ visible={visible}
|
|
|
+ footer={null}
|
|
|
+ onCancel={this.handleCancel}
|
|
|
+ >
|
|
|
+ <WechatDeploy
|
|
|
+ userID={userID}
|
|
|
+ bucketName={bucketName}
|
|
|
+ />
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 该组件作用在于 重新部署 fc
|
|
|
+// 因为需要同名覆盖,所以要求输入同名的函数名
|
|
|
+// 目前不直接在 step before存入共同父组件再传递回来,是因为
|
|
|
+// 考虑到用户刷新,数据将消失
|
|
|
+// 如果需要上述操作,可以考虑限制用户每种案例部署一个,通过查询数据库来实现自动填写
|
|
|
+// 目前的限制一个,在于限制了用户实际部署的,但在前端没有限制个数
|
|
|
+class WechatDeploy extends Component {
|
|
|
+ constructor(props) {
|
|
|
+ super(props)
|
|
|
+ this.state = {
|
|
|
+ dbKind: 'fc-db',
|
|
|
+ customName: props.bucketName,
|
|
|
+ deployButton: true,
|
|
|
+ deploySuccess: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ let {customName, dbKind, deployButton, deploySuccess} = this.state
|
|
|
+ let {bucketName, userID} = this.props;
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <div>
|
|
|
+ <div className={'schema-name'}><FormattedMessage id='Name'/></div>
|
|
|
+ <div>
|
|
|
+ <span className='item-title-cloud'><FormattedMessage id='name'/>:</span>
|
|
|
+ <Input style={{width: 250}} value={customName}
|
|
|
+ onChange={(e) => {
|
|
|
+ this.setState({customName: e.target.value})
|
|
|
+ }}/>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style={{marginTop: 20}}>
|
|
|
+ <div className={'schema-name'}><FormattedMessage id='DB Choose'/></div>
|
|
|
+ <RadioGroup onChange={(e) => {
|
|
|
+ this.setState({dbKind: e.target.value})
|
|
|
+ }} value={dbKind}>
|
|
|
+ <Radio value='fc-db'>fc-db</Radio>
|
|
|
+ <Radio value='mongodb'>mongodb</Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {
|
|
|
+ deployButton?
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ this.setState({
|
|
|
+ deployButton: false
|
|
|
+ })
|
|
|
+ let functionName = convert_(customName),
|
|
|
+ resources = [`${bucketName}/schema.edn`, `${bucketName}/resolve-map.edn`, `${bucketName}/${dbKind}/${userID}/deploy-conf.edn`, `${bucketName}/html/index.html`, `${bucketName}/wx-config.edn`];
|
|
|
+
|
|
|
+ if (dbKind === 'mongodb') {
|
|
|
+ resources.push(`${bucketName}/${dbKind}/${userID}/mongo-config.edn`)
|
|
|
+ }
|
|
|
+ axios.post(deployFC, {
|
|
|
+ 'fc-name': userID + '_' + functionName,
|
|
|
+ 'bucket': dbKind === 'mongodb' ? 'native-fc' : 'fcdb-deploy',
|
|
|
+ 'object-file': 'fc-only.zip',
|
|
|
+ 'res-bucket': 'case',
|
|
|
+ 'resources': resources
|
|
|
+ }).then(res => {
|
|
|
+ console.log(res)
|
|
|
+ this.setState({
|
|
|
+ deployButton: true,
|
|
|
+ deploySuccess: true
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }}>使用</Button>
|
|
|
+ :
|
|
|
+ '正在部署中...'
|
|
|
+ }
|
|
|
+ {
|
|
|
+ deploySuccess?
|
|
|
+ '部署成功' : ''
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|