Browse Source

feat: site login bg upload

Nicolas Giard 2 years ago
parent
commit
8e87a5d489

+ 8 - 0
server/controllers/common.js

@@ -59,6 +59,14 @@ router.get('/_site/:siteId?/:resource', async (req, res, next) => {
       }
       break
     }
+    case 'loginbg': {
+      if (site.config.assets.loginBg) {
+        res.sendFile(path.join(siteAssetsPath, `loginbg-${site.id}.jpg`))
+      } else {
+        res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/bg/login.jpg'))
+      }
+      break
+    }
     default: {
       return res.status(404).send('Invalid Site Resource')
     }

+ 2 - 1
server/db/migrations/3.0.0.js

@@ -489,7 +489,8 @@ exports.up = async knex => {
         logo: false,
         logoExt: 'svg',
         favicon: false,
-        faviconExt: 'svg'
+        faviconExt: 'svg',
+        loginBg: false
       },
       theme: {
         dark: false,

+ 49 - 0
server/graph/resolvers/site.js

@@ -248,6 +248,55 @@ module.exports = {
         WIKI.logger.warn(err)
         return graphHelper.generateError(err)
       }
+    },
+    /**
+     * UPLOAD LOGIN BG
+     */
+    async uploadSiteLoginBg (obj, args) {
+      try {
+        const { filename, mimetype, createReadStream } = await args.image
+        WIKI.logger.info(`Processing site login bg ${filename} of type ${mimetype}...`)
+        if (!WIKI.extensions.ext.sharp.isInstalled) {
+          throw new Error('This feature requires the Sharp extension but it is not installed.')
+        }
+        if (!['.png', '.jpg', '.webp'].some(s => filename.endsWith(s))) {
+          throw new Error('Invalid File Extension. Must be png, jpg or webp.')
+        }
+        const destFolder = path.resolve(
+          process.cwd(),
+          WIKI.config.dataPath,
+          `assets`
+        )
+        const destPath = path.join(destFolder, `loginbg-${args.id}.jpg`)
+        await fs.ensureDir(destFolder)
+        // -> Resize
+        await WIKI.extensions.ext.sharp.resize({
+          format: 'jpg',
+          inputStream: createReadStream(),
+          outputPath: destPath,
+          width: 1920
+        })
+        // -> Save login bg meta to DB
+        const site = await WIKI.models.sites.query().findById(args.id)
+        if (!site.config.assets.loginBg) {
+          site.config.assets.loginBg = uuid()
+          await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
+          await WIKI.models.sites.reloadCache()
+        }
+        // -> Save image data to DB
+        const imgBuffer = await fs.readFile(destPath)
+        await WIKI.models.knex('assetData').insert({
+          id: site.config.assets.loginBg,
+          data: imgBuffer
+        }).onConflict('id').merge()
+        WIKI.logger.info('New site login bg processed successfully.')
+        return {
+          operation: graphHelper.generateSuccess('Site login bg uploaded successfully')
+        }
+      } catch (err) {
+        WIKI.logger.warn(err)
+        return graphHelper.generateError(err)
+      }
     }
   }
 }

+ 5 - 0
server/graph/schemas/site.graphql

@@ -36,6 +36,11 @@ extend type Mutation {
     image: Upload!
   ): DefaultResponse
 
+  uploadSiteLoginBg (
+    id: UUID!
+    image: Upload!
+  ): DefaultResponse
+
   deleteSite (
     id: UUID!
   ): DefaultResponse

+ 3 - 1
server/models/sites.js

