AdminSystem.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <template lang='pug'>
  2. q-page.admin-system
  3. .row.q-pa-md.items-center
  4. .col-auto
  5. img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-processor.svg')
  6. .col.q-pl-md
  7. .text-h5.text-primary.animated.fadeInLeft {{ t('admin.system.title') }}
  8. .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.system.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 + `/system/`'
  15. target='_blank'
  16. type='a'
  17. )
  18. q-btn.q-mr-sm.acrylic-btn(
  19. icon='las la-redo-alt'
  20. flat
  21. color='secondary'
  22. :loading='state.loading > 0'
  23. @click='load'
  24. )
  25. q-btn.acrylic-btn(
  26. ref='copySysInfoBtn'
  27. flat
  28. icon='fa-regular fa-clipboard'
  29. label='Copy System Info'
  30. color='primary'
  31. @click=''
  32. :disabled='state.loading > 0'
  33. )
  34. q-separator(inset)
  35. .row.q-pa-md.q-col-gutter-md
  36. .col-6
  37. //- -----------------------
  38. //- WIKI.JS
  39. //- -----------------------
  40. q-card.q-pb-sm.shadow-1
  41. q-card-section
  42. .text-subtitle1 Wiki.js
  43. q-item
  44. blueprint-icon(icon='breakable', :hue-rotate='-45')
  45. q-item-section
  46. q-item-label {{ t('admin.system.currentVersion') }}
  47. q-item-label(caption) {{t('admin.system.currentVersionHint')}}
  48. q-item-section
  49. q-item-label.dark-value(caption) {{ state.info.currentVersion }}
  50. q-separator(inset)
  51. q-item
  52. blueprint-icon(icon='cloud-checked', :hue-rotate='-45')
  53. q-item-section
  54. q-item-label {{ t('admin.system.latestVersion') }}
  55. q-item-label(caption) {{t('admin.system.latestVersionHint')}}
  56. q-item-section
  57. .row.q-col-gutter-sm
  58. .col
  59. .dark-value(caption) {{ state.info.latestVersion }}
  60. .col-auto
  61. q-btn.acrylic-btn(
  62. flat
  63. color='purple'
  64. @click='checkForUpdates'
  65. :label='t(`admin.system.checkUpdate`)'
  66. )
  67. //- -----------------------
  68. //- CLIENT
  69. //- -----------------------
  70. q-no-ssr
  71. q-card.q-mt-md.q-pb-sm.shadow-1
  72. q-card-section
  73. .text-subtitle1 {{t('admin.system.client')}}
  74. q-item
  75. blueprint-icon(icon='navigation-toolbar-top', :hue-rotate='-45')
  76. q-item-section
  77. q-item-label {{t('admin.system.browser')}}
  78. q-item-label(caption) {{t('admin.system.browserHint')}}
  79. q-item-section
  80. q-item-label.dark-value(caption) {{ clientBrowser }}
  81. q-separator(inset)
  82. q-item
  83. blueprint-icon(icon='computer', :hue-rotate='-45')
  84. q-item-section
  85. q-item-label {{t('admin.system.clientPlatform')}}
  86. q-item-label(caption) {{t('admin.system.clientPlatformHint')}}
  87. q-item-section
  88. q-item-label.dark-value(caption) {{ clientPlatform }}
  89. q-separator(inset)
  90. q-item
  91. blueprint-icon(icon='translation', :hue-rotate='-45')
  92. q-item-section
  93. q-item-label {{t('admin.system.clientLanguage')}}
  94. q-item-label(caption) {{t('admin.system.clientLanguageHint')}}
  95. q-item-section
  96. q-item-label.dark-value(caption) {{ clientLanguage }}
  97. q-separator(inset)
  98. q-item
  99. blueprint-icon(icon='cookies', :hue-rotate='-45')
  100. q-item-section
  101. q-item-label {{t('admin.system.clientCookies')}}
  102. q-item-label(caption) {{t('admin.system.clientCookiesHint')}}
  103. q-item-section
  104. q-item-label.dark-value(caption) {{ clientCookies }}
  105. q-separator(inset)
  106. q-item
  107. blueprint-icon(icon='widescreen', :hue-rotate='-45')
  108. q-item-section
  109. q-item-label {{t('admin.system.clientViewport')}}
  110. q-item-label(caption) {{t('admin.system.clientViewportHint')}}
  111. q-item-section
  112. q-item-label.dark-value(caption) {{ clientViewport }}
  113. .col-6
  114. //- -----------------------
  115. //- ENGINES
  116. //- -----------------------
  117. q-card.q-pb-sm.shadow-1
  118. q-card-section
  119. .text-subtitle1 {{t('admin.system.engines')}}
  120. q-item
  121. blueprint-icon(icon='nodejs', :hue-rotate='-45')
  122. q-item-section
  123. q-item-label Node.js
  124. q-item-label(caption) {{t('admin.system.nodejsHint')}}
  125. q-item-section
  126. q-item-label.dark-value(caption) {{ state.info.nodeVersion }}
  127. q-separator(inset)
  128. q-item
  129. blueprint-icon(icon='postgresql', :hue-rotate='-45')
  130. q-item-section
  131. q-item-label {{t('admin.system.database')}}
  132. q-item-label(caption) {{t('admin.system.databaseHint')}}
  133. q-item-section
  134. q-item-label.dark-value(caption) PostgreSQL {{dbVersion}}
  135. q-separator(inset)
  136. q-item
  137. blueprint-icon(icon='database', :hue-rotate='-45')
  138. q-item-section
  139. q-item-label {{t('admin.system.databaseHost')}}
  140. q-item-label(caption) {{t('admin.system.databaseHostHint')}}
  141. q-item-section
  142. q-item-label.dark-value(caption) {{ state.info.dbHost }}
  143. //- -----------------------
  144. //- HOST INFORMATION
  145. //- -----------------------
  146. q-card.q-mt-md.q-pb-sm.shadow-1
  147. q-card-section
  148. .text-subtitle1 {{ t('admin.system.hostInfo') }}
  149. q-item
  150. blueprint-icon(:icon='platformLogo', :hue-rotate='-45')
  151. q-item-section
  152. q-item-label {{ t('admin.system.os') }}
  153. q-item-label(caption) {{t('admin.system.osHint')}}
  154. q-item-section
  155. q-item-label.dark-value(caption) {{ (state.info.platform === 'docker') ? 'Docker Container (Linux)' : state.info.operatingSystem }}
  156. q-separator(inset)
  157. q-item
  158. blueprint-icon(icon='server', :hue-rotate='-45')
  159. q-item-section
  160. q-item-label {{ t('admin.system.hostname') }}
  161. q-item-label(caption) {{t('admin.system.hostnameHint')}}
  162. q-item-section
  163. q-item-label.dark-value(caption) {{ state.info.hostname }}
  164. q-separator(inset)
  165. q-item
  166. blueprint-icon(icon='processor', :hue-rotate='-45')
  167. q-item-section
  168. q-item-label {{ t('admin.system.cpuCores') }}
  169. q-item-label(caption) {{t('admin.system.cpuCoresHint')}}
  170. q-item-section
  171. q-item-label.dark-value(caption) {{ state.info.cpuCores }}
  172. q-separator(inset)
  173. q-item
  174. blueprint-icon(icon='memory-slot', :hue-rotate='-45')
  175. q-item-section
  176. q-item-label {{ t('admin.system.totalRAM') }}
  177. q-item-label(caption) {{t('admin.system.totalRAMHint')}}
  178. q-item-section
  179. q-item-label.dark-value(caption) {{ state.info.ramTotal }}
  180. q-separator(inset)
  181. q-item
  182. blueprint-icon(icon='program', :hue-rotate='-45')
  183. q-item-section
  184. q-item-label {{ t('admin.system.workingDirectory') }}
  185. q-item-label(caption) {{t('admin.system.workingDirectoryHint')}}
  186. q-item-section
  187. q-item-label.dark-value(caption) {{ state.info.workingDirectory }}
  188. q-separator(inset)
  189. q-item
  190. blueprint-icon(icon='automation', :hue-rotate='-45')
  191. q-item-section
  192. q-item-label {{ t('admin.system.configFile') }}
  193. q-item-label(caption) {{t('admin.system.configFileHint')}}
  194. q-item-section
  195. q-item-label.dark-value(caption) {{ state.info.configFile }}
  196. //- v-list-item-action-text {{ t('admin.system.published') }} {{ state.info.latestVersionReleaseDate | moment('from') }}
  197. //- v-card-actions(v-if='info.upgradeCapable && !isLatestVersion && info.platform === `docker`', :class='$vuetify.theme.dark ? `grey darken-3-d5` : `indigo lighten-5`')
  198. //- .caption.indigo--text.pl-3(:class='$vuetify.theme.dark ? `text--lighten-4` : ``') Wiki.js can perform the upgrade to the latest version for you.
  199. //- v-spacer
  200. //- v-btn.px-3(
  201. //- color='indigo'
  202. //- dark
  203. //- @click='performUpgrade'
  204. //- )
  205. //- v-icon(left) mdi-upload
  206. //- span Perform Upgrade
  207. //- v-dialog(
  208. //- v-model='isUpgrading'
  209. //- persistent
  210. //- width='450'
  211. //- )
  212. //- v-card.blue.darken-5(dark)
  213. //- v-card-text.text-center.pa-10
  214. //- self-building-square-spinner(
  215. //- :animation-duration='4000'
  216. //- :size='40'
  217. //- color='#FFF'
  218. //- style='margin: 0 auto;'
  219. //- )
  220. //- .body-2.mt-5.blue--text.text--lighten-4 Your Wiki.js container is being upgraded...
  221. //- .caption.blue--text.text--lighten-2 Please wait
  222. //- v-progress-linear.mt-5(
  223. //- color='blue lighten-2'
  224. //- :value='upgradeProgress'
  225. //- :buffer-value='upgradeProgress'
  226. //- rounded
  227. //- :stream='isUpgradingStarted'
  228. //- query
  229. //- :indeterminate='!isUpgradingStarted'
  230. //- )
  231. </template>
  232. <script setup>
  233. import { cloneDeep } from 'lodash-es'
  234. import gql from 'graphql-tag'
  235. import { useI18n } from 'vue-i18n'
  236. import { useMeta, useQuasar } from 'quasar'
  237. import { computed, onMounted, reactive, ref, watch } from 'vue'
  238. import ClipboardJS from 'clipboard'
  239. import { useSiteStore } from 'src/stores/site'
  240. import CheckUpdateDialog from '../components/CheckUpdateDialog.vue'
  241. // QUASAR
  242. const $q = useQuasar()
  243. // STORES
  244. const siteStore = useSiteStore()
  245. // I18N
  246. const { t } = useI18n()
  247. // META
  248. useMeta({
  249. title: t('admin.system.title')
  250. })
  251. // DATA
  252. const state = reactive({
  253. clip: null,
  254. loading: 0,
  255. isUpgrading: false,
  256. isUpgradingStarted: false,
  257. upgradeProgress: 0,
  258. info: {
  259. platform: ''
  260. }
  261. })
  262. // REFS
  263. const copySysInfoBtn = ref(null)
  264. // COMPUTED
  265. const dbVersion = computed(() => {
  266. return state.info?.dbVersion?.replace(/(?:\r\n|\r|\n)/g, ', ')
  267. })
  268. const platformLogo = computed(() => {
  269. switch (state.info.platform) {
  270. case 'docker':
  271. return 'docker-container'
  272. case 'darwin':
  273. return 'apple-logo'
  274. case 'linux':
  275. if (this.info.operatingSystem.indexOf('Ubuntu') >= 0) {
  276. return 'ubuntu'
  277. } else {
  278. return 'linux'
  279. }
  280. case 'win32':
  281. return 'windows8'
  282. default:
  283. return 'washing-machine'
  284. }
  285. })
  286. const isLatestVersion = computed(() => {
  287. return state.info.currentVersion === state.info.latestVersion
  288. })
  289. const clientBrowser = computed(() => {
  290. return !import.meta.env.SSR ? navigator.userAgent : ''
  291. })
  292. const clientPlatform = computed(() => {
  293. return !import.meta.env.SSR ? navigator.platform : ''
  294. })
  295. const clientLanguage = computed(() => {
  296. return !import.meta.env.SSR ? navigator.language : ''
  297. })
  298. const clientCookies = computed(() => {
  299. return !import.meta.env.SSR ? navigator.cookieEnabled : ''
  300. })
  301. const clientViewport = computed(() => {
  302. return !import.meta.env.SSR ? `${document.documentElement.clientWidth}x${document.documentElement.clientHeight}` : ''
  303. })
  304. // METHODS
  305. async function load () {
  306. state.loading++
  307. $q.loading.show()
  308. const resp = await APOLLO_CLIENT.query({
  309. query: gql`
  310. query getSystemInfo {
  311. systemInfo {
  312. configFile
  313. cpuCores
  314. currentVersion
  315. dbHost
  316. dbVersion
  317. hostname
  318. latestVersion
  319. latestVersionReleaseDate
  320. nodeVersion
  321. operatingSystem
  322. platform
  323. ramTotal
  324. upgradeCapable
  325. workingDirectory
  326. }
  327. }
  328. `,
  329. fetchPolicy: 'network-only'
  330. })
  331. state.info = cloneDeep(resp?.data?.systemInfo)
  332. $q.loading.hide()
  333. state.loading--
  334. }
  335. function checkForUpdates () {
  336. $q.dialog({
  337. component: CheckUpdateDialog
  338. })
  339. }
  340. // async function performUpgrade () {
  341. // state.isUpgrading = true
  342. // state.isUpgradingStarted = false
  343. // state.upgradeProgress = 0
  344. // this.$store.commit('loadingStart', 'admin-system-upgrade')
  345. // try {
  346. // const respRaw = await APOLLO_CLIENT.mutate({
  347. // mutation: gql`
  348. // mutation performUpdate {
  349. // system {
  350. // performUpgrade {
  351. // responseResult {
  352. // succeeded
  353. // errorCode
  354. // slug
  355. // message
  356. // }
  357. // }
  358. // }
  359. // }
  360. // `
  361. // })
  362. // const resp = _get(respRaw, 'data.system.performUpgrade.responseResult', {})
  363. // if (resp.succeeded) {
  364. // this.isUpgradingStarted = true
  365. // const progressInterval = setInterval(() => {
  366. // this.upgradeProgress += 0.83
  367. // }, 500)
  368. // setTimeout(() => {
  369. // clearInterval(progressInterval)
  370. // window.location.reload(true)
  371. // }, 60000)
  372. // } else {
  373. // throw new Error(resp.message)
  374. // }
  375. // } catch (err) {
  376. // this.$store.commit('pushGraphError', err)
  377. // this.$store.commit('loadingStop', 'admin-system-upgrade')
  378. // this.isUpgrading = false
  379. // }
  380. // }
  381. // MOUNTED
  382. onMounted(() => {
  383. load()
  384. const clip = new ClipboardJS(copySysInfoBtn.value.$el, {
  385. text: () => {
  386. return `Wiki.js ${state.info.currentVersion}
  387. Postgres ${dbVersion.value}
  388. Node.js ${state.info.nodeVersion}
  389. OS: ${state.info.operatingSystem}
  390. Platform: ${state.info.platform}
  391. CPU Cores: ${state.info.cpuCores}
  392. Total RAM: ${state.info.ramTotal}`
  393. }
  394. })
  395. clip.on('success', () => {
  396. $q.notify({
  397. type: 'positive',
  398. message: 'Info copied successfully',
  399. icon: 'las la-clipboard'
  400. })
  401. })
  402. clip.on('error', () => {
  403. $q.notify({
  404. type: 'negative',
  405. message: 'Failed to copy to system info'
  406. })
  407. })
  408. })
  409. </script>
  410. <style lang='scss'>
  411. .admin-system {
  412. .v-list-item-title, .v-list-item__subtitle {
  413. user-select: text;
  414. }
  415. .dark-value {
  416. background-color: #F8F8F8;
  417. color: #333;
  418. padding: 8px 12px;
  419. border-radius: 4px;
  420. font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace;
  421. }
  422. }
  423. </style>