setup.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <template lang="pug">
  2. v-app.setup
  3. v-content
  4. v-container
  5. v-layout
  6. v-flex(xs12, lg6, offset-lg3)
  7. v-card.elevation-20.radius-7.animated.fadeInUp
  8. v-alert(v-if='isDevMode', tile, dark, color='red darken-3', icon='mdi-alert', prominent)
  9. .body-2 You are running an unstable, unreleased development version. This code base is #[strong NOT] for production use!
  10. .body-2.mt-3 Cloning the dev branch directly from GitHub is #[strong NOT] the proper way to install Wiki.js!
  11. .body-2 Read the #[a(href='https://docs.requarks.io/install', style='color: #FFF;') documentation] on correctly installing the latest stable version.
  12. .text-center
  13. img.setup-logo.animated.fadeInUp.wait-p2s(src='/_assets/svg/logo-wikijs-full.svg', alt='Wiki.js Logo')
  14. v-alert(v-model='error', type='error', icon='mdi-alert', tile, dismissible) {{ errorMessage }}
  15. v-alert(v-if='!error', tile, color='blue lighten-5', :value='true')
  16. v-icon.mr-3(color='blue') mdi-package-variant
  17. span.blue--text You are about to install Wiki.js #[strong {{wikiVersion}}].
  18. v-card-text
  19. .overline.pl-3 Administrator Account
  20. v-container.pa-3.mt-3(grid-list-xl)
  21. v-layout(row, wrap)
  22. v-flex(xs12)
  23. v-text-field(
  24. outlined
  25. v-model='conf.adminEmail',
  26. label='Administrator Email',
  27. hint='The email address of the administrator account.',
  28. persistent-hint
  29. required
  30. ref='adminEmailInput'
  31. )
  32. v-flex(xs6)
  33. v-text-field(
  34. outlined
  35. ref='adminPassword',
  36. counter='255'
  37. v-model='conf.adminPassword',
  38. label='Password',
  39. :append-icon="pwdMode ? 'mdi-eye-off' : 'mdi-eye'"
  40. @click:append="() => (pwdMode = !pwdMode)"
  41. :type="pwdMode ? 'password' : 'text'"
  42. hint='At least 8 characters long.',
  43. persistent-hint
  44. )
  45. v-flex(xs6)
  46. v-text-field(
  47. outlined
  48. ref='adminPasswordConfirm',
  49. counter='255'
  50. v-model='conf.adminPasswordConfirm',
  51. label='Confirm Password',
  52. :append-icon="pwdConfirmMode ? 'mdi-eye-off' : 'mdi-eye'"
  53. @click:append="() => (pwdConfirmMode = !pwdConfirmMode)"
  54. :type="pwdConfirmMode ? 'password' : 'text'"
  55. hint='Verify your password again.',
  56. persistent-hint
  57. )
  58. v-divider.mb-4
  59. .overline.pl-3.mb-5 Site URL
  60. v-text-field.mb-4.mx-3(
  61. outlined
  62. ref='adminSiteUrl',
  63. v-model='conf.siteUrl',
  64. label='Site URL',
  65. hint='Full URL to your wiki, without the trailing slash (e.g. https://wiki.example.com). This should be the public facing URL, not the internal one if using a reverse-proxy.',
  66. persistent-hint
  67. @keyup.enter='install'
  68. )
  69. v-divider.mb-4
  70. .overline.pl-3.mb-3 Telemetry
  71. v-switch.ml-3(
  72. inset
  73. color='primary',
  74. v-model='conf.telemetry',
  75. label='Allow Telemetry',
  76. persistent-hint,
  77. hint='Help Wiki.js developers improve this app with anonymized telemetry.'
  78. )
  79. a.pl-3(style='font-size: 12px; letter-spacing: initial;', href='https://docs.requarks.io/telemetry', target='_blank') Learn more
  80. v-divider.mt-2
  81. v-card-actions
  82. v-btn(color='primary', @click='install', :disabled='loading', x-large, depressed, block)
  83. v-icon(left) mdi-check
  84. span Install
  85. v-dialog(v-model='loading', width='450', persistent)
  86. v-card(color='primary', dark).radius-7
  87. v-card-text.text-center.py-5
  88. .py-3(style='width: 64px; display:inline-block;')
  89. breeding-rhombus-spinner(
  90. :animation-duration='2000'
  91. :size='64'
  92. color='#FFF'
  93. )
  94. template(v-if='!success')
  95. .subtitle-1.white--text Finalizing your installation...
  96. .caption Just a moment
  97. template(v-else)
  98. .subtitle-1.white--text Installation complete!
  99. .caption Redirecting...
  100. </template>
  101. <script>
  102. import _ from 'lodash'
  103. import validate from 'validate.js'
  104. import { BreedingRhombusSpinner } from 'epic-spinners'
  105. import confetti from 'canvas-confetti'
  106. /* global siteConfig */
  107. export default {
  108. components: {
  109. BreedingRhombusSpinner
  110. },
  111. props: {
  112. wikiVersion: {
  113. type: String,
  114. required: true
  115. }
  116. },
  117. data() {
  118. return {
  119. loading: false,
  120. success: false,
  121. error: false,
  122. errorMessage: '',
  123. conf: {
  124. adminEmail: '',
  125. adminPassword: '',
  126. adminPasswordConfirm: '',
  127. siteUrl: 'https://wiki.yourdomain.com',
  128. telemetry: true
  129. },
  130. pwdMode: true,
  131. pwdConfirmMode: true,
  132. isDevMode: false
  133. }
  134. },
  135. mounted() {
  136. _.delay(() => {
  137. this.$refs.adminEmailInput.focus()
  138. }, 500)
  139. this.isDevMode = siteConfig.devMode === true
  140. },
  141. methods: {
  142. async install () {
  143. this.error = false
  144. const validationResults = validate(this.conf, {
  145. adminEmail: {
  146. presence: {
  147. allowEmpty: false
  148. },
  149. email: true
  150. },
  151. adminPassword: {
  152. presence: {
  153. allowEmpty: false
  154. },
  155. length: {
  156. minimum: 6,
  157. maximum: 255
  158. }
  159. },
  160. adminPasswordConfirm: {
  161. equality: 'adminPassword'
  162. },
  163. siteUrl: {
  164. presence: {
  165. allowEmpty: false
  166. },
  167. url: {
  168. schemes: ['http', 'https'],
  169. allowLocal: true,
  170. allowDataUrl: false
  171. },
  172. format: {
  173. pattern: '^(?!.*/$).*$',
  174. flags: 'i',
  175. message: 'must not have a trailing slash'
  176. }
  177. }
  178. }, {
  179. format: 'flat'
  180. })
  181. if (validationResults) {
  182. this.error = true
  183. this.errorMessage = validationResults[0]
  184. this.$forceUpdate()
  185. return
  186. }
  187. this.loading = true
  188. this.success = false
  189. this.$forceUpdate()
  190. _.delay(async () => {
  191. try {
  192. const resp = await fetch('/finalize', {
  193. method: 'POST',
  194. cache: 'no-cache',
  195. headers: {
  196. 'Content-Type': 'application/json'
  197. },
  198. body: JSON.stringify(this.conf)
  199. }).then(res => res.json())
  200. if (resp.ok === true) {
  201. _.delay(() => {
  202. confetti({
  203. particleCount: 100,
  204. spread: 70,
  205. zIndex: 100000
  206. })
  207. this.success = true
  208. _.delay(() => {
  209. window.location.assign('/login')
  210. }, 3000)
  211. }, 10000)
  212. } else {
  213. this.error = true
  214. this.errorMessage = resp.error
  215. this.loading = false
  216. }
  217. } catch (err) {
  218. window.alert(err.message)
  219. }
  220. }, 1000)
  221. }
  222. }
  223. }
  224. </script>
  225. <style lang='scss'>
  226. .setup {
  227. .v-application--wrap {
  228. padding-top: 10vh;
  229. background-color: #111;
  230. background-image: linear-gradient(45deg, mc('blue', '100'), mc('blue', '700'), mc('indigo', '900'));
  231. background-blend-mode: exclusion;
  232. &::before {
  233. content: '';
  234. position: absolute;
  235. left: 0;
  236. top: 0;
  237. width: 100%;
  238. height: 100vh;
  239. z-index: 0;
  240. background-color: transparent;
  241. background-image: url(../static/svg/motif-grid.svg) !important;
  242. background-size: 100px;
  243. background-repeat: repeat;
  244. animation: bg-anim 100s linear infinite;
  245. }
  246. }
  247. @keyframes bg-anim {
  248. 0% {
  249. background-position: 0 0;
  250. }
  251. 100% {
  252. background-position: 100% 100%;
  253. }
  254. }
  255. &-logo {
  256. width: 400px;
  257. margin: 2rem 0 2rem 0;
  258. }
  259. }
  260. </style>