setup.vue 7.76 KB
Newer Older
1
<template lang="pug">
2
  v-app.setup
Nick's avatar
Nick committed
3 4 5 6
    v-content
      v-container
        v-layout
          v-flex(xs12, lg6, offset-lg3)
7
            v-card.elevation-20.radius-7.animated.fadeInUp
8
              .text-center
9 10
                img.setup-logo.animated.fadeInUp.wait-p2s(src='/svg/logo-wikijs-full.svg', alt='Wiki.js Logo')
              v-alert(v-model='error', type='error', icon='mdi-alert', tile, dismissible) {{ errorMessage }}
11 12 13
              v-alert(v-if='!error', tile, color='blue lighten-5', :value='true')
                v-icon.mr-3(color='blue') mdi-package-variant
                span.blue--text You are about to install Wiki.js #[strong {{wikiVersion}}].
Nick's avatar
Nick committed
14
              v-card-text
15 16
                .overline.pl-3 Administrator Account
                v-container.pa-3.mt-3(grid-list-xl)
Nick's avatar
Nick committed
17 18 19
                  v-layout(row, wrap)
                    v-flex(xs12)
                      v-text-field(
20
                        outlined
Nick's avatar
Nick committed
21 22
                        v-model='conf.adminEmail',
                        label='Administrator Email',
23
                        hint='The email address of the administrator account.',
Nick's avatar
Nick committed
24
                        persistent-hint
25
                        required
Nick's avatar
Nick committed
26 27 28 29
                        ref='adminEmailInput'
                      )
                    v-flex(xs6)
                      v-text-field(
30
                        outlined
Nick's avatar
Nick committed
31 32 33 34
                        ref='adminPassword',
                        counter='255'
                        v-model='conf.adminPassword',
                        label='Password',
35
                        :append-icon="pwdMode ? 'mdi-eye-off' : 'mdi-eye'"
Nick's avatar
Nick committed
36 37 38 39 40 41 42
                        @click:append="() => (pwdMode = !pwdMode)"
                        :type="pwdMode ? 'password' : 'text'"
                        hint='At least 8 characters long.',
                        persistent-hint
                      )
                    v-flex(xs6)
                      v-text-field(
43
                        outlined
Nick's avatar
Nick committed
44 45 46 47
                        ref='adminPasswordConfirm',
                        counter='255'
                        v-model='conf.adminPasswordConfirm',
                        label='Confirm Password',
48
                        :append-icon="pwdConfirmMode ? 'mdi-eye-off' : 'mdi-eye'"
Nick's avatar
Nick committed
49 50 51 52 53 54
                        @click:append="() => (pwdConfirmMode = !pwdConfirmMode)"
                        :type="pwdConfirmMode ? 'password' : 'text'"
                        hint='Verify your password again.',
                        persistent-hint
                      )
                v-divider.mb-4
55 56 57 58 59 60 61 62 63 64 65 66 67 68
                .overline.pl-3.mb-5 Site URL
                v-text-field.mb-4.mx-3(
                  outlined
                  ref='adminSiteUrl',
                  v-model='conf.siteUrl',
                  label='Site URL',
                  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.',
                  persistent-hint
                  @keyup.enter='install'
                )
                v-divider.mb-4
                .overline.pl-3.mb-3 Telemetry
                v-switch.ml-3(
                  inset
69 70 71 72 73 74
                  color='primary',
                  v-model='conf.telemetry',
                  label='Allow Telemetry',
                  persistent-hint,
                  hint='Help Wiki.js developers improve this app with anonymized telemetry.'
                )
75
                a.pl-3(style='font-size: 12px; letter-spacing: initial;', href='https://docs.requarks.io/telemetry', target='_blank') Learn more
76
              v-divider.mt-2
Nick's avatar
Nick committed
77
              v-card-actions
78
                v-btn(color='primary', @click='install', :disabled='loading', x-large, depressed, block)
79
                  v-icon(left) mdi-check
Nick's avatar
Nick committed
80 81 82 83
                  span Install

    v-dialog(v-model='loading', width='450', persistent)
      v-card(color='primary', dark).radius-7
84
        v-card-text.text-center.py-5
Nick's avatar
Nick committed
85 86 87 88 89 90 91
          .py-3(style='width: 64px; display:inline-block;')
            breeding-rhombus-spinner(
              :animation-duration='2000'
              :size='64'
              color='#FFF'
              )
          template(v-if='!success')
