فهرست منبع

fix(webapp): improve token management GUI, fixes #460

Peter Thomassen 4 سال پیش
والد
کامیت
6781c09f62
4فایلهای تغییر یافته به همراه33 افزوده شده و 29 حذف شده
  1. 2 1
      webapp/src/components/Field/GenericText.vue
  2. 22 23
      webapp/src/views/CrudList.vue
  3. 4 1
      webapp/src/views/DomainList.vue
  4. 5 4
      webapp/src/views/TokenList.vue

+ 2 - 1
webapp/src/components/Field/GenericText.vue

@@ -1,5 +1,5 @@
 <template>
-  <v-text-field
+  <v-text-field v-if="!readonly"
     :label="label"
     :disabled="disabled || readonly"
     :error-messages="errorMessages"
@@ -11,6 +11,7 @@
     @input="input($event)"
     @keyup="keyup($event)"
   />
+  <span v-else>{{ value }}</span>
 </template>
 
 <script>

+ 22 - 23
webapp/src/views/CrudList.vue

@@ -16,19 +16,6 @@
               Close
             </v-btn>
           </v-snackbar>
-          <v-snackbar
-            v-model="snackbarInfo"
-            :timeout="-1"
-          >
-            <span v-html="snackbarInfoText"></span>
-            <v-btn
-              color="pink"
-              text
-              @click="snackbarInfoText = ''"
-            >
-              Close
-            </v-btn>
-          </v-snackbar>
 
           <!-- The Actual Table -->
           <v-data-table
@@ -107,6 +94,14 @@
                         {{ errors[errors.length - 1] }}
                       </v-alert>
 
+                      <v-alert
+                              :value="createDialogSuccess"
+                              type="success"
+                              style="overflow: auto"
+                      >
+                        <span v-html="texts.createSuccess(createDialogItem)"></span>
+                      </v-alert>
+
                       <v-alert
                               :value="!!texts.createWarning(destroyDialogItem)"
                               type="warning"
@@ -116,7 +111,7 @@
 
                       <v-card-text v-if="createDialog">
                         <!-- v-if required here to make autofocus below working for the 2nd+ times, cf stackoverflow.com/a/51476992 -->
-                        <p>{{ texts.create() }}</p>
+                        <span v-html="texts.create()"></span>
                         <!-- New Form -->
                         <component
                                 :is="c.datatype"
@@ -127,30 +122,31 @@
                                 :label="c.textCreate || c.text"
                                 :error-messages="c.createErrors"
                                 :required="c.required || false"
-                                :disabled="createInhibited"
+                                :disabled="createInhibited || createDialogSuccess"
                                 autofocus
                                 @input="clearErrors(c)"
                         />
                       </v-card-text>
 
-                      <v-card-actions>
+                      <v-card-actions class="pb-4">
                         <v-spacer />
                         <v-btn
                                 color="primary"
                                 class="grow"
-                                outlined
+                                :outlined="!createDialogSuccess"
                                 :disabled="createDialogWorking"
                                 @click.native="close"
                         >
-                          Cancel
+                          {{ createDialogSuccess ? 'Close' : 'Cancel' }}
                         </v-btn>
                         <v-btn
                                 type="submit"
                                 color="primary"
                                 class="grow"
                                 depressed
-                                :disabled="createInhibited || !valid || createDialogWorking"
+                                :disabled="createInhibited || !valid || createDialogWorking || createDialogSuccess"
                                 :loading="createDialogWorking"
+                                v-if="!createDialogSuccess"
                         >
                           Save
                         </v-btn>
