GlobalSearch.vue 15.6 KB
Newer Older
Robert's avatar
Robert committed
1 2 3
<template>
  <b-form inline @submit="searchAction" class="mr-2">
    <transition name="slide">
4
      <b-form-group v-if="show_search || ($refs.navdropdown && $refs.navdropdown.show)" class="position-relative">
Robert's avatar
Robert committed
5
        <b-form-input
6 7 8
          size="sm" placeholder="Suche & Schnellnavigation"
          id="search"
          ref="search"
Janis Streib's avatar
FIXUP  
Janis Streib committed
9
          autocomplete="off"
Janis Streib's avatar
Janis Streib committed
10 11 12 13 14 15 16
          v-model="search_input" class="mr-2 search"
          @focus="search_has_focus = true"
          @blur="searchLostFocus"
          @keydown.tab="searchKeyboardTab($event)"
          @keydown.down="searchKeyboardSelect($event,true)"
          @keydown.up="searchKeyboardSelect($event,false)"
          @keydown.enter="searchKeyboardSubmit($event)"
Janis Streib's avatar
Janis Streib committed
17
          @update="checkSearch()"
Robert's avatar
Robert committed
18 19
        />
        <b-button class="search-clear bg-transparent" size="sm" v-if="search_input !== ''"
Janis Streib's avatar
Janis Streib committed
20
                  :disabled="searching > 0"
Robert's avatar
Robert committed
21 22 23 24 25 26
                  @click="search_input = ''">
          <font-awesome-icon :icon="['fas','times']" class="text-secondary"/>
        </b-button>
      </b-form-group>
    </transition>
    <b-popover custom-class="popover-wide suggestions-popover shadow"
Janis Streib's avatar
Janis Streib committed
27
               :show="(cutDescriminator() !== '' && search_has_focus) || searching !== 0"
Robert's avatar
Robert committed
28 29
               target="search" placement="bottom">
      <div class="suggestions-wrapper">
Janis Streib's avatar
Janis Streib committed
30 31
        <div v-if="getSearchDiscriminator()" class="text-center suggestion p-3"><i>Globale Suche
          in </i><b>{{ getSearchDiscriminator() }}</b></div>
Robert's avatar
Robert committed
32 33 34
        <div v-for="(suggestion, index) in suggestions"
             :key="'search-suggestion-' + index"
             class="suggestion p-3"
35
             :class="index === keyboard_focus ? 'keyboard-focus':''"
Robert's avatar
Robert committed
36
             @click="suggestionClicked($event, suggestion)">
Robert's avatar
Robert committed
37
          <font-awesome-icon class="text-secondary mr-2 suggestion-icon"
Janis Streib's avatar
Janis Streib committed
38
                             :icon="suggIcon(suggestion.type)"/>
Robert's avatar
Robert committed
39
          <font-awesome-icon class="text-primary mr-2 suggestion-arrow" :icon="['fas', 'arrow-right']"/>
Janis Streib's avatar
Janis Streib committed
40
          <b-badge style="margin-right: 5px" v-if="suggestion.type !== 'page'">{{ suggestion.type }}</b-badge>
Janis Streib's avatar
Janis Streib committed
41
          {{ suggestion.name[0] }}<b>{{ suggestion.name[1] }}</b>{{ suggestion.name[2] }}
Robert's avatar
Robert committed
42
        </div>
Janis Streib's avatar
Janis Streib committed
43 44 45 46 47 48 49
        <div v-if="searching !== 0">
          <div v-if="descriminator" class="text-center"><i>Suche in </i><b>{{ descriminator }}</b></div>
          <div class="text-center mt-3">
            <b-spinner variant="primary"/>
          </div>
        </div>
        <div v-if="searching === 0 && suggestions && suggestions.length === 0"
Robert's avatar
Robert committed
50 51
             class="font-italic text-center p-3">
          Keine Ergebnisse
Janis Streib's avatar
Janis Streib committed
52 53
          <template v-if="search_input != last_search && search_input !== ''">
            <hr/>
54
            Enter oder Such-Button klicken, um eine neue Suche auszulösen<br>
Janis Streib's avatar
Janis Streib committed
55
            <code>s/&lt;regex&gt;</code> für RegEx-Suche<br>
56 57
            <code>&lt;systemname&gt;:&lt;suche&gt;</code> für globale Suche im System (z.B.
            <code>dnsvs:s/^kit.edu</code>)
Janis Streib's avatar
Janis Streib committed
58
          </template>
