Many stuphs
This commit is contained in:
@ -19,7 +19,8 @@ module.exports = configure(function (/* ctx */) {
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||
boot: [
|
||||
'pocketbase'
|
||||
'pocketbase',
|
||||
'helper'
|
||||
],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||
|
||||
33
src/App.vue
33
src/App.vue
@ -6,6 +6,37 @@
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App'
|
||||
name: 'App',
|
||||
async created () {
|
||||
if (await this.loggedIn()) {
|
||||
this.$router.push('/dashboard')
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
|
||||
this.$router.beforeEach(async (to, from, next) => {
|
||||
if (await this.loggedIn()) {
|
||||
if (to.path === '/') {
|
||||
this.$router.push('/dashboard')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
console.log('logged out')
|
||||
if (to.path !== '/') {
|
||||
this.$router.push('/')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async loggedIn () {
|
||||
let creds = await this.$helpers.getStoredCredentials()
|
||||
let logged = await this.$helpers.verifyCredentials(creds)
|
||||
return logged
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
106
src/boot/helper.js
Normal file
106
src/boot/helper.js
Normal file
@ -0,0 +1,106 @@
|
||||
import { boot } from 'quasar/wrappers'
|
||||
import { Notify, Dialog, LocalStorage } from 'quasar'
|
||||
|
||||
// ...existing code...
|
||||
const helpers = {
|
||||
sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
},
|
||||
|
||||
formatDate (date, options = { year: 'numeric', month: 'short', day: 'numeric' }, locale = 'fr-FR') {
|
||||
const d = (date instanceof Date) ? date : new Date(date)
|
||||
return new Intl.DateTimeFormat(locale, options).format(d)
|
||||
},
|
||||
|
||||
formatCurrency (amount, locale = 'fr-FR', currency = 'EUR') {
|
||||
return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
|
||||
},
|
||||
|
||||
uuidv4 () {
|
||||
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
||||
const rnds = new Uint8Array(16)
|
||||
crypto.getRandomValues(rnds)
|
||||
rnds[6] = (rnds[6] & 0x0f) | 0x40
|
||||
rnds[8] = (rnds[8] & 0x3f) | 0x80
|
||||
const toHex = (n) => n.toString(16).padStart(2, '0')
|
||||
return [...rnds].map(toHex).join('').replace(/^(.{8})(.{4})(.{4})(.{4})(.+)$/, '$1-$2-$3-$4-$5')
|
||||
}
|
||||
|
||||
// fallback
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
||||
const r = Math.random() * 16 | 0
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
},
|
||||
|
||||
async hashSHA256 (text) {
|
||||
if (!text) return ''
|
||||
const enc = new TextEncoder()
|
||||
const data = enc.encode(text)
|
||||
const subtle = (typeof crypto !== 'undefined' && crypto.subtle) ? crypto.subtle : (window.crypto ? window.crypto.subtle : null)
|
||||
if (!subtle) throw new Error('Web Crypto API not available')
|
||||
const hash = await subtle.digest('SHA-256', data)
|
||||
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
},
|
||||
|
||||
copyToClipboard (text) {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
return navigator.clipboard.writeText(text)
|
||||
}
|
||||
try {
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = text
|
||||
ta.setAttribute('readonly', '')
|
||||
ta.style.position = 'absolute'
|
||||
ta.style.left = '-9999px'
|
||||
document.body.appendChild(ta)
|
||||
ta.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(ta)
|
||||
return Promise.resolve()
|
||||
} catch (err) {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
},
|
||||
|
||||
notify (opts) {
|
||||
if (typeof opts === 'string') opts = { message: opts }
|
||||
return Notify.create(opts)
|
||||
},
|
||||
|
||||
async confirm (title = 'Confirmer', message = '', opts = {}) {
|
||||
try {
|
||||
await Dialog.create(Object.assign({ title, message, cancel: true, persistent: true }, opts))
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
getStoredCredentials () {
|
||||
// Le projet utilise LocalStorage.getKey('credentials') dans Index.vue
|
||||
// if (LocalStorage && typeof LocalStorage.getKey === 'function') return LocalStorage.getKey('credentials')
|
||||
if (LocalStorage && typeof LocalStorage.getItem === 'function') return LocalStorage.getItem('credentials')
|
||||
return null
|
||||
},
|
||||
|
||||
setStoredCredentials (creds) {
|
||||
// if (LocalStorage && typeof LocalStorage.setKey === 'function') return LocalStorage.setKey('credentials', creds)
|
||||
if (LocalStorage && typeof LocalStorage.setItem === 'function') return LocalStorage.setItem('credentials', creds)
|
||||
return null
|
||||
},
|
||||
|
||||
async verifyCredentials (creds) {
|
||||
const credsOk = await this.hashSHA256(creds) === '48b0723e62bf43bfad1cfd12406584d8161fa7466aa376465ed5e0ccb9811bde'
|
||||
if (credsOk) this.setStoredCredentials(creds)
|
||||
return credsOk
|
||||
}
|
||||
}
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// expose as this.$helpers in Options API
|
||||
app.config.globalProperties.$helpers = helpers
|
||||
})
|
||||
|
||||
export { helpers }
|
||||
@ -191,6 +191,7 @@ export default {
|
||||
})
|
||||
.onOk(() => {
|
||||
this.$q.localStorage.remove('username')
|
||||
this.$q.localStorage.remove('credentials')
|
||||
this.$router.push('/')
|
||||
})
|
||||
},
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<q-page class="flex flex-center flex-">
|
||||
<q-page class="flex flex-center">
|
||||
<div class="text-center">
|
||||
<img alt="Quasar logo" src="~assets/gyoza.jpg">
|
||||
<img alt="Quasar logo" src="~assets/gyoza.jpg" style="max-width:80vw">
|
||||
<br/>
|
||||
<q-btn label="Go" color="primary" to="/dashboard"></q-btn>
|
||||
<q-input label="Username" v-model="username" class="q-mt-md" autofocus @keydown.enter="login"></q-input>
|
||||
<br/>
|
||||
<q-input label="Password" type="password" v-model="password" @keydown.enter="login"></q-input>
|
||||
<br/>
|
||||
<q-btn label="Login" color="primary" @click="login" ></q-btn>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
@ -13,6 +17,37 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PageIndex'
|
||||
name: 'PageIndex',
|
||||
data () {
|
||||
return {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!window.crypto || !window.crypto.subtle) {
|
||||
throw new Error('Web Crypto API is not supported in this browser. Use Chrome 79+.')
|
||||
}
|
||||
// use the helper boot file which exposes $helpers
|
||||
let creds = this.$helpers.getStoredCredentials()
|
||||
console.log('credits', creds)
|
||||
if (creds) {
|
||||
this.$router.push('/dashboard')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async login () {
|
||||
let logged = await this.$helpers.verifyCredentials(this.username + ':' + this.password)
|
||||
console.log('logged', logged)
|
||||
if (logged) {
|
||||
this.$router.push('/dashboard')
|
||||
} else {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Invalid username or password'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user