AdminAuth.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <template lang='pug'>
  2. q-page.admin-mail
  3. .row.q-pa-md.items-center
  4. .col-auto
  5. img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-security-lock.svg')
  6. .col.q-pl-md
  7. .text-h5.text-primary.animated.fadeInLeft {{ t('admin.auth.title') }}
  8. .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.auth.subtitle') }}
  9. .col-auto
  10. q-btn.q-mr-sm.acrylic-btn(
  11. icon='las la-question-circle'
  12. flat
  13. color='grey'
  14. :href='siteStore.docsBase + `/admin/auth`'
  15. target='_blank'
  16. type='a'
  17. )
  18. q-btn(
  19. unelevated
  20. icon='mdi-check'
  21. :label='t(`common.actions.apply`)'
  22. color='secondary'
  23. @click='save'
  24. :loading='state.loading > 0'
  25. )
  26. q-separator(inset)
  27. .row.q-pa-md.q-col-gutter-md
  28. .col-auto
  29. q-card.rounded-borders.bg-dark
  30. q-list(
  31. style='min-width: 350px;'
  32. padding
  33. dark
  34. )
  35. q-item(
  36. v-for='str of combinedActiveStrategies'
  37. :key='str.key'
  38. active-class='bg-primary text-white'
  39. :active='state.selectedStrategy === str.id'
  40. @click='state.selectedStrategy = str.id'
  41. clickable
  42. )
  43. q-item-section(side)
  44. q-icon(:name='`img:` + str.strategy.icon')
  45. q-item-section
  46. q-item-label {{str.displayName}}
  47. q-item-label(caption) {{str.strategy.title}}
  48. q-item-section(side)
  49. status-light(:color='str.isEnabled ? `positive` : `negative`', :pulse='str.isEnabled')
  50. q-btn.q-mt-sm.full-width(
  51. color='primary'
  52. icon='las la-plus'
  53. :label='t(`admin.auth.addStrategy`)'
  54. )
  55. q-menu(auto-close, fit, max-width='300px')
  56. q-list(separator)
  57. q-item(
  58. v-for='str of availableStrategies'
  59. :key='str.key'
  60. clickable
  61. @click='addStrategy(str)'
  62. )
  63. q-item-section(avatar)
  64. q-avatar(
  65. rounded
  66. color='dark'
  67. text-color='white'
  68. )
  69. q-icon(
  70. :name='`img:` + str.icon'
  71. )
  72. q-item-section
  73. q-item-label: strong {{str.title}}
  74. q-item-label(caption, lines='2') {{str.description}}
  75. .col
  76. q-card.shadow-1.q-pb-sm
  77. q-card-section
  78. .text-subtitle1 {{t('admin.storage.contentTypes')}}
  79. .text-body2.text-grey {{ t('admin.storage.contentTypesHint') }}
  80. //- -----------------------
  81. //- Configuration
  82. //- -----------------------
  83. q-card.shadow-1.q-pb-sm.q-mt-md
  84. q-card-section
  85. .text-subtitle1 {{t('admin.storage.config')}}
  86. q-banner.q-mt-md(
  87. v-if='!state.strategy.config || Object.keys(state.strategy.config).length < 1'
  88. rounded
  89. :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
  90. ) {{t('admin.storage.noConfigOption')}}
  91. template(
  92. v-for='(cfg, cfgKey, idx) in state.strategy.config'
  93. )
  94. template(
  95. v-if='configIfCheck(cfg.if)'
  96. )
  97. q-separator.q-my-sm(inset, v-if='idx > 0')
  98. q-item(v-if='cfg.type === `boolean`', tag='label')
  99. blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
  100. q-item-section
  101. q-item-label {{cfg.title}}
  102. q-item-label(caption) {{cfg.hint}}
  103. q-item-section(avatar)
  104. q-toggle(
  105. v-model='cfg.value'
  106. color='primary'
  107. checked-icon='las la-check'
  108. unchecked-icon='las la-times'
  109. :aria-label='t(`admin.general.allowComments`)'
  110. :disable='cfg.readOnly'
  111. )
  112. q-item(v-else)
  113. blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
  114. q-item-section
  115. q-item-label {{cfg.title}}
  116. q-item-label(caption) {{cfg.hint}}
  117. q-item-section(
  118. :style='cfg.type === `number` ? `flex: 0 0 150px;` : ``'
  119. :class='{ "col-auto": cfg.enum && cfg.enumDisplay === `buttons` }'
  120. )
  121. q-btn-toggle(
  122. v-if='cfg.enum && cfg.enumDisplay === `buttons`'
  123. v-model='cfg.value'
  124. push
  125. glossy
  126. no-caps
  127. toggle-color='primary'
  128. :options='cfg.enum'
  129. :disable='cfg.readOnly'
  130. )
  131. q-select(
  132. v-else-if='cfg.enum'
  133. outlined
  134. v-model='cfg.value'
  135. :options='cfg.enum'
  136. emit-value
  137. map-options
  138. dense
  139. options-dense
  140. :aria-label='cfg.title'
  141. :disable='cfg.readOnly'
  142. )
  143. q-input(
  144. v-else
  145. outlined
  146. v-model='cfg.value'
  147. dense
  148. :type='cfg.multiline ? `textarea` : `input`'
  149. :aria-label='cfg.title'
  150. :disable='cfg.readOnly'
  151. )
  152. .col-auto(v-if='state.selectedStrategy && state.strategy')
  153. //- -----------------------
  154. //- Infobox
  155. //- -----------------------
  156. q-card.rounded-borders.q-pb-md(style='width: 350px;')
  157. q-card-section
  158. .text-subtitle1 {{state.strategy.strategy.title}}
  159. q-img.q-mt-sm.rounded-borders(
  160. :src='state.strategy.strategy.logo'
  161. fit='contain'
  162. no-spinner
  163. style='height: 100px;'
  164. )
  165. .text-body2.q-mt-md {{state.strategy.strategy.description}}
  166. q-separator.q-mb-sm(inset)
  167. q-item
  168. q-item-section
  169. q-item-label.text-grey {{t(`admin.auth.vendor`)}}
  170. q-item-label {{state.strategy.strategy.vendor}}
  171. q-separator.q-my-sm(inset)
  172. q-item
  173. q-item-section
  174. q-item-label.text-grey {{t(`admin.auth.vendorWebsite`)}}
  175. q-item-label: a(:href='state.strategy.strategy.website', target='_blank', rel='noreferrer') {{state.strategy.strategy.website}}
  176. //- -----------------------
  177. //- Status
  178. //- -----------------------
  179. q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 350px;')
  180. q-card-section
  181. .text-subtitle1 {{t(`admin.auth.status`)}}
  182. q-item(tag='label')
  183. q-item-section
  184. q-item-label {{t(`admin.auth.enabled`)}}
  185. q-item-label(caption) {{t(`admin.auth.enabledHint`)}}
  186. q-item-label.text-deep-orange(v-if='state.strategy.strategy.key === `local`', caption) {{t(`admin.auth.enabledForced`)}}
  187. q-item-section(avatar)
  188. q-toggle(
  189. v-model='state.strategy.isEnabled'
  190. :disable='state.strategy.strategy.key === `local`'
  191. color='primary'
  192. checked-icon='las la-check'
  193. unchecked-icon='las la-times'
  194. :aria-label='t(`admin.auth.enabled`)'
  195. )
  196. q-separator.q-my-sm(inset)
  197. q-item
  198. q-item-section
  199. q-btn.acrylic-btn(
  200. icon='las la-trash-alt'
  201. flat
  202. color='negative'
  203. :disable='state.strategy.strategy.key === `local`'
  204. label='Delete Strategy'
  205. )
  206. //- v-flex(xs12, lg9)
  207. //- v-card.animated.fadeInUp.wait-p2s
  208. //- v-toolbar(color='primary', dense, flat, dark)
  209. //- .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
  210. //- v-spacer
  211. //- v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()')
  212. //- v-icon(left) mdi-close
  213. //- span {{$t('common.actions.delete')}}
  214. //- v-card-info(color='blue')
  215. //- div
  216. //- span {{strategy.strategy.description}}
  217. //- .caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
  218. //- v-spacer
  219. //- .admin-providerlogo
  220. //- img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
  221. //- v-card-text
  222. //- .row
  223. //- .col-8
  224. //- v-text-field(
  225. //- outlined
  226. //- :label='$t(`admin.auth.displayName`)'
  227. //- v-model='strategy.displayName'
  228. //- prepend-icon='mdi-format-title'
  229. //- :hint='$t(`admin.auth.displayNameHint`)'
  230. //- persistent-hint
  231. //- )
  232. //- .col-4
  233. //- v-switch.mt-1(
  234. //- :label='$t(`admin.auth.strategyIsEnabled`)'
  235. //- v-model='strategy.isEnabled'
  236. //- color='primary'
  237. //- prepend-icon='mdi-power'
  238. //- :hint='$t(`admin.auth.strategyIsEnabledHint`)'
  239. //- persistent-hint
  240. //- inset
  241. //- :disabled='strategy.key === `local`'
  242. //- )
  243. //- template(v-if='strategy.config && Object.keys(strategy.config).length > 0')
  244. //- v-divider
  245. //- .overline.my-5 {{$t('admin.auth.strategyConfiguration')}}
  246. //- .pr-3
  247. //- template(v-for='cfg in strategy.config')
  248. //- v-select.mb-3(
  249. //- v-if='cfg.value.type === "string" && cfg.value.enum'
  250. //- outlined
  251. //- :items='cfg.value.enum'
  252. //- :key='cfg.key'
  253. //- :label='cfg.value.title'
  254. //- v-model='cfg.value.value'
  255. //- prepend-icon='mdi-cog-box'
  256. //- :hint='cfg.value.hint ? cfg.value.hint : ""'
  257. //- persistent-hint
  258. //- :class='cfg.value.hint ? "mb-2" : ""'
  259. //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
  260. //- )
  261. //- v-switch.mb-6(
  262. //- v-else-if='cfg.value.type === "boolean"'
  263. //- :key='cfg.key'
  264. //- :label='cfg.value.title'
  265. //- v-model='cfg.value.value'
  266. //- color='primary'
  267. //- prepend-icon='mdi-cog-box'
  268. //- :hint='cfg.value.hint ? cfg.value.hint : ""'
  269. //- persistent-hint
  270. //- inset
  271. //- )
  272. //- v-textarea.mb-3(
  273. //- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
  274. //- outlined
  275. //- :key='cfg.key'
  276. //- :label='cfg.value.title'
  277. //- v-model='cfg.value.value'
  278. //- prepend-icon='mdi-cog-box'
  279. //- :hint='cfg.value.hint ? cfg.value.hint : ""'
  280. //- persistent-hint
  281. //- :class='cfg.value.hint ? "mb-2" : ""'
  282. //- )
  283. //- v-text-field.mb-3(
  284. //- v-else
  285. //- outlined
  286. //- :key='cfg.key'
  287. //- :label='cfg.value.title'
  288. //- v-model='cfg.value.value'
  289. //- prepend-icon='mdi-cog-box'
  290. //- :hint='cfg.value.hint ? cfg.value.hint : ""'
  291. //- persistent-hint
  292. //- :class='cfg.value.hint ? "mb-2" : ""'
  293. //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
  294. //- )
  295. //- v-divider
  296. //- .overline.my-5 {{$t('admin.auth.registration')}}
  297. //- .pr-3
  298. //- v-switch.ml-3(
  299. //- v-model='strategy.selfRegistration'
  300. //- :label='$t(`admin.auth.selfRegistration`)'
  301. //- color='primary'
  302. //- :hint='$t(`admin.auth.selfRegistrationHint`)'
  303. //- persistent-hint
  304. //- inset
  305. //- )
  306. //- v-combobox.ml-3.mt-5(
  307. //- :label='$t(`admin.auth.domainsWhitelist`)'
  308. //- v-model='strategy.domainWhitelist'
  309. //- prepend-icon='mdi-email-check-outline'
  310. //- outlined
  311. //- :disabled='!strategy.selfRegistration'
  312. //- :hint='$t(`admin.auth.domainsWhitelistHint`)'
  313. //- persistent-hint
  314. //- small-chips
  315. //- deletable-chips
  316. //- clearable
  317. //- multiple
  318. //- chips
  319. //- )
  320. //- v-autocomplete.mt-3.ml-3(
  321. //- outlined
  322. //- :disabled='!strategy.selfRegistration'
  323. //- :items='groups'
  324. //- item-text='name'
  325. //- item-value='id'
  326. //- :label='$t(`admin.auth.autoEnrollGroups`)'
  327. //- v-model='strategy.autoEnrollGroups'
  328. //- prepend-icon='mdi-account-group'
  329. //- :hint='$t(`admin.auth.autoEnrollGroupsHint`)'
  330. //- small-chips
  331. //- persistent-hint
  332. //- deletable-chips
  333. //- clearable
  334. //- multiple
  335. //- chips
  336. //- )
  337. //- v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
  338. //- v-toolbar(color='primary', dense, flat, dark)
  339. //- .subtitle-1 {{$t('admin.auth.configReference')}}
  340. //- v-card-text
  341. //- .body-2 {{$t('admin.auth.configReferenceSubtitle')}}
  342. //- v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert')
  343. //- i18next(path='admin.auth.siteUrlNotSetup', tag='span')
  344. //- strong(place='siteUrl') {{$t('admin.general.siteUrl')}}
  345. //- strong(place='general') {{$t('admin.general.title')}}
  346. //- .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`')
  347. //- .body-2: strong {{$t('admin.auth.allowedWebOrigins')}}
  348. //- .body-2 {{host}}
  349. //- v-divider.my-3
  350. //- .body-2: strong {{$t('admin.auth.callbackUrl')}}
  351. //- .body-2 {{host}}/login/{{strategy.key}}/callback
  352. //- v-divider.my-3
  353. //- .body-2: strong {{$t('admin.auth.loginUrl')}}
  354. //- .body-2 {{host}}/login
  355. //- v-divider.my-3
  356. //- .body-2: strong {{$t('admin.auth.logoutUrl')}}
  357. //- .body-2 {{host}}
  358. //- v-divider.my-3
  359. //- .body-2: strong {{$t('admin.auth.tokenEndpointAuthMethod')}}
  360. //- .body-2 HTTP-POST
  361. </template>
  362. <script setup>
  363. import gql from 'graphql-tag'
  364. import { find, reject, transform } from 'lodash-es'
  365. import { v4 as uuid } from 'uuid'
  366. import { useI18n } from 'vue-i18n'
  367. import { useMeta, useQuasar } from 'quasar'
  368. import { computed, onMounted, reactive, watch, nextTick } from 'vue'
  369. import { useAdminStore } from 'src/stores/admin'
  370. import { useSiteStore } from 'src/stores/site'
  371. // QUASAR
  372. const $q = useQuasar()
  373. // STORES
  374. const adminStore = useAdminStore()
  375. const siteStore = useSiteStore()
  376. // I18N
  377. const { t } = useI18n()
  378. // META
  379. useMeta({
  380. title: t('admin.auth.title')
  381. })
  382. // DATA
  383. const state = reactive({
  384. loading: 0,
  385. groups: [],
  386. strategies: [],
  387. activeStrategies: [],
  388. selectedStrategy: '',
  389. host: '',
  390. strategy: {
  391. strategy: {}
  392. }
  393. })
  394. // COMPUTED
  395. const availableStrategies = computed(() => {
  396. return state.strategies.filter(str => str.key !== 'local')
  397. })
  398. const combinedActiveStrategies = computed(() => {
  399. return state.activeStrategies.map(str => ({
  400. ...str,
  401. strategy: find(state.strategies, ['key', str.strategy?.key]) || {}
  402. }))
  403. })
  404. // WATCHERS
  405. watch(() => state.selectedStrategy, (newValue, oldValue) => {
  406. const str = find(combinedActiveStrategies.value, ['id', newValue]) || {}
  407. str.config = transform(str.strategy.props, (cfg, v, k) => {
  408. cfg[k] = {
  409. ...v,
  410. value: str.config[k]
  411. }
  412. }, {})
  413. state.strategy = str
  414. })
  415. watch(() => state.activeStrategies, (newValue, oldValue) => {
  416. state.selectedStrategy = newValue[0]?.id
  417. })
  418. // METHODS
  419. async function load () {
  420. state.loading++
  421. $q.loading.show()
  422. const resp = await APOLLO_CLIENT.query({
  423. query: gql`
  424. query adminFetchAuthStrategies {
  425. authStrategies {
  426. key
  427. props
  428. title
  429. description
  430. isAvailable
  431. useForm
  432. usernameType
  433. logo
  434. color
  435. vendor
  436. website
  437. icon
  438. }
  439. authActiveStrategies {
  440. id
  441. strategy {
  442. key
  443. }
  444. displayName
  445. isEnabled
  446. config
  447. selfRegistration
  448. domainWhitelist
  449. autoEnrollGroups
  450. }
  451. }
  452. `,
  453. fetchPolicy: 'network-only'
  454. })
  455. state.strategies = resp?.data?.authStrategies || []
  456. state.activeStrategies = resp?.data?.authActiveStrategies || []
  457. $q.loading.hide()
  458. state.loading--
  459. }
  460. function configIfCheck (ifs) {
  461. if (!ifs || ifs.length < 1) { return true }
  462. return ifs.every(s => state.strategy.config[s.key]?.value === s.eq)
  463. }
  464. function addStrategy (str) {
  465. const newStr = {
  466. id: uuid(),
  467. strategy: {
  468. key: str.key
  469. },
  470. config: transform(str.props, (cfg, v, k) => {
  471. cfg[k] = v.default
  472. }, {}),
  473. isEnabled: false,
  474. displayName: str.title,
  475. selfRegistration: false,
  476. domainWhitelist: [],
  477. autoEnrollGroups: []
  478. }
  479. state.activeStrategies = [...state.activeStrategies, newStr]
  480. nextTick(() => {
  481. state.selectedStrategy = newStr.id
  482. })
  483. }
  484. function deleteStrategy (id) {
  485. state.activeStrategies = reject(state.activeStrategies, ['id', id])
  486. }
  487. async function save () {
  488. state.loading++
  489. try {
  490. const resp = await APOLLO_CLIENT.mutate({
  491. mutation: gql`
  492. mutation($strategies: [AuthenticationStrategyInput]!) {
  493. authentication {
  494. updateStrategies(strategies: $strategies) {
  495. responseResult {
  496. succeeded
  497. errorCode
  498. slug
  499. message
  500. }
  501. }
  502. }
  503. }
  504. `,
  505. variables: {
  506. strategies: this.activeStrategies.map((str, idx) => ({
  507. key: str.key,
  508. strategyKey: str.strategy.key,
  509. displayName: str.displayName,
  510. order: idx,
  511. isEnabled: str.isEnabled,
  512. config: str.config.map(cfg => ({ ...cfg, value: JSON.stringify({ v: cfg.value.value }) })),
  513. selfRegistration: str.selfRegistration,
  514. domainWhitelist: str.domainWhitelist,
  515. autoEnrollGroups: str.autoEnrollGroups
  516. }))
  517. }
  518. })
  519. if (resp?.data?.authentication?.updateStrategies?.operation.succeeded) {
  520. $q.notify({
  521. type: 'positive',
  522. message: t('admin.auth.saveSuccess')
  523. })
  524. } else {
  525. throw new Error(resp?.data?.authentication?.updateStrategies?.operation?.message || t('common.error.unexpected'))
  526. }
  527. } catch (err) {
  528. $q.notify({
  529. type: 'negative',
  530. message: 'Failed to save site theme config',
  531. caption: err.message
  532. })
  533. }
  534. state.loading--
  535. }
  536. // apollo: {
  537. // strategies: {
  538. // query: gql`
  539. // query {
  540. // authentication {
  541. // strategies {
  542. // key
  543. // title
  544. // description
  545. // isAvailable
  546. // useForm
  547. // logo
  548. // website
  549. // props {
  550. // key
  551. // value
  552. // }
  553. // }
  554. // }
  555. // }
  556. // `,
  557. // skip: true,
  558. // fetchPolicy: 'network-only',
  559. // update: (data) => _.get(data, 'authentication.strategies', []).map(str => ({
  560. // ...str,
  561. // isDisabled: !str.isAvailable || str.key === 'local',
  562. // props: _.sortBy(str.props.map(cfg => ({
  563. // key: cfg.key,
  564. // ...JSON.parse(cfg.value)
  565. // })), [t => t.order])
  566. // })),
  567. // watchLoading (isLoading) {
  568. // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-strategies-refresh')
  569. // }
  570. // },
  571. // activeStrategies: {
  572. // query: gql`
  573. // query {
  574. // authentication {
  575. // activeStrategies {
  576. // key
  577. // strategy {
  578. // key
  579. // title
  580. // description
  581. // useForm
  582. // logo
  583. // website
  584. // }
  585. // config {
  586. // key
  587. // value
  588. // }
  589. // order
  590. // isEnabled
  591. // displayName
  592. // selfRegistration
  593. // domainWhitelist
  594. // autoEnrollGroups
  595. // }
  596. // }
  597. // }
  598. // `,
  599. // skip: true,
  600. // fetchPolicy: 'network-only',
  601. // update: (data) => _.sortBy(_.get(data, 'authentication.activeStrategies', []).map(str => ({
  602. // ...str,
  603. // config: _.sortBy(str.config.map(cfg => ({
  604. // ...cfg,
  605. // value: JSON.parse(cfg.value)
  606. // })), [t => t.value.order])
  607. // })), ['order']),
  608. // watchLoading (isLoading) {
  609. // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
  610. // }
  611. // },
  612. // groups: {
  613. // query: gql`{ test }`,
  614. // fetchPolicy: 'network-only',
  615. // update: (data) => data.groups.list,
  616. // watchLoading (isLoading) {
  617. // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-groups-refresh')
  618. // }
  619. // },
  620. // host: {
  621. // query: gql`{ test }`,
  622. // fetchPolicy: 'network-only',
  623. // update: (data) => _.cloneDeep(data.site.config.host),
  624. // watchLoading (isLoading) {
  625. // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-host-refresh')
  626. // }
  627. // }
  628. onMounted(() => {
  629. load()
  630. })
  631. </script>