diff --git a/wp-content/themes/headless/nuxt.config.ts b/wp-content/themes/headless/nuxt.config.ts index 3087bd9..f3f79ab 100644 --- a/wp-content/themes/headless/nuxt.config.ts +++ b/wp-content/themes/headless/nuxt.config.ts @@ -2,7 +2,7 @@ import { version, description } from "./package.json"; // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ - modules: ["@lewebsimple/nuxt-graphql", "@nuxt/ui", "@nuxtjs/seo", "nuxt-svgo"], + modules: ["@lewebsimple/nuxt-graphql", "@nuxt/ui", "@nuxtjs/seo", "nuxt-auth-utils", "nuxt-svgo"], compatibilityDate: "2026-03-18", devtools: { enabled: true }, @@ -35,6 +35,7 @@ export default defineNuxtConfig({ }, }, server: { + context: ["server/graphql/context.ts"], schema: [{ type: "remote", endpoint: `${process.env.NUXT_WP_URL}/graphql` }], }, }, diff --git a/wp-content/themes/headless/package.json b/wp-content/themes/headless/package.json index f9c5502..0719b20 100644 --- a/wp-content/themes/headless/package.json +++ b/wp-content/themes/headless/package.json @@ -23,6 +23,7 @@ "@nuxt/ui": "^4.6.0", "@nuxtjs/seo": "^4.0.2", "nuxt": "^4.4.2", + "nuxt-auth-utils": "^0.5.29", "nuxt-svgo": "^4.2.6", "tailwindcss": "^4.2.2", "vue": "^3.5.31", diff --git a/wp-content/themes/headless/pnpm-lock.yaml b/wp-content/themes/headless/pnpm-lock.yaml index bee9dfc..1fdabac 100644 --- a/wp-content/themes/headless/pnpm-lock.yaml +++ b/wp-content/themes/headless/pnpm-lock.yaml @@ -23,18 +23,15 @@ importers: '@nuxtjs/seo': specifier: ^4.0.2 version: 4.0.2(489f84e1ce5b91b262b98d380824761d) - '@resvg/resvg-js': - specifier: ^2.6.2 - version: 2.6.2 nuxt: specifier: ^4.4.2 version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.31)(cac@6.7.14)(db0@0.3.4)(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(oxlint@1.57.0)(rollup-plugin-visualizer@7.0.1(rollup@4.60.0))(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.3) + nuxt-auth-utils: + specifier: ^0.5.29 + version: 0.5.29(magicast@0.5.2) nuxt-svgo: specifier: ^4.2.6 version: 4.2.6(magicast@0.5.2)(vue@3.5.31(typescript@5.9.3)) - satori: - specifier: ^0.26.0 - version: 0.26.0 tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -66,6 +63,18 @@ importers: packages: + '@adonisjs/hash@9.1.1': + resolution: {integrity: sha512-ZkRguwjAp4skKvKDdRAfdJ2oqQ0N7p9l3sioyXO1E8o0WcsyDgEpsTQtuVNoIdMiw4sn4gJlmL3nyF4BcK1ZDQ==} + engines: {node: '>=20.6.0'} + peerDependencies: + argon2: ^0.31.2 || ^0.41.0 || ^0.43.0 + bcrypt: ^5.1.1 || ^6.0.0 + peerDependenciesMeta: + argon2: + optional: true + bcrypt: + optional: true + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -1740,6 +1749,10 @@ packages: resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} + '@phc/format@1.0.0': + resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} + engines: {node: '>=10'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1756,6 +1769,17 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@poppinss/object-builder@1.1.0': + resolution: {integrity: sha512-FOrOq52l7u8goR5yncX14+k+Ewi5djnrt1JwXeS/FvnwAPOiveFhiczCDuvXdssAwamtrV2hp5Rw9v+n2T7hQg==} + engines: {node: '>=20.6.0'} + + '@poppinss/string@1.7.1': + resolution: {integrity: sha512-OrLzv/nGDU6l6dLXIQHe8nbNSWWfuSbpB/TW5nRpZFf49CLuQlIHlSPN9IdSUv2vG+59yGM6LoibsaHn8B8mDw==} + + '@poppinss/utils@6.10.1': + resolution: {integrity: sha512-da+MMyeXhBaKtxQiWPfy7+056wk3lVIhioJnXHXkJ2/OHDaZfFcyKHNl1R06sdYO8lIRXcXdoZ6LO2ARmkAREA==} + engines: {node: '>=18.16.0'} + '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -2474,6 +2498,9 @@ packages: '@types/node@25.5.0': resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + '@types/pluralize@0.0.33': + resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2946,6 +2973,10 @@ packages: capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + case-anything@3.1.2: + resolution: {integrity: sha512-wljhAjDDIv/hM2FzgJnYQg90AWmZMNtESCjTeLH680qTzdo0nErlCxOmgzgX4ZsZAtIvqHyD87ES8QyriXB+BQ==} + engines: {node: '>=18'} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3476,6 +3507,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + fontaine@0.8.0: resolution: {integrity: sha512-eek1GbzOdWIj9FyQH/emqW1aEdfC3lYRCHepzwlFCm5T77fBSRSyNRKE6/antF1/B1M+SfJXVRQTY9GAr7lnDg==} engines: {node: '>=18.12.0'} @@ -3826,6 +3861,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4215,6 +4253,23 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nuxt-auth-utils@0.5.29: + resolution: {integrity: sha512-aQ9oD8QR51jUCe2BFEsO/G/E0K+XUy8Skjn3hYcNLiqZ9XE4Y3uT/ozBCmAQ+TR3WW6prj8vUR0wQes8W1N0PA==} + peerDependencies: + '@atproto/api': ^0.13.15 + '@atproto/oauth-client-node': ^0.2.0 + '@simplewebauthn/browser': ^11.0.0 + '@simplewebauthn/server': ^11.0.0 + peerDependenciesMeta: + '@atproto/api': + optional: true + '@atproto/oauth-client-node': + optional: true + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nuxt-link-checker@4.3.9: resolution: {integrity: sha512-iYJU+A/xUhk62v4tol9cdjJS1+ZOSl0+tdUObgifdeSts6IqAUByUAiX4H6yOY2tdQYKjahMFbQr1GJ+/4LYnQ==} @@ -4336,6 +4391,9 @@ packages: engines: {node: '>=18'} hasBin: true + oauth4webapi@3.8.5: + resolution: {integrity: sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==} + obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -4374,6 +4432,9 @@ packages: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} + openid-client@6.8.2: + resolution: {integrity: sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==} + orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} @@ -4510,6 +4571,10 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + postcss-calc@10.1.1: resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -4879,6 +4944,10 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + satori@0.26.0: resolution: {integrity: sha512-tkMFrfIs3l2mQ2JEcyW0ADTy3zGggFRFzi6Ef8YozQSFsFKEqaSO1Y8F9wJg4//PJGQauMalHGTUEkPrFwhVPA==} engines: {node: '>=16'} @@ -4890,6 +4959,9 @@ packages: scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4972,6 +5044,10 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slugify@1.6.8: + resolution: {integrity: sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA==} + engines: {node: '>=8.0.0'} + smob@1.6.1: resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} engines: {node: '>=20.0.0'} @@ -5698,6 +5774,11 @@ packages: snapshots: + '@adonisjs/hash@9.1.1': + dependencies: + '@phc/format': 1.0.0 + '@poppinss/utils': 6.10.1 + '@alloc/quick-lru@5.2.0': {} '@antfu/install-pkg@1.1.0': @@ -7540,6 +7621,8 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.6 '@parcel/watcher-win32-x64': 2.5.6 + '@phc/format@1.0.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -7557,6 +7640,24 @@ snapshots: '@poppinss/exception@1.2.3': {} + '@poppinss/object-builder@1.1.0': {} + + '@poppinss/string@1.7.1': + dependencies: + '@types/pluralize': 0.0.33 + case-anything: 3.1.2 + pluralize: 8.0.0 + slugify: 1.6.8 + + '@poppinss/utils@6.10.1': + dependencies: + '@poppinss/exception': 1.2.3 + '@poppinss/object-builder': 1.1.0 + '@poppinss/string': 1.7.1 + flattie: 1.1.1 + safe-stable-stringify: 2.5.0 + secure-json-parse: 4.1.0 + '@remirror/core-constants@3.0.0': {} '@repeaterjs/repeater@3.0.6': {} @@ -7611,6 +7712,7 @@ snapshots: '@resvg/resvg-js-win32-arm64-msvc': 2.6.2 '@resvg/resvg-js-win32-ia32-msvc': 2.6.2 '@resvg/resvg-js-win32-x64-msvc': 2.6.2 + optional: true '@rolldown/pluginutils@1.0.0-rc.12': {} @@ -7798,6 +7900,7 @@ snapshots: dependencies: fflate: 0.7.4 string.prototype.codepointat: 0.2.1 + optional: true '@sindresorhus/is@7.2.0': {} @@ -8163,6 +8266,8 @@ snapshots: dependencies: undici-types: 7.18.2 + '@types/pluralize@0.0.33': {} + '@types/resolve@1.20.2': {} '@types/unist@3.0.3': {} @@ -8590,7 +8695,8 @@ snapshots: dependencies: bare-path: 3.0.0 - base64-js@0.0.8: {} + base64-js@0.0.8: + optional: true base64-js@1.5.1: {} @@ -8663,7 +8769,8 @@ snapshots: pascal-case: 3.1.2 tslib: 2.6.3 - camelize@1.0.1: {} + camelize@1.0.1: + optional: true caniuse-api@3.0.0: dependencies: @@ -8680,6 +8787,8 @@ snapshots: tslib: 2.6.3 upper-case-first: 2.0.2 + case-anything@3.1.2: {} + ccount@2.0.1: {} chalk@5.6.2: {} @@ -8852,17 +8961,21 @@ snapshots: dependencies: uncrypto: 0.1.3 - css-background-parser@0.1.0: {} + css-background-parser@0.1.0: + optional: true - css-box-shadow@1.0.0-3: {} + css-box-shadow@1.0.0-3: + optional: true - css-color-keywords@1.0.0: {} + css-color-keywords@1.0.0: + optional: true css-declaration-sorter@7.3.1(postcss@8.5.8): dependencies: postcss: 8.5.8 - css-gradient-parser@0.0.17: {} + css-gradient-parser@0.0.17: + optional: true css-select@5.2.2: dependencies: @@ -8877,6 +8990,7 @@ snapshots: camelize: 1.0.1 css-color-keywords: 1.0.0 postcss-value-parser: 4.2.0 + optional: true css-tree@2.2.1: dependencies: @@ -9068,7 +9182,8 @@ snapshots: embla-carousel@8.6.0: {} - emoji-regex-xs@2.0.1: {} + emoji-regex-xs@2.0.1: + optional: true emoji-regex@10.6.0: {} @@ -9194,7 +9309,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 - fflate@0.7.4: {} + fflate@0.7.4: + optional: true file-uri-to-path@1.0.0: {} @@ -9202,6 +9318,8 @@ snapshots: dependencies: to-regex-range: 5.0.1 + flattie@1.1.1: {} + fontaine@0.8.0: dependencies: '@capsizecss/unpack': 4.0.0 @@ -9412,7 +9530,8 @@ snapshots: capital-case: 1.0.4 tslib: 2.6.3 - hex-rgb@4.3.0: {} + hex-rgb@4.3.0: + optional: true hey-listen@1.0.8: {} @@ -9581,6 +9700,8 @@ snapshots: jiti@2.6.1: {} + jose@6.2.2: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -9670,6 +9791,7 @@ snapshots: dependencies: base64-js: 0.0.8 unicode-trie: 2.0.0 + optional: true linkify-it@5.0.0: dependencies: @@ -10036,6 +10158,24 @@ snapshots: dependencies: boolbase: 1.0.0 + nuxt-auth-utils@0.5.29(magicast@0.5.2): + dependencies: + '@adonisjs/hash': 9.1.1 + '@nuxt/kit': 4.4.2(magicast@0.5.2) + defu: 6.1.4 + h3: 1.15.10 + hookable: 6.1.0 + jose: 6.2.2 + ofetch: 1.5.1 + openid-client: 6.8.2 + pathe: 2.0.3 + scule: 1.3.0 + uncrypto: 0.1.3 + transitivePeerDependencies: + - argon2 + - bcrypt + - magicast + nuxt-link-checker@4.3.9(db0@0.3.4)(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3)): dependencies: '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3)) @@ -10509,6 +10649,8 @@ snapshots: pathe: 2.0.3 tinyexec: 1.0.4 + oauth4webapi@3.8.5: {} + obug@2.1.1: {} ofetch@1.5.1: @@ -10555,6 +10697,11 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 + openid-client@6.8.2: + dependencies: + jose: 6.2.2 + oauth4webapi: 3.8.5 + orderedmap@2.1.1: {} oxc-minify@0.117.0: @@ -10717,7 +10864,8 @@ snapshots: package-manager-detector@1.6.0: {} - pako@0.2.9: {} + pako@0.2.9: + optional: true param-case@3.0.4: dependencies: @@ -10728,6 +10876,7 @@ snapshots: dependencies: color-name: 1.1.4 hex-rgb: 4.3.0 + optional: true parse-filepath@1.0.2: dependencies: @@ -10799,6 +10948,8 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + pluralize@8.0.0: {} + postcss-calc@10.1.1(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -11220,6 +11371,8 @@ snapshots: safe-buffer@5.2.1: {} + safe-stable-stringify@2.5.0: {} + satori@0.26.0: dependencies: '@shuding/opentype.js': 1.4.0-beta.0 @@ -11233,11 +11386,14 @@ snapshots: parse-css-color: 0.2.1 postcss-value-parser: 4.2.0 yoga-layout: 3.2.1 + optional: true sax@1.6.0: {} scule@1.3.0: {} + secure-json-parse@4.1.0: {} + semver@6.3.1: {} semver@7.7.4: {} @@ -11334,6 +11490,8 @@ snapshots: slash@5.1.0: {} + slugify@1.6.8: {} + smob@1.6.1: {} snake-case@3.0.4: @@ -11395,7 +11553,8 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 - string.prototype.codepointat@0.2.1: {} + string.prototype.codepointat@0.2.1: + optional: true string_decoder@1.1.1: dependencies: @@ -11590,6 +11749,7 @@ snapshots: dependencies: pako: 0.2.9 tiny-inflate: 1.0.3 + optional: true unicorn-magic@0.3.0: {} @@ -12023,7 +12183,8 @@ snapshots: yocto-queue@0.1.0: {} - yoga-layout@3.2.1: {} + yoga-layout@3.2.1: + optional: true youch-core@0.3.3: dependencies: diff --git a/wp-content/themes/headless/server/api/login.gql b/wp-content/themes/headless/server/api/login.gql new file mode 100644 index 0000000..54ebe76 --- /dev/null +++ b/wp-content/themes/headless/server/api/login.gql @@ -0,0 +1,23 @@ +fragment AuthUser on User { + id + email @nonNull + roles @nonNull { + nodes { + name @nonNull + } + } +} + +fragment AuthPayload on LoginPayload { + authToken + refreshToken + user { + ...AuthUser + } +} + +mutation AuthLogin($username: String!, $password: String!) { + login(input: { provider: PASSWORD, credentials: { username: $username, password: $password } }) { + ...AuthPayload + } +} diff --git a/wp-content/themes/headless/server/api/login.post.ts b/wp-content/themes/headless/server/api/login.post.ts new file mode 100644 index 0000000..40c3b83 --- /dev/null +++ b/wp-content/themes/headless/server/api/login.post.ts @@ -0,0 +1,27 @@ +export default defineEventHandler(async (event) => { + try { + // Validate the request body against the schema + const variables = authLoginFormSchema.parse(await readBody(event)); + + // Execute the GraphQL operation to authenticate the user + const { data, error } = await executeSchemaOperation(event, { + operationName: "AuthLogin", + variables, + }); + + // Handle errors and validate the response data + if (error) throw error; + if (!data?.login) throw new Error("Identifiants invalides. Veuillez réessayer."); + + // Handle the login process by setting the session data + if (!(await handleLogin(event, data.login))) { + throw new Error("Une erreur est survenue lors de la connexion."); + } + + return { success: true, message: "Connexion réussie" }; + } catch (error) { + const message = + error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion."; + return { success: false, message }; + } +}); diff --git a/wp-content/themes/headless/server/api/logout.post.ts b/wp-content/themes/headless/server/api/logout.post.ts new file mode 100644 index 0000000..a76c2cd --- /dev/null +++ b/wp-content/themes/headless/server/api/logout.post.ts @@ -0,0 +1,13 @@ +import { defineEventHandler } from "h3"; + +export default defineEventHandler(async (event) => { + try { + await handleLogout(event); + + return { success: true, message: "Déconnexion réussie" }; + } catch (error) { + const message = + error instanceof Error ? error.message : "Une erreur est survenue lors de la déconnexion."; + return { success: false, message }; + } +}); diff --git a/wp-content/themes/headless/server/graphql/context.ts b/wp-content/themes/headless/server/graphql/context.ts new file mode 100644 index 0000000..e0a2a84 --- /dev/null +++ b/wp-content/themes/headless/server/graphql/context.ts @@ -0,0 +1,3 @@ +export default defineGraphQLContext(async (event) => { + return {}; +}); diff --git a/wp-content/themes/headless/server/utils/auth.ts b/wp-content/themes/headless/server/utils/auth.ts new file mode 100644 index 0000000..b71156a --- /dev/null +++ b/wp-content/themes/headless/server/utils/auth.ts @@ -0,0 +1,49 @@ +import type { AuthPayloadFragment, AuthUserFragment } from "#graphql/types"; +import type { H3Event } from "h3"; + +/** + * Handle user login by setting the session data with the provided authentication information. + * + * @param event The H3 event object. + * @param payload The authentication payload containing user and token information. + * @return A promise that resolves to true if the login was successful, or false if there was an error. + */ +export async function handleLogin( + event: H3Event, + { user, authToken, refreshToken }: AuthPayloadFragment, +) { + if (!user || !authToken || !refreshToken) { + return false; + } + + await setUserSession(event, { + user: getAuthUser(user), + secure: { authToken, refreshToken }, + loggedInAt: new Date().toISOString(), + }); + + return true; +} + +/** + * Handle user logout by clearing the session data. + * + * @param event The H3 event object. + * @returns A promise that resolves when the session has been cleared. + */ +export async function handleLogout(event: H3Event) { + await clearUserSession(event); +} + +/** + * Convert the AuthUserFragment to a User object expected by nuxt-auth-utils + * + * @param user The AuthUserFragment containing user data from the GraphQL response + * @returns A User object with the expected structure for nuxt-auth-utils, including an array of role names + */ +function getAuthUser(user: AuthUserFragment) { + return { + ...user, + roles: user.roles.nodes.map(({ name }) => name) || [], + }; +} diff --git a/wp-content/themes/headless/shared/types/auth.d.ts b/wp-content/themes/headless/shared/types/auth.d.ts new file mode 100644 index 0000000..78d260f --- /dev/null +++ b/wp-content/themes/headless/shared/types/auth.d.ts @@ -0,0 +1,18 @@ +declare module "#auth-utils" { + interface User { + id: string; + email: string; + roles: string[]; + } + + interface UserSession { + loggedInAt: string; + } + + interface SecureSessionData { + authToken: string; + refreshToken: string; + } +} + +export {}; diff --git a/wp-content/themes/headless/shared/utils/auth-login-form.ts b/wp-content/themes/headless/shared/utils/auth-login-form.ts new file mode 100644 index 0000000..fc895d8 --- /dev/null +++ b/wp-content/themes/headless/shared/utils/auth-login-form.ts @@ -0,0 +1,7 @@ +import * as z from "zod"; + +export const authLoginFormSchema = z.object({ + username: z.email("Courriel invalide"), + password: z.string("Veuillez saisir votre mot de passe"), +}); +export type AuthLoginForm = z.infer;