Robert's avatar
Robert committed
59 60 61 62 63 64 65 66
        </div>
      </div>
    </b-popover>
    <b-button class="search-button" size="sm" @click="searchAction"
              :variant="search_input === '' ? 'secondary' : 'success'">
      <font-awesome-icon :icon="['fas','search']"/>
    </b-button>
    <div class="search-cover-wrapper">
Janis Streib's avatar
Janis Streib committed
67
      <div :class="`bg-${$sysinfo.host_oper_mode.mode} search-cover`"/>
Robert's avatar
Robert committed
68 69 70 71 72
    </div>
  </b-form>
</template>

<script>
Janis Streib's avatar
Janis Streib committed
73
import SearchService from '@/api-services/search.service'
74
import ipaddress from 'ipaddr.js'
Janis Streib's avatar
Janis Streib committed
75

Robert's avatar
Robert committed
76
export default {
Janis Streib's avatar
Janis Streib committed
77
  name: 'GlobalSearch',
Janis Streib's avatar
Janis Streib committed
78
  data() {
Robert's avatar
Robert committed
79
    return {
Janis Streib's avatar
Janis Streib committed
80
      last_search: null,
Janis Streib's avatar
Janis Streib committed
81 82 83
      search_res: [],
      descriminator: null,
      searching: 0,
Robert's avatar
Robert committed
84 85 86 87 88
      show_search: false,
      search_input: '',
      search_has_focus: false,
      pages: [
        {
Janis Streib's avatar
Janis Streib committed
89 90
          name: 'DNSVS',
          url: '/dnsvs'
Robert's avatar
Robert committed
91 92
        },
        {
Janis Streib's avatar
Janis Streib committed
93
          name: 'Ihre BCDs',
Janis Streib's avatar
Janis Streib committed
94
          url: '/dnsvs/bcds'
Robert's avatar
Robert committed
95
        },
Janis Streib's avatar
Janis Streib committed
96 97 98 99
        {
          name: 'Ihre Domains',
          url: '/dnsvs/fqdns'
        },
Robert's avatar
Robert committed
100
        {
101
          name: 'Unterkonten & API-Tokens',
Janis Streib's avatar
Janis Streib committed
102
          url: '/user/tokens'
Robert's avatar
Robert committed
103 104
        },
        {
105
          name: 'API-Browser (Swagger)',
Janis Streib's avatar
Janis Streib committed
106
          url: '/swagger'
Robert's avatar
Robert committed
107 108
        },
        {
Janis Streib's avatar
Janis Streib committed
109 110
          name: 'MACAuth',
          url: '/macauth'
111 112 113 114 115 116 117 118
        },
        {
          name: 'Orgranisationseinheiten (OE/OU)',
          url: '/cntl/ou'
        },
        {
          name: 'Gruppen & Untergruppen',
          url: '/cntl/groups'
Janis Streib's avatar
Janis Streib committed
119
        }
Robert's avatar
Robert committed
120
      ],
121
      keyboard_focus: -1
Robert's avatar
Robert committed
122 123 124
    }
  },
  computed: {
Janis Streib's avatar
Janis Streib committed
125
    suggestions() {
Janis Streib's avatar
Janis Streib committed
126
      const suggestions = []
Janis Streib's avatar
FIXUP  
Janis Streib committed
127
      const search = this.cutDescriminator().trim()
128 129 130 131
      let isRegex = false
      if (search.startsWith('s/')) {
        isRegex = true
      }
Robert's avatar
Robert committed
132
      this.pages.forEach(page => {
Janis Streib's avatar
Janis Streib committed
133
        const index = page.name.toLowerCase().indexOf(search.toLowerCase())
Janis Streib's avatar
Janis Streib committed
134 135 136
        if (index !== -1) {
          suggestions.push({
            name:
Janis Streib's avatar
Janis Streib committed
137 138
              [page.name.substring(0, index),
                page
Janis Streib's avatar
Janis Streib committed
139
                  .name.substring(index, index + search.length),
Janis Streib's avatar
Janis Streib committed
140
                page
Janis Streib's avatar
Janis Streib committed
141
                  .name.substring(index + search.length)
Janis Streib's avatar
Janis Streib committed
142
              ],
Janis Streib's avatar
Janis Streib committed
143 144 145 146
            type: 'page',
            url: page.url
          })
        }
Janis Streib's avatar
Janis Streib committed
147 148 149
      })
      this.search_res.forEach(item => {
        let index = -1
Janis Streib's avatar
Janis Streib committed
150
        let record_composit = null
151
        let compiled_term = null
Janis Streib's avatar
Janis Streib committed
152
        switch (item.type.toLowerCase()) {
Janis Streib's avatar
Janis Streib committed
153 154
          case 'dns.record':
            record_composit = item.term.fqdn + ' IN ' + item.term.type + ' ' + item.term.data
155
            if (isRegex) {
Janis Streib's avatar
Janis Streib committed
156 157
              suggestions.push({
                name:
158 159 160
                  ['',
                    record_composit,
                    ''
Janis Streib's avatar
Janis Streib committed
161 162 163 164
                  ],
                type: item.type,
                url: '/dnsvs/fqdns/' + item.term.fqdn + '/records'
              })
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
            } else {
              index = record_composit.toLowerCase().indexOf(search.toLowerCase())
              if (index !== -1) {
                suggestions.push({
                  name:
                    [record_composit.substring(0, index),
                      record_composit
                        .substring(index, index + search.length),
                      record_composit
                        .substring(index + search.length)
                    ],
                  type: item.type,
                  url: '/dnsvs/fqdns/' + item.term.fqdn + '/records'
                })
              }
Janis Streib's avatar
Janis Streib committed
180 181
            }
            break
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
          case 'nd.ip_subnet':
            if (isRegex) {
              suggestions.push({
                name:
                  ['',
                    item.term.cidr
                      .substring(index, index + search.length),
                    ''
                  ],
                type: item.type,
                url: '/dnsvs/bcds/' + item.term.bcd
              })
            } else {
              suggestions.push({
                name:
                  ['',
                    item.term.cidr + ' zur BCD ' + item.term.bcd,
                    ''
                  ],
                type: item.type,
                url: '/dnsvs/bcds/' + item.term.bcd
              })
            }
            break
Janis Streib's avatar
Janis Streib committed
206
          case 'nd.bcd':
207
            if (isRegex) {
Janis Streib's avatar
Janis Streib committed
208 209
              suggestions.push({
                name:
210
                  ['',
Janis Streib's avatar
Janis Streib committed
211
                    item.term.name
Janis Streib's avatar
Janis Streib committed
212
                      .substring(index, index + search.length),
213
                    ''
Janis Streib's avatar
Janis Streib committed
214 215 216 217
                  ],
                type: item.type,
                url: '/dnsvs/bcds/' + item.term.name
              })
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
            } else {
              index = item.term.name.toLowerCase().indexOf(search.toLowerCase())
              if (index !== -1) {
                suggestions.push({
                  name:
                    [item.term.name.substring(0, index),
                      item.term.name
                        .substring(index, index + search.length),
                      item.term.name
                        .substring(index + search.length)
                    ],
                  type: item.type,
                  url: '/dnsvs/bcds/' + item.term.name
                })
              }
Janis Streib's avatar
Janis Streib committed
233 234
            }
            break
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
          case 'nd.vlan':
            compiled_term = item.term.name + ' (' + item.term.id + '@' + item.term.net_instnc + ')'
            if (isRegex) {
              suggestions.push({
                name:
                  ['',
                    compiled_term,
                    ''
                  ],
                type: item.type,
                url: '/dnsvs/bcds/' + item.term.bcd
              })
            } else {
              index = compiled_term.toLowerCase().indexOf(search.toLowerCase())
              if (index !== -1) {
                suggestions.push({
                  name:
                    [compiled_term.substring(0, index),
                      compiled_term
                        .substring(index, index + search.length),
                      compiled_term
                        .substring(index + search.length)
                    ],
                  type: item.type,
                  url: '/dnsvs/bcds/' + item.term.bcd
                })
              }
            }
            break
Janis Streib's avatar
Janis Streib committed
264 265
        }
      })
Robert's avatar
Robert committed
266 267 268 269
      return suggestions
    }
  },
  methods: {
Janis Streib's avatar
Janis Streib committed
270 271 272 273 274 275 276
    suggIcon(type) {
      if (type === 'page') {
        return 'link'
      }
      if (type.startsWith('dns.')) {
        return 'map-signs'
      }
277 278 279
      if (type === 'nd.vlan') {
        return 'ethernet'
      }
Janis Streib's avatar
Janis Streib committed
280 281 282 283 284
      if (type.startsWith('nd.')) {
        return 'network-wired'
      }
      return 'questions'
    },
Janis Streib's avatar
Janis Streib committed
285 286 287 288 289 290
    checkSearch() {
      this.keyboard_focus = -1
      if (this.search_input === '') {
        this.search_res = []
      }
    },
Janis Streib's avatar
Janis Streib committed
291
    getSearchDiscriminator() {
Janis Streib's avatar
Janis Streib committed
292 293 294 295 296
      if (this.search_input.indexOf(':') === -1) {
        return null
      }
      const parts = this.search_input.split(':', 2)
      switch (parts[0].toLowerCase()) {
Janis Streib's avatar
Janis Streib committed
297 298
        case 'dnsvs':
          return parts[0]
Janis Streib's avatar
Janis Streib committed
299
        case 'macauth':
Janis Streib's avatar
Janis Streib committed
300
          return parts[0]
Janis Streib's avatar
Janis Streib committed
301 302 303
      }
      return null
    },
304 305 306 307 308 309
    cutRegexDescriminator() {
      if (this.search_input.startsWith('s/')) {
        return this.search_input.slice(2)
      }
      return this.search_input
    },
Janis Streib's avatar
Janis Streib committed
310 311 312 313 314 315 316
    cutDescriminator() {
      const descr = this.getSearchDiscriminator()
      if (descr !== null) {
        return this.search_input.slice(descr.length + 1)
      }
      return this.search_input
    },
317
    dispatch_search(fun, type, index = 0) {
Janis Streib's avatar
Janis Streib committed
318 319
      const self = this
      fun.then((response) => {
320 321 322 323
        if (this.searching === 0) {
          window.console.debug('Dropping result.')
          return
        }
324
        response.data[index].forEach((item) => {
Janis Streib's avatar
Janis Streib committed
325 326 327 328 329 330 331 332 333
          self.search_res.push({term: item, type: type})
        })
        self.searching--
      }).catch(() => {
        self.searching--
      })
      this.searching++
    },
    searchAction(e) {
Robert's avatar
Robert committed
334 335 336
      e.preventDefault()
      if (this.search_input === '') {
        this.show_search = !this.show_search
337
        this.keyboard_focus = -1
Robert's avatar
Robert committed
338 339 340 341 342 343
        if (this.show_search) {
          this.$nextTick(() => {
            this.$refs.search.focus()
          })
        }
      } else {
Janis Streib's avatar
Janis Streib committed
344
        this.last_search = this.search_input
Janis Streib's avatar
Janis Streib committed
345 346
        this.search_res = []
        this.descriminator = this.getSearchDiscriminator()
Janis Streib's avatar
Janis Streib committed
347
        const term = this.cutDescriminator().trim()
348
        const termIsIP = ipaddress.isValid(term.split('/')[0])
349
        const termIsNumeric = !isNaN(parseInt(term))
Janis Streib's avatar
Janis Streib committed
350
        if (this.descriminator == null) { // Nur is_own, ohne regex
351 352 353
          if (termIsNumeric) {
            this.dispatch_search(SearchService.searchVlan(this.$store.state.netdb_axios_config, parseInt(term)), 'nd.vlan')
          } else if (termIsIP) {
354 355 356 357 358 359
            this.dispatch_search(SearchService.searchRRByIP(this.$store.state.netdb_axios_config, term, true), 'dns.record')
            this.dispatch_search(SearchService.searchBCDSubnet(this.$store.state.netdb_axios_config, term, true), 'nd.ip_subnet')
          } else {
            this.dispatch_search(SearchService.searchBCDRegex(this.$store.state.netdb_axios_config, term, true), 'nd.bcd')
            this.dispatch_search(SearchService.searchRecordRegex(this.$store.state.netdb_axios_config, term, true), 'dns.record')
          }
Janis Streib's avatar
Janis Streib committed
360
        } else if (this.descriminator === 'dnsvs') {
361 362 363
          if (termIsNumeric) {
            this.dispatch_search(SearchService.searchVlan(this.$store.state.netdb_axios_config, parseInt(term)), 'nd.vlan')
          } else if (termIsIP) {
364 365 366 367 368 369 370
            this.dispatch_search(SearchService.searchRRByIP(this.$store.state.netdb_axios_config, term, null), 'dns.record')
            this.dispatch_search(SearchService.searchBCDSubnet(this.$store.state.netdb_axios_config, term, null), 'nd.ip_subnet')
          } else {
            this.dispatch_search(SearchService.searchBCDRegex(this.$store.state.netdb_axios_config, term, null), 'nd.bcd')
            this.dispatch_search(SearchService.searchRecordRegex(this.$store.state.netdb_axios_config, term, null), 'dns.record')
            this.dispatch_search(SearchService.searchRRDataRegex(this.$store.state.netdb_axios_config, term, null), 'dns.record')
          }
Janis Streib's avatar
Janis Streib committed
371 372
        }
        this.descriminator = null
Robert's avatar
Robert committed
373 374
      }
    },
Janis Streib's avatar
Janis Streib committed
375 376
    suggestionClicked(e, suggestion) {
      if ('url' in suggestion && this.$router.currentRoute !== suggestion.url) {
377 378 379 380
        this.show_search = false
        this.search_input = ''
        this.searching = 0
        this.search_res = []
381 382 383 384 385
        if (e.ctrlKey) {
          window.open(this.$router.resolve(suggestion.url).href, '_blank')
        } else {
          this.$router.push(suggestion.url)
        }
Robert's avatar
Robert committed
386 387 388 389
      }
      if (suggestion.type === 'test') {
        window.console.log('Clicked test suggestion: ' + suggestion.name[0] + suggestion.name[1] + suggestion.name[2])
      }
390
    },
Janis Streib's avatar
Janis Streib committed
391
    searchKeyboardSelect(e, down) {
392 393 394
      e.preventDefault()
      if (this.suggestions.length > 0) {
        this.keyboard_focus += down ? 1 : -1
Janis Streib's avatar
Janis Streib committed
395
        this.keyboard_focus = Math.min(Math.max(this.keyboard_focus, -1), this.suggestions.length - 1)
396 397
      }
    },
Janis Streib's avatar
Janis Streib committed
398
    searchKeyboardTab(e) {
Robert's avatar
Robert committed
399 400 401 402
      if (this.keyboard_focus < this.suggestions.length - 1) {
        this.searchKeyboardSelect(e, true)
      }
    },
Janis Streib's avatar
Janis Streib committed
403
    searchKeyboardSubmit(e) {
404 405 406 407 408
      if (this.keyboard_focus !== -1) {
        e.preventDefault()
        this.suggestionClicked(e, this.suggestions[this.keyboard_focus])
      }
    },
Janis Streib's avatar
Janis Streib committed
409
    searchLostFocus() {
410 411
      this.search_has_focus = false
      this.keyboard_focus = -1
Robert's avatar
Robert committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    }
  }
}
</script>

