Browse Source

Make MX and SRV records editable

Thomas Dy 8 years ago
parent
commit
5aebcd5b54
3 changed files with 208 additions and 21 deletions
  1. 178 20
      lib/ui/RecordList.jsx
  2. 24 1
      lib/util.js
  3. 6 0
      main.css

+ 178 - 20
lib/ui/RecordList.jsx

@@ -1,6 +1,163 @@
 var DomainStore = require('../stores').Domains;
 var React = require('react');
+var deepMerge = require('../util').deepMerge;
 
+var TextName = React.createClass({
+  getName: function() {
+    return {
+      name: this.refs.name.getDOMNode().value.trim()
+    }
+  },
+  clear: function() {
+    this.refs.name.getDOMNode().value = "";
+  },
+  render: function() {
+    return (
+      <td><input type="text" ref="name" defaultValue={this.props.record.name} /></td>
+    )
+  }
+});
+var SRVName = React.createClass({
+  getName: function() {
+    return {
+      data: {
+        service: this.refs.service.getDOMNode().value.trim(),
+        proto: this.refs.proto.getDOMNode().value
+      }
+    }
+  },
+  clear: function() {
+    this.refs.service.getDOMNode().value = "";
+    this.refs.proto.getDOMNode().value = "_udp";
+  },
+  render: function() {
+    var record = this.props.record;
+    var data = record && record.data || {};
+    var protos = ['_udp', '_tcp', '_tls'];
+    var options = protos.map(function(proto) {
+      return <option key={proto} value={proto}>{proto}</option>
+    });
+    return (
+      <td className="multi-input">
+        <div>
+          <label>Service</label>
+          <input type="text" ref="service" defaultValue={data.service} />
+        </div>
+        <div>
+          <label>Proto</label>
+          <select ref="proto" defaultValue={data.proto}>{options}</select>
+        </div>
+      </td>
+    )
+  }
+});
+var TextValue = React.createClass({
+  getValue: function() {
+    return {
+      content: this.refs.value.getDOMNode().value.trim()
+    }
+  },
+  clear: function() {
+    this.refs.value.getDOMNode().value = "";
+  },
+  render: function() {
+    return (
+      <td><input type="text" ref="value" defaultValue={this.props.record.content} /></td>
+    )
+  }
+});
+var MXValue = React.createClass({
+  getValue: function() {
+    return {
+      content: this.refs.value.getDOMNode().value.trim(),
+      priority: this.refs.priority.getDOMNode().value.trim()
+    }
+  },
+  clear: function() {
+    this.refs.value.getDOMNode().value = "";
+    this.refs.priority.getDOMNode().value = "";
+  },
+  render: function() {
+    return (
+      <td className="multi-input">
+        <div>
+          <label>Domain</label>
+          <input type="text" ref="value" defaultValue={this.props.record.content} />
+        </div>
+        <div>
+          <label>Priority</label>
+          <input type="number" ref="priority" minValue="0" maxValue="65535" defaultValue={this.props.record.priority} />
+        </div>
+      </td>
+    )
+  }
+});
+var SRVValue = React.createClass({
+  getValue: function() {
+    return {
+      data: {
+        name: this.refs.name.getDOMNode().value.trim(),
+        priority: this.refs.priority.getDOMNode().value.trim(),
+        weight: this.refs.weight.getDOMNode().value.trim(),
+        port: this.refs.port.getDOMNode().value.trim(),
+        target: this.refs.target.getDOMNode().value.trim()
+      }
+    }
+  },
+  clear: function() {
+    this.refs.name.getDOMNode().value = "";
+    this.refs.priority.getDOMNode().value = "";
+    this.refs.weight.getDOMNode().value = "";
+    this.refs.port.getDOMNode().value = "";
+    this.refs.target.getDOMNode().value = "";
+  },
+  render: function() {
+    var record = this.props.record;
+    var data = record && record.data || {};
+    return (
+      <td className="multi-input">
+        <div>
+          <label>Name</label>
+          <input type="text" ref="name" defaultValue={data.name} />
+        </div>
+        <div>
+          <label>Priority</label>
+          <input type="number" ref="priority" minValue="0" maxValue="65535" defaultValue={data.priority} />
+        </div>
+        <div>
+          <label>Weight</label>
+          <input type="number" ref="weight" minValue="0" maxValue="65535" defaultValue={data.weight} />
+        </div>
+        <div>
+          <label>Port</label>
+          <input type="number" ref="port" minValue="0" maxValue="65535" defaultValue={data.port} />
+        </div>
+        <div>
+          <label>Target</label>
+          <input type="text" ref="target" defaultValue={data.target} />
+        </div>
+      </td>
+    )
+  }
+});
+function getNameComponent(type) {
+  switch(type) {
+    case 'SRV':
+      return SRVName;
+    default:
+      return TextName;
+  }
+}
+function getValueComponent(type) {
+  switch(type) {
+    case 'MX':
+      return MXValue;
+    case 'SRV':
+      return SRVValue;
+    default:
+      return TextValue;
+  }
+}
 var CloudActive = React.createClass({
   render: function() {
     var record = this.props.record;
@@ -20,9 +177,12 @@ var CloudActive = React.createClass({
 });
 var RecordCreate = React.createClass({
   getInitialState: function() {
-    return {saving: false};
+    return {saving: false, type: 'A'};
+  },
+  types: ['A', 'AAAA', 'CNAME', 'LOC', 'MX', 'NS', 'SPF', 'SRV', 'TXT'],
+  changeType: function(event) {
+    this.setState({ type: event.target.value });
   },
-  types: ['A', 'AAAA', 'CNAME', 'LOC', 'NS', 'SPF', 'TXT'],
   finishSave: function(promise) {
     promise.then(function() {
       this.setState({saving: false});
@@ -30,18 +190,15 @@ var RecordCreate = React.createClass({
     }.bind(this));
   },
   reset: function() {
-    this.refs.type.getDOMNode().value = this.types[0];
-    this.refs.name.getDOMNode().value = "";
-    this.refs.value.getDOMNode().value = "";
+    this.refs.name.clear();
+    this.refs.value.clear();
   },
   commitAdd: function() {
     this.setState({saving: true});
-    var newRecord = {
+    var newRecord = deepMerge({
       type: this.refs.type.getDOMNode().value,
-      name: this.refs.name.getDOMNode().value.trim(),
-      content: this.refs.value.getDOMNode().value.trim(),
       ttl: 1 // automatic
-    };
+    }, this.refs.name.getName(), this.refs.value.getValue());
     this.finishSave(DomainStore.recordAdd(this.props.domain, newRecord));
   },
   render: function() {
@@ -49,15 +206,17 @@ var RecordCreate = React.createClass({
     var options = this.types.map(function(type) {
       return <option key={type} value={type}>{type}</option>
     });
+    var Name = getNameComponent(this.state.type);
+    var Value = getValueComponent(this.state.type);
     return (
       <tr className={className}>
         <td>
-          <select ref="type">
+          <select ref="type" onChange={this.changeType}>
             {options}
           </select>
         </td>
-        <td><input type="text" ref="name" /></td>
-        <td><input type="text" ref="value" /></td>
+        <Name ref="name" record={{}} />
+        <Value ref="value" record={{}} />
         <td></td>
         <td>
           <button className="btn btn-success" onClick={this.commitAdd}>Add</button>
@@ -90,13 +249,11 @@ var Record = React.createClass({
   commitEdit: function() {
     this.setState({saving: true});
     var record = this.props.record;
-    var newRecord = {
+    var newRecord = deepMerge({
       id: record.id.val(),
       type: record.type.val(),
-      name: this.refs.name.getDOMNode().value.trim(),
-      content: this.refs.value.getDOMNode().value.trim(),
       ttl: 1 // automatic
-    };
+    }, this.refs.name.getName(), this.refs.value.getValue());
     if(record.proxied.val()) {
       newRecord.proxied = record.proxied.val();
     }
@@ -118,7 +275,6 @@ var Record = React.createClass({
   render: function() {
     var record = this.props.record;
     var className = this.state.saving ? 'saving' : '';
-    var editDisabled = ['MX', 'SRV'].indexOf(record.type.val()) >= 0;
     var displayName = record.name.val();
     var zoneName = '.'+record.zone_name.val();
     var limit = displayName.length - zoneName.length;
@@ -126,11 +282,13 @@ var Record = React.createClass({
       displayName = displayName.substring(0, limit);
     }
     if(this.state.state === 'edit') {
+      var Name = getNameComponent(record.type.val());
+      var Value = getValueComponent(record.type.val());
       return (
         <tr className={className}>
           <td className="record-type"><span className={record.type.val()}>{record.type.val()}</span></td>
-          <td><input type="text" ref="name" defaultValue={displayName} /></td>
-          <td><input type="text" ref="value" defaultValue={record.content.val()} /></td>
+          <Name ref="name" record={record.val()} />
+          <Value ref="value" record={record.val()} />
           <td>
             <a onClick={this.cancelEdit}>Cancel</a>
           </td>
@@ -163,7 +321,7 @@ var Record = React.createClass({
           <td className="value">{record.content.val()}</td>
           <td><CloudActive record={record} onClick={this.toggleProxy} /></td>
           <td className="actions">
-            <button className="btn btn-primary" disabled={editDisabled} onClick={this.setEditing}>Edit</button>
+            <button className="btn btn-primary" onClick={this.setEditing}>Edit</button>
             <span> </span>
             <button className="btn btn-danger" onClick={this.setDeleting}>Delete</button>
           </td>

+ 24 - 1
lib/util.js

@@ -15,6 +15,29 @@ function dateToString(date) {
     return dateString;
 }
 
+function merge(a, b) {
+  for(var i in b) {
+    var n = a[i];
+    var o = b[i];
+    if(typeof n == 'object' && typeof o == 'object') {
+      merge(n, o);
+    }
+    else {
+      a[i] = b[i];
+    }
+  }
+  return a;
+}
+
+function deepMerge() {
+  var ret = {};
+  for(var i = 0; i < arguments.length; ++i) {
+    merge(ret, arguments[i]);
+  }
+  return ret;
+}
+
 module.exports = {
-  dateToString: dateToString
+  dateToString: dateToString,
+  deepMerge: deepMerge
 }

+ 6 - 0
main.css

@@ -50,6 +50,12 @@
 #records .table tr.saving {
 	background: lightyellow;
 }
+#records .table td.multi-input label {
+	display: block;
+}
+#records .table td.multi-input div {
+	margin-bottom: 10px;
+}
 .nav.nav-tabs {
 	margin-bottom: 10px;
 }