From 0d4688da4d01c68989c4a78fe32cb658f573250a Mon Sep 17 00:00:00 2001 From: karkinge Date: Sun, 15 Mar 2026 13:40:41 +0100 Subject: [PATCH] Many stuphs --- package-lock.json => package-lock.old.json | 0 quasar.config.js | 3 +- src/App.vue | 33 ++++++- src/boot/helper.js | 106 +++++++++++++++++++++ src/layouts/DefaultLayout.vue | 1 + src/pages/Index.vue | 43 ++++++++- 6 files changed, 180 insertions(+), 6 deletions(-) rename package-lock.json => package-lock.old.json (100%) create mode 100644 src/boot/helper.js 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 @@