92
            .subtitle-1.white--text Finalizing your installation...
Nick's avatar
Nick committed
93 94
            .caption Just a moment
          template(v-else)
95
            .subtitle-1.white--text Installation complete!
Nick's avatar
Nick committed
96
            .caption Redirecting...
97 98 99
</template>

<script>
100
import _ from 'lodash'
101
import validate from 'validate.js'
102
import { BreedingRhombusSpinner } from 'epic-spinners'
103 104

export default {
105
  components: {
106
    BreedingRhombusSpinner
107
  },
108 109 110 111 112 113 114 115 116
  props: {
    wikiVersion: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      loading: false,
Nick's avatar
Nick committed
117 118 119
      success: false,
      error: false,
      errorMessage: '',
120 121 122 123
      conf: {
        adminEmail: '',
        adminPassword: '',
        adminPasswordConfirm: '',
124
        siteUrl: 'https://wiki.yourdomain.com',
Nick's avatar
Nick committed
125
        telemetry: true
126 127 128
      },
      pwdMode: true,
      pwdConfirmMode: true
129 130
    }
  },
Nick's avatar
Nick committed
131 132 133 134 135
  mounted() {
    _.delay(() => {
      this.$refs.adminEmailInput.focus()
    }, 500)
  },
136
  methods: {
Nick's avatar
Nick committed
137
    async install () {
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
      this.error = false

      const validationResults = validate(this.conf, {
        adminEmail: {
          presence: {
            allowEmpty: false
          },
          email: true
        },
        adminPassword: {
          presence: {
            allowEmpty: false
          },
          length: {
            minimum: 6,
            maximum: 255
          }
        },
        adminPasswordConfirm: {
          equality: 'adminPassword'
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        },
        siteUrl: {
          presence: {
            allowEmpty: false
          },
          url: {
            schemes: ['http', 'https'],
            allowLocal: true,
            allowDataUrl: false
          },
          format: {
            pattern: '^(?!.*/$).*$',
            flags: 'i',
            message: 'must not have a trailing slash'
          }
173 174 175 176 177 178 179 180
        }
      }, {
        format: 'flat'
      })
      if (validationResults) {
        this.error = true
        this.errorMessage = validationResults[0]
        this.$forceUpdate()
Nick's avatar
Nick committed
181
        return
182 183 184
      }

      this.loading = true
Nick's avatar
Nick committed
185
      this.success = false
186
      this.$forceUpdate()
187

Nick's avatar
Nick committed
188 189
      _.delay(async () => {
        try {
190 191 192 193 194 195 196 197 198 199
          const resp = await fetch('/finalize', {
            method: 'POST',
            cache: 'no-cache',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify(this.conf)
          }).then(res => res.json())

          if (resp.ok === true) {
200
            _.delay(() => {
Nick's avatar
Nick committed
201 202 203 204 205
              this.success = true
              _.delay(() => {
                window.location.assign('/login')
              }, 3000)
            }, 10000)
206
          } else {
Nick's avatar
Nick committed
207
            this.error = true
208
            this.errorMessage = resp.error
Nick's avatar
Nick committed
209
            this.loading = false
210
          }
211
        } catch (err) {
212
          window.alert(err.message)
Nick's avatar
Nick committed
213
        }
214 215 216 217 218 219
      }, 1000)
    }
  }
}

</script>
220 221

<style lang='scss'>
Nick's avatar
Nick committed
222
.setup {
223
  .v-application--wrap {
Nick's avatar
Nick committed
224
    padding-top: 10vh;
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    background-color: #111;
    background-image: linear-gradient(45deg, mc('blue', '100'), mc('blue', '700'), mc('indigo', '900'));
    background-blend-mode: exclusion;

    &::before {
      content: '';
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100vh;
      z-index: 0;
      background-color: transparent;
      background-image: url(/svg/motif-grid.svg) !important;
      background-size: 100px;
      background-repeat: repeat;
      animation: bg-anim 100s linear infinite;
    }
  }

  @keyframes bg-anim {
    0% {
      background-position: 0 0;
    }
    100% {
      background-position: 100% 100%;
    }
Nick's avatar
Nick committed
252
  }
253

Nick's avatar
Nick committed
254
  &-logo {
255 256
    width: 400px;
    margin: 2rem 0 2rem 0;
Nick's avatar
Nick committed
257 258
  }
}
259
</style>