|
1 | 1 | <template> |
2 | 2 | <div> |
| 3 | + <div |
| 4 | + class="form-group dropzone" |
| 5 | + v-if="ui_needs_upload" |
| 6 | + v-on:dragover.prevent="dragover_handler" |
| 7 | + v-on:dragleave.prevent="dragleave_handler" |
| 8 | + v-on:drop.prevent="drop_handler" |
| 9 | + v-on:click="show_file_select" |
| 10 | + v-bind:class="{ |
| 11 | + 'dropzone--can-drop': ui_can_drop, |
| 12 | + 'dropzone--error': ui_drop_error, |
| 13 | + 'dropzone--success': ui_drop_success, |
| 14 | + }" |
| 15 | + > |
| 16 | + <strong>Drag your GCE config file or click here</strong> |
| 17 | + <p> |
| 18 | + File <code>configs/gce.json</code> was not found. Please create it (<a |
| 19 | + href="https://github.com/trailofbits/algo/blob/master/docs/cloud-gce.md" |
| 20 | + target="_blank" |
| 21 | + rel="noopener noreferrer" |
| 22 | + >how?</a |
| 23 | + >) or upload. |
| 24 | + </p> |
| 25 | + <p>After upload it <strong>will be saved</strong> in the configs folder.</p> |
| 26 | + <div v-if="ui_drop_error" class="alert alert-warning" role="alert"> |
| 27 | + <strong>Error:</strong> {{ ui_drop_error }}. |
| 28 | + </div> |
| 29 | + |
| 30 | + <div v-if="ui_drop_success" class="alert alert-success" role="alert"> |
| 31 | + <strong>{{ ui_drop_filename }} loaded successfully</strong> |
| 32 | + </div> |
| 33 | + </div> |
| 34 | + <input type="file" accept=".json,applciation/json" v-on:change="filechange_handler" /> |
| 35 | + |
| 36 | + <div class="form-group"> |
| 37 | + <region-select v-model="region" v-bind:options="ui_region_options" v-bind:loading="ui_loading_check || ui_loading_regions"> |
| 38 | + <label>Please specify <code>gce.json</code> credentials file to select region</label> |
| 39 | + </region-select> |
| 40 | + </div> |
| 41 | + |
3 | 42 | <button |
4 | 43 | class="btn btn-primary" |
5 | 44 | type="button" |
|
13 | 52 |
|
14 | 53 | <script> |
15 | 54 | module.exports = { |
16 | | - data: function() { |
| 55 | + data: function () { |
17 | 56 | return { |
| 57 | + drop_error: null, |
18 | 58 | gce_credentials_file: null, |
19 | 59 | region: null, |
20 | 60 | // helper variables |
21 | | - region_options: [], |
22 | | - is_loading: false |
| 61 | + ui_can_drop: false, |
| 62 | + ui_drop_error: null, |
| 63 | + ui_drop_success: null, |
| 64 | + ui_drop_filename: null, |
| 65 | + ui_needs_upload: null, |
| 66 | + ui_loading_regions: false, |
| 67 | + ui_loading_check: false, |
| 68 | + ui_region_options: [] |
23 | 69 | }; |
24 | 70 | }, |
| 71 | + created: function() { |
| 72 | + this.check_config(); |
| 73 | + }, |
25 | 74 | computed: { |
26 | 75 | is_valid() { |
27 | 76 | return this.gce_credentials_file && this.region; |
28 | | - }, |
29 | | - is_region_disabled() { |
30 | | - return !(this.gce_credentials_file) || this.is_loading; |
31 | 77 | } |
32 | 78 | }, |
33 | 79 | methods: { |
34 | | - load_regions() { |
35 | | - if (this.gce_credentials_file && this.region_options.length === 0) { |
36 | | - this.is_loading = true; |
37 | | - fetch('/gce_regions', { |
38 | | - method: 'post', |
39 | | - headers: { |
40 | | - 'Content-Type': 'application/json' |
41 | | - }, |
42 | | - body: JSON.stringify({ |
43 | | - gce_credentials_file: this.gce_credentials_file |
44 | | - }) |
45 | | - }) |
| 80 | + show_file_select(e) { |
| 81 | + if (e.target.tagName === 'A') { |
| 82 | + return; |
| 83 | + } |
| 84 | + const input = this.$el.querySelector(['input[type=file]']); |
| 85 | + const event = new MouseEvent('click', { |
| 86 | + 'view': window, |
| 87 | + 'bubbles': true, |
| 88 | + 'cancelable': true |
| 89 | + }); |
| 90 | + input.dispatchEvent(event); |
| 91 | + }, |
| 92 | + dragover_handler(e) { |
| 93 | + this.ui_can_drop = true; |
| 94 | + this.ui_drop_success = false; |
| 95 | + this.ui_drop_error = false; |
| 96 | + this.ui_drop_filename = null; |
| 97 | + }, |
| 98 | + dragleave_handler() { |
| 99 | + this.ui_can_drop = false; |
| 100 | + }, |
| 101 | + drop_handler(e) { |
| 102 | + try { |
| 103 | + const droppedFiles = e.dataTransfer.files; |
| 104 | + if (droppedFiles.length !== 1) { |
| 105 | + this.ui_drop_error = 'Please upload GCE config as single file'; |
| 106 | + } |
| 107 | + this.read_file(droppedFiles[0]); |
| 108 | + } catch (e) { |
| 109 | + this.ui_drop_error = 'Unhandled error while trying to read GCE config'; |
| 110 | + } |
| 111 | + }, |
| 112 | + filechange_handler(e) { |
| 113 | + if (e.target.files.length) { |
| 114 | + this.read_file(e.target.files[0]); |
| 115 | + } |
| 116 | + }, |
| 117 | + read_file(file) { |
| 118 | + if (file.type !== 'application/json') { |
| 119 | + this.ui_drop_error = 'Incorrect file type'; |
| 120 | + } |
| 121 | + const reader = new FileReader(); |
| 122 | + reader.onload = e => { |
| 123 | + let gce_config_content = null; |
| 124 | + try { |
| 125 | + gce_config_content = JSON.parse(e.target.result); |
| 126 | + this.ui_drop_success = true; |
| 127 | + this.ui_drop_filename = file.name; |
| 128 | + this.gce_credentials_file = 'configs/gce.json'; |
| 129 | + } catch (e) { |
| 130 | + this.ui_drop_error = 'JSON format error'; |
| 131 | + } |
| 132 | + gce_config_content && this.load_regions(gce_config_content); |
| 133 | + } |
| 134 | + reader.onerror = e => { |
| 135 | + this.ui_drop_error = 'Error while reading file'; |
| 136 | + } |
| 137 | + reader.readAsText(file); |
| 138 | + |
| 139 | + }, |
| 140 | + check_config() { |
| 141 | + this.ui_loading_check = true; |
| 142 | + fetch("/gce_config") |
46 | 143 | .then(r => r.json()) |
47 | | - .then(data => { |
48 | | - this.region_options = data; |
| 144 | + .then(response => { |
| 145 | + if (response.status === 'ok') { |
| 146 | + this.gce_credentials_file = 'configs/gce.json'; |
| 147 | + this.load_regions(); |
| 148 | + this.ui_needs_upload = false; |
| 149 | + } else { |
| 150 | + this.ui_needs_upload = true; |
| 151 | + } |
49 | 152 | }) |
50 | 153 | .finally(() => { |
51 | | - this.is_loading = false; |
| 154 | + this.ui_loading_check = false; |
52 | 155 | }); |
| 156 | + }, |
| 157 | + load_regions(gce_config_content) { |
| 158 | + if (this.gce_credentials_file && this.ui_region_options.length === 0) { |
| 159 | + this.ui_loading_regions = true; |
| 160 | + fetch("/gce_regions", { |
| 161 | + method: "post", |
| 162 | + headers: { |
| 163 | + "Content-Type": "application/json", |
| 164 | + }, |
| 165 | + body: gce_config_content ? JSON.stringify(gce_config_content) : '{}', |
| 166 | + }) |
| 167 | + .then((r) => r.json()) |
| 168 | + .then((data) => { |
| 169 | + this.ui_region_options = data.items.map(i => ({ |
| 170 | + value: i.name, |
| 171 | + key: i.name |
| 172 | + })); |
| 173 | + }) |
| 174 | + .finally(() => { |
| 175 | + this.ui_loading_regions = false; |
| 176 | + }); |
53 | 177 | } |
54 | 178 | }, |
55 | 179 | submit() { |
56 | | - this.$emit('submit', { |
| 180 | + this.$emit("submit", { |
57 | 181 | gce_credentials_file: this.gce_credentials_file, |
58 | | - region: this.region |
| 182 | + region: this.region, |
59 | 183 | }); |
60 | | - } |
61 | | - } |
| 184 | + }, |
| 185 | + }, |
| 186 | + components: { |
| 187 | + "region-select": window.httpVueLoader("/static/region-select.vue"), |
| 188 | + }, |
62 | 189 | }; |
63 | 190 | </script> |
| 191 | +<style scoped> |
| 192 | +.dropzone { |
| 193 | + padding: 2em; |
| 194 | + border: 5px dotted #ccc; |
| 195 | + cursor: pointer; |
| 196 | +} |
| 197 | +input[type=file] { |
| 198 | + visibility: hidden; |
| 199 | +} |
| 200 | +.dropzone--can-drop { |
| 201 | + border-color: var(--blue); |
| 202 | +} |
| 203 | +.dropzone--error { |
| 204 | + border-color: var(--red); |
| 205 | +} |
| 206 | +.dropzone--success { |
| 207 | + border-color: var(--green); |
| 208 | +} |
| 209 | +</style> |
0 commit comments