@@ -332,6 +328,7 @@ export default {
     createDialogIndex: null,
     createDialogItem: {},
     createDialogError: false,
+    createDialogSuccess: false,
     destroyDialog: false,
     destroyDialogWorking: false,
     destroyDialogItem: {},
@@ -342,7 +339,6 @@ export default {
     extraComponentBind: {},
     fullWidth: false,
     snackbar: false,
-    snackbarInfoText: '',
     search: '',
     rows: [],
     valid: false,
@@ -360,6 +356,7 @@ export default {
     texts: {
       banner: undefined,
       create: () => ('Create a new object.'),
+      createSuccess: () => undefined,
       createWarning: () => (false),
       destroy: () => ('Delete an object permanently. This operation can likely not be undone.'),
       destroyInfo: () => (false),
@@ -378,7 +375,7 @@ export default {
     itemDefaults: () => ({}),
     // callbacks
     itemIsReadOnly: () => false,
-    postcreate: () => (undefined),
+    postcreate: this.close,
     keyupHandler: (e) => {
       // Intercept Enter key
       if (e.keyCode === 13) {
@@ -402,7 +399,6 @@ export default {
       });
       return cols; // data table expects an array
     },
-    snackbarInfo() { return !!this.snackbarInfoText; },
     writeableColumns() {
       const filter = function (obj, predicate) {
         const result = {};
@@ -500,6 +496,7 @@ export default {
         // new item
         this.createDialogWorking = true;
         this.createDialogError = false;
+        this.createDialogSuccess = false;
         const url = this.resourcePath(
                 this.resourcePath(this.paths.create, this.$route.params, '::'),
                 this.createDialogItem,
@@ -507,7 +504,8 @@ export default {
         );
         const r = await withWorking(this.error, () => HTTP.post(url, self.createDialogItem))
         if (r) {
-          this.close();
+          this.createDialogItem = r.data;
+          this.createDialogSuccess = true;
           const l = this.rows.push(r.data);
           this.postcreate(this.rows[l - 1]);
         }
@@ -522,6 +520,7 @@ export default {
       this.createDialogItem = Object.assign({}, this.itemDefaults());
       this.createDialogIndex = null;
       this.createDialogError = false;
+      this.createDialogSuccess = false;
       for (const c in this.columns) {
         this.columns[c].createErrors = [];
       }

+ 4 - 1
webapp/src/views/DomainList.vue

@@ -66,7 +66,10 @@ export default {
           delete: 'domains/:{name}/',
         },
         itemDefaults: () => ({ name: '' }),
-        postcreate: d => this.showDomainInfo(d, true),
+        postcreate: d => {
+          this.close();
+          this.showDomainInfo(d, true);
+        },
         async showDomainInfo(d, isNew = false) {
           const url = this.resourcePath(this.paths.delete, d, ':');
           if (d.keys === undefined) {

+ 5 - 4
webapp/src/views/TokenList.vue

@@ -17,7 +17,8 @@ export default {
         },
         texts: {
           banner: () => ('Any API Token can be used to perform any DNS operation on any domain in this account. Token scoping is on our roadmap.'),
-          create: () => ('Names are purely for your own convenience and carry no technical meaning.'),
+          create: () => ('<p>You can create a new API token here. The token is displayed after submitting this form.</p><p><strong>Warning:</strong> Be sure to protect your tokens appropriately! Knowledge of an API token allows performing actions on your behalf.</p>'),
+          createSuccess: (item) => `Your new token is: <code>${item.token}</code><br />It is only displayed once.`,
           destroy: d => (d.name ? `Delete token with name "${d.name}" and ID ${d.id}?` : `Delete unnamed token with ID ${d.id}?`),
           destroyInfo: () => ('This operation is permanent. Any devices using this token will no longer be able to authenticate.'),
           destroyWarning: d => (d.id == store.state.token.id ? 'This is your current session token. Deleting it will invalidate the session.' : ''),
@@ -25,7 +26,7 @@ export default {
         columns: {
           id: {
             name: 'item.id',
-            text: 'ID',
+            text: 'Identifier',
             align: 'left',
             value: 'id',
             readonly: true,
@@ -35,7 +36,7 @@ export default {
           name: {
             name: 'item.name',
             text: 'Name',
-            textCreate: 'Token name',
+            textCreate: 'Token name (for your convenience only)',
             align: 'left',
             sortable: true,
             value: 'name',
@@ -73,7 +74,7 @@ export default {
         },
         itemDefaults: () => ({ name: '' }),
         itemIsReadOnly: (item) => item.id == store.state.token.id,
-        postcreate(d) { this.snackbarInfoText = `Your new token is: <code>${d.token}</code><br />It is only displayed once.`; },
+        postcreate: () => false,  // do not close dialog
     }
   },
 };