@@ -87,7 +87,9 @@ module.exports = class Site extends Model {
         assets: {
           logo: false,
           logoExt: 'svg',
-          favicon: false
+          favicon: false,
+          faviconExt: 'svg',
+          loginBg: false
         },
         theme: {
           dark: false,

BIN
ux/public/_assets/bg/login-v3.jpg


BIN
ux/public/_assets/bg/login.jpg


BIN
ux/public/_assets/bg/test-1.jpg


BIN
ux/public/_assets/bg/test-2.jpg


+ 8 - 2
ux/src/components/HeaderNav.vue

@@ -13,11 +13,17 @@ q-header.bg-header.text-white.site-header(
         to='/'
         )
         q-avatar(
+          v-if='siteStore.logoText'
           size='34px'
           square
           )
-          img(src='/_assets/logo-wikijs.svg')
-      q-toolbar-title.text-h6 {{siteStore.title}}
+          img(src='/_site/logo')
+        img(
+          v-else
+          src='/_site/logo'
+          style='height: 34px'
+          )
+      q-toolbar-title.text-h6(v-if='siteStore.logoText') {{siteStore.title}}
     q-toolbar.gt-sm(
       style='height: 64px;'
       dark

+ 3 - 1
ux/src/i18n/locales/en.json

@@ -1496,5 +1496,7 @@
   "admin.terminal.connectError": "Connection Error:",
   "admin.utilities.disconnectWS": "Disconnect WebSocket Sessions",
   "admin.utilities.disconnectWSHint": "Force all active websocket connections to be closed.",
-  "admin.utilities.disconnectWSSuccess": "All active websocket connections have been terminated."
+  "admin.utilities.disconnectWSSuccess": "All active websocket connections have been terminated.",
+  "admin.login.bgUploadSuccess": "Login background image uploaded successfully.",
+  "admin.login.saveSuccess": "Login configuration saved successfully."
 }

+ 71 - 21
ux/src/pages/AdminLogin.vue

@@ -51,6 +51,7 @@ q-page.admin-login
               icon='las la-upload'
               color='primary'
               text-color='white'
+              @click='uploadBg'
             )
         q-separator.q-my-sm(inset)
         q-item(tag='label')
@@ -254,46 +255,95 @@ async function load () {
 }
 
 async function save () {
+  state.loading++
   try {
     await APOLLO_CLIENT.mutate({
       mutation: gql`
-        mutation saveLoginSettings (
-          $authAutoLogin: Boolean
-          $authEnforce2FA: Boolean
+        mutation saveLoginConfig (
+          $id: UUID!
+          $patch: SiteUpdateInput!
         ) {
-          site {
-            updateConfig(
-              authAutoLogin: $authAutoLogin,
-              authEnforce2FA: $authEnforce2FA
-            ) {
-              responseResult {
-                succeeded
-                errorCode
-                slug
-                message
-              }
+          updateSite (
+            id: $id
+            patch: $patch
+          ) {
+            operation {
+              succeeded
+              message
             }
           }
         }
       `,
       variables: {
-        authAutoLogin: state.config.authAutoLogin ?? false,
-        authEnforce2FA: state.config.authEnforce2FA ?? false
-      },
-      watchLoading (isLoading) {
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update')
+        id: adminStore.currentSiteId,
+        patch: {
+          authAutoLogin: state.config.authAutoLogin ?? false,
+          authEnforce2FA: state.config.authEnforce2FA ?? false
+        }
       }
     })
     $q.notify({
       type: 'positive',
-      message: 'Configuration saved successfully.'
+      message: t('admin.login.saveSuccess')
     })
   } catch (err) {
     $q.notify({
       type: 'negative',
-      message: err.message
+      message: 'Failed to save login configuration.',
+      caption: err.message
     })
   }
+  state.loading--
+}
+
+async function uploadBg () {
+  const input = document.createElement('input')
+  input.type = 'file'
+
+  input.onchange = async e => {
+    state.loading++
+    try {
+      const resp = await APOLLO_CLIENT.mutate({
+        mutation: gql`
+          mutation uploadLoginBg (
+            $id: UUID!
+            $image: Upload!
+          ) {
+            uploadSiteLoginBg (
+              id: $id
+              image: $image
+            ) {
+              operation {
+                succeeded
+                message
+              }
+            }
+          }
+        `,
+        variables: {
+          id: adminStore.currentSiteId,
+          image: e.target.files[0]
+        }
+      })
+      if (resp?.data?.uploadSiteLoginBg?.operation?.succeeded) {
+        $q.notify({
+          type: 'positive',
+          message: t('admin.login.bgUploadSuccess')
+        })
+      } else {
+        throw new Error(resp?.data?.uploadSiteLoginBg?.operation?.message || 'An unexpected error occured.')
+      }
+    } catch (err) {
+      $q.notify({
+        type: 'negative',
+        message: 'Failed to upload login background image.',
+        caption: err.message
+      })
+    }
+    state.loading--
+  }
+
+  input.click()
 }
 
 // MOUNTED

+ 1 - 1
ux/src/pages/Login.vue

@@ -7,7 +7,7 @@
     p.text-grey-7 Login to continue
     auth-login-panel
   .auth-bg(aria-hidden="true")
-    img(src='https://docs.requarks.io/_assets/img/splash/1.jpg' alt='')
+    img(src='/_site/loginbg' alt='')
 </template>
 
 <script setup>