<style scoped>
.slide-enter-active,
.slide-leave-active {
  transition: transform 250ms;
  z-index: -10;
}

.slide-enter,
.slide-leave-to {
  transform: translateX(120%);
}

.search-cover-wrapper {
  position: relative;
  width: 0;
  height: 0;
}

.search-cover {
  z-index: -1;
  position: absolute;
  left: -30px;
  top: -25px;
  width: 300px;
  height: 50px;
}

.search {
445
  max-width: 450px;
Robert's avatar
Robert committed
446 447 448 449 450 451 452 453 454 455
}

.search-button {
  z-index: 1;
}

.suggestions-popover {
  width: 300px;
  max-height: 75vh;
  overflow-y: scroll;
456
  scrollbar-width: none;
Robert's avatar
Robert committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470
}

.suggestions-popover::-webkit-scrollbar {
  display: none;
}

.suggestions-wrapper {
  margin: -0.5rem -0.75rem;
}

.suggestion {
  cursor: pointer;
}

471 472 473 474 475 476 477 478 479 480 481 482
.keyboard-focus {
  background: #b8b8b8 !important;
}

.keyboard-focus > .suggestion-arrow {
  display: initial;
}

.keyboard-focus > .suggestion-icon {
  display: none;
}

Robert's avatar
Robert committed
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
.suggestion:nth-child(even) {
  background: #f1f1f1;
}

.suggestion-arrow {
  display: none;
  width: 16px;
}

.suggestion-icon {
  width: 16px;
}

.suggestion:hover {
  background: #d7d7d7;
}

.suggestion:hover > .suggestion-arrow {
  display: initial;
}

.suggestion:hover > .suggestion-icon {
  display: none;
}

.search-clear,
.search-clear:focus,
.search-clear:hover,
.search-clear:active {
512 513 514
  position: absolute;
  right: 10px;
  top: 1px;
Robert's avatar
Robert committed
515 516
  border: none;
}
517 518 519 520

.popover-wide {
  width: 50vw !important;
}
Robert's avatar
Robert committed
521
</style>