diff --git a/package-lock.json b/package-lock.old.json
similarity index 100%
rename from package-lock.json
rename to package-lock.old.json
diff --git a/quasar.config.js b/quasar.config.js
index 13fc0a8..ef6c19c 100644
--- a/quasar.config.js
+++ b/quasar.config.js
@@ -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
diff --git a/src/App.vue b/src/App.vue
index 38442ee..944cb44 100644
--- a/src/App.vue
+++ b/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
+ }
+ }
})
diff --git a/src/boot/helper.js b/src/boot/helper.js
new file mode 100644
index 0000000..a093a7d
--- /dev/null
+++ b/src/boot/helper.js
@@ -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 }
diff --git a/src/layouts/DefaultLayout.vue b/src/layouts/DefaultLayout.vue
index 63cd0fc..6d82397 100644
--- a/src/layouts/DefaultLayout.vue
+++ b/src/layouts/DefaultLayout.vue
@@ -191,6 +191,7 @@ export default {
})
.onOk(() => {
this.$q.localStorage.remove('username')
+ this.$q.localStorage.remove('credentials')
this.$router.push('/')
})
},
diff --git a/src/pages/Index.vue b/src/pages/Index.vue
index 35151da..3366deb 100644
--- a/src/pages/Index.vue
+++ b/src/pages/Index.vue
@@ -1,9 +1,13 @@
-
+
-

+
-
+
+
+
+
+
@@ -13,6 +17,37 @@