You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
8.1 KiB

  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row, wrap)
  4. v-flex(xs12)
  5. .admin-header
  6. img.animated.fadeInUp(src='/_assets/svg/icon-rest-api.svg', alt='API', style='width: 80px;')
  7. .admin-header-title
  8. .headline.primary--text.animated.fadeInLeft {{$t('admin:api.title')}}
  9. .subtitle-1.grey--text.animated.fadeInLeft {{$t('admin:api.subtitle')}}
  10. v-spacer
  11. template(v-if='enabled')
  12. status-indicator.mr-3(positive, pulse)
  13. .caption.green--text.animated.fadeInLeft {{$t('admin:api.enabled')}}
  14. template(v-else)
  15. status-indicator.mr-3(negative, pulse)
  16. .caption.red--text.animated.fadeInLeft {{$t('admin:api.disabled')}}
  17. v-spacer
  18. v-btn.mr-3.animated.fadeInDown.wait-p2s(outlined, color='grey', icon, @click='refresh')
  19. v-icon mdi-refresh
  20. v-btn.mr-3.animated.fadeInDown.wait-p1s(:color='enabled ? `red` : `green`', depressed, @click='globalSwitch', dark, :loading='isToggleLoading')
  21. v-icon(left) mdi-power
  22. span(v-if='!enabled') {{$t('admin:api.enableButton')}}
  23. span(v-else) {{$t('admin:api.disableButton')}}
  24. v-btn.animated.fadeInDown(color='primary', depressed, large, @click='newKey', dark)
  25. v-icon(left) mdi-plus
  26. span {{$t('admin:api.newKeyButton')}}
  27. v-card.mt-3.animated.fadeInUp
  28. v-simple-table(v-if='keys && keys.length > 0')
  29. template(v-slot:default)
  30. thead
  31. tr.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-5`')
  32. th {{$t('admin:api.headerName')}}
  33. th {{$t('admin:api.headerKeyEnding')}}
  34. th {{$t('admin:api.headerExpiration')}}
  35. th {{$t('admin:api.headerCreated')}}
  36. th {{$t('admin:api.headerLastUpdated')}}
  37. th(width='100') {{$t('admin:api.headerRevoke')}}
  38. tbody
  39. tr(v-for='key of keys', :key='`key-` + key.id')
  40. td
  41. strong(:class='key.isRevoked ? `red--text` : ``') {{ key.name }}
  42. em.caption.ml-1.red--text(v-if='key.isRevoked') (revoked)
  43. td.caption {{ key.keyShort }}
  44. td(:style='key.isRevoked ? `text-decoration: line-through;` : ``') {{ key.expiration | moment('LL') }}
  45. td {{ key.createdAt | moment('calendar') }}
  46. td {{ key.updatedAt | moment('calendar') }}
  47. td: v-btn(icon, @click='revoke(key)', :disabled='key.isRevoked'): v-icon(color='error') mdi-cancel
  48. v-card-text(v-else)
  49. v-alert.mb-0(icon='mdi-information', :value='true', outlined, color='info') {{$t('admin:api.noKeyInfo')}}
  50. create-api-key(v-model='isCreateDialogShown', @refresh='refresh(false)')
  51. v-dialog(v-model='isRevokeConfirmDialogShown', max-width='500', persistent)
  52. v-card
  53. .dialog-header.is-red {{$t('admin:api.revokeConfirm')}}
  54. v-card-text.pa-4
  55. i18next(tag='span', path='admin:api.revokeConfirmText')
  56. strong(place='name') {{ current.name }}
  57. v-card-actions
  58. v-spacer
  59. v-btn(text, @click='isRevokeConfirmDialogShown = false', :disabled='revokeLoading') {{$t('common:actions.cancel')}}
  60. v-btn(color='red', dark, @click='revokeConfirm', :loading='revokeLoading') {{$t('admin:api.revoke')}}
  61. </template>
  62. <script>
  63. import _ from 'lodash'
  64. import gql from 'graphql-tag'
  65. import { StatusIndicator } from 'vue-status-indicator'
  66. import CreateApiKey from './admin-api-create.vue'
  67. export default {
  68. components: {
  69. StatusIndicator,
  70. CreateApiKey
  71. },
  72. data() {
  73. return {
  74. enabled: false,
  75. isToggleLoading: false,
  76. keys: [],
  77. isCreateDialogShown: false,
  78. isRevokeConfirmDialogShown: false,
  79. revokeLoading: false,
  80. current: {}
  81. }
  82. },
  83. methods: {
  84. async refresh (notify = true) {
  85. this.$apollo.queries.keys.refetch()
  86. if (notify) {
  87. this.$store.commit('showNotification', {
  88. message: this.$t('admin:api.refreshSuccess'),
  89. style: 'success',
  90. icon: 'cached'
  91. })
  92. }
  93. },
  94. async globalSwitch () {
  95. this.isToggleLoading = true
  96. try {
  97. const resp = await this.$apollo.mutate({
  98. mutation: gql`
  99. mutation ($enabled: Boolean!) {
  100. authentication {
  101. setApiState (enabled: $enabled) {
  102. responseResult {
  103. succeeded
  104. errorCode
  105. slug
  106. message
  107. }
  108. }
  109. }
  110. }
  111. `,
  112. variables: {
  113. enabled: !this.enabled
  114. },
  115. watchLoading (isLoading) {
  116. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-toggle')
  117. }
  118. })
  119. if (_.get(resp, 'data.authentication.setApiState.responseResult.succeeded', false)) {
  120. this.$store.commit('showNotification', {
  121. style: 'success',
  122. message: this.enabled ? this.$t('admin:api.toggleStateDisabledSuccess') : this.$t('admin:api.toggleStateEnabledSuccess'),
  123. icon: 'check'
  124. })
  125. await this.$apollo.queries.enabled.refetch()
  126. } else {
  127. this.$store.commit('showNotification', {
  128. style: 'red',
  129. message: _.get(resp, 'data.authentication.setApiState.responseResult.message', 'An unexpected error occurred.'),
  130. icon: 'alert'
  131. })
  132. }
  133. } catch (err) {
  134. this.$store.commit('pushGraphError', err)
  135. }
  136. this.isToggleLoading = false
  137. },
  138. async newKey () {
  139. this.isCreateDialogShown = true
  140. },
  141. revoke (key) {
  142. this.current = key
  143. this.isRevokeConfirmDialogShown = true
  144. },
  145. async revokeConfirm () {
  146. this.revokeLoading = true
  147. try {
  148. const resp = await this.$apollo.mutate({
  149. mutation: gql`
  150. mutation ($id: Int!) {
  151. authentication {
  152. revokeApiKey (id: $id) {
  153. responseResult {
  154. succeeded
  155. errorCode
  156. slug
  157. message
  158. }
  159. }
  160. }
  161. }
  162. `,
  163. variables: {
  164. id: this.current.id
  165. },
  166. watchLoading (isLoading) {
  167. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-revoke')
  168. }
  169. })
  170. if (_.get(resp, 'data.authentication.revokeApiKey.responseResult.succeeded', false)) {
  171. this.$store.commit('showNotification', {
  172. style: 'success',
  173. message: this.$t('admin:api.revokeSuccess'),
  174. icon: 'check'
  175. })
  176. this.refresh(false)
  177. } else {
  178. this.$store.commit('showNotification', {
  179. style: 'red',
  180. message: _.get(resp, 'data.authentication.revokeApiKey.responseResult.message', 'An unexpected error occurred.'),
  181. icon: 'alert'
  182. })
  183. }
  184. } catch (err) {
  185. this.$store.commit('pushGraphError', err)
  186. }
  187. this.isRevokeConfirmDialogShown = false
  188. this.revokeLoading = false
  189. }
  190. },
  191. apollo: {
  192. enabled: {
  193. query: gql`
  194. {
  195. authentication {
  196. apiState
  197. }
  198. }
  199. `,
  200. fetchPolicy: 'network-only',
  201. update: (data) => data.authentication.apiState,
  202. watchLoading (isLoading) {
  203. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-state-refresh')
  204. }
  205. },
  206. keys: {
  207. query: gql`
  208. {
  209. authentication {
  210. apiKeys {
  211. id
  212. name
  213. keyShort
  214. expiration
  215. isRevoked
  216. createdAt
  217. updatedAt
  218. }
  219. }
  220. }
  221. `,
  222. fetchPolicy: 'network-only',
  223. update: (data) => data.authentication.apiKeys,
  224. watchLoading (isLoading) {
  225. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-keys-refresh')
  226. }
  227. }
  228. }
  229. }
  230. </script>
  231. <style lang='scss'>
  232. </style>