diff --git a/auto-imports.d.ts b/auto-imports.d.ts new file mode 100644 index 0000000..101b7d6 --- /dev/null +++ b/auto-imports.d.ts @@ -0,0 +1,90 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const EffectScope: typeof import('vue').EffectScope + const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate + const computed: typeof import('vue').computed + const createApp: typeof import('vue').createApp + const createPinia: typeof import('pinia').createPinia + const customRef: typeof import('vue').customRef + const defineAsyncComponent: typeof import('vue').defineAsyncComponent + const defineComponent: typeof import('vue').defineComponent + const defineStore: typeof import('pinia').defineStore + const effectScope: typeof import('vue').effectScope + const getActivePinia: typeof import('pinia').getActivePinia + const getCurrentInstance: typeof import('vue').getCurrentInstance + const getCurrentScope: typeof import('vue').getCurrentScope + const getCurrentWatcher: typeof import('vue').getCurrentWatcher + const h: typeof import('vue').h + const inject: typeof import('vue').inject + const isProxy: typeof import('vue').isProxy + const isReactive: typeof import('vue').isReactive + const isReadonly: typeof import('vue').isReadonly + const isRef: typeof import('vue').isRef + const isShallow: typeof import('vue').isShallow + const mapActions: typeof import('pinia').mapActions + const mapGetters: typeof import('pinia').mapGetters + const mapState: typeof import('pinia').mapState + const mapStores: typeof import('pinia').mapStores + const mapWritableState: typeof import('pinia').mapWritableState + const markRaw: typeof import('vue').markRaw + const nextTick: typeof import('vue').nextTick + const onActivated: typeof import('vue').onActivated + const onBeforeMount: typeof import('vue').onBeforeMount + const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave + const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate + const onBeforeUnmount: typeof import('vue').onBeforeUnmount + const onBeforeUpdate: typeof import('vue').onBeforeUpdate + const onDeactivated: typeof import('vue').onDeactivated + const onErrorCaptured: typeof import('vue').onErrorCaptured + const onMounted: typeof import('vue').onMounted + const onRenderTracked: typeof import('vue').onRenderTracked + const onRenderTriggered: typeof import('vue').onRenderTriggered + const onScopeDispose: typeof import('vue').onScopeDispose + const onServerPrefetch: typeof import('vue').onServerPrefetch + const onUnmounted: typeof import('vue').onUnmounted + const onUpdated: typeof import('vue').onUpdated + const onWatcherCleanup: typeof import('vue').onWatcherCleanup + const provide: typeof import('vue').provide + const reactive: typeof import('vue').reactive + const readonly: typeof import('vue').readonly + const ref: typeof import('vue').ref + const resolveComponent: typeof import('vue').resolveComponent + const setActivePinia: typeof import('pinia').setActivePinia + const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix + const shallowReactive: typeof import('vue').shallowReactive + const shallowReadonly: typeof import('vue').shallowReadonly + const shallowRef: typeof import('vue').shallowRef + const storeToRefs: typeof import('pinia').storeToRefs + const toRaw: typeof import('vue').toRaw + const toRef: typeof import('vue').toRef + const toRefs: typeof import('vue').toRefs + const toValue: typeof import('vue').toValue + const triggerRef: typeof import('vue').triggerRef + const unref: typeof import('vue').unref + const useAttrs: typeof import('vue').useAttrs + const useCssModule: typeof import('vue').useCssModule + const useCssVars: typeof import('vue').useCssVars + const useId: typeof import('vue').useId + const useLink: typeof import('vue-router').useLink + const useModel: typeof import('vue').useModel + const useRoute: typeof import('vue-router').useRoute + const useRouter: typeof import('vue-router').useRouter + const useSlots: typeof import('vue').useSlots + const useTemplateRef: typeof import('vue').useTemplateRef + const watch: typeof import('vue').watch + const watchEffect: typeof import('vue').watchEffect + const watchPostEffect: typeof import('vue').watchPostEffect + const watchSyncEffect: typeof import('vue').watchSyncEffect +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + import('vue') +} diff --git a/bun.lock b/bun.lock index 1193c20..a4df97c 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "@vitejs/plugin-vue": "^6.0.3", "@vitejs/plugin-vue-jsx": "^5.1.3", "unocss": "^66.5.12", + "unplugin-auto-import": "^20.3.0", "unplugin-vue-components": "^30.0.0", "vite": "^7.3.0", "vite-ssr-components": "^0.5.2", @@ -654,7 +655,9 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], @@ -752,6 +755,8 @@ "rollup": ["rollup@4.54.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.54.0", "@rollup/rollup-android-arm64": "4.54.0", "@rollup/rollup-darwin-arm64": "4.54.0", "@rollup/rollup-darwin-x64": "4.54.0", "@rollup/rollup-freebsd-arm64": "4.54.0", "@rollup/rollup-freebsd-x64": "4.54.0", "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", "@rollup/rollup-linux-arm-musleabihf": "4.54.0", "@rollup/rollup-linux-arm64-gnu": "4.54.0", "@rollup/rollup-linux-arm64-musl": "4.54.0", "@rollup/rollup-linux-loong64-gnu": "4.54.0", "@rollup/rollup-linux-ppc64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-musl": "4.54.0", "@rollup/rollup-linux-s390x-gnu": "4.54.0", "@rollup/rollup-linux-x64-gnu": "4.54.0", "@rollup/rollup-linux-x64-musl": "4.54.0", "@rollup/rollup-openharmony-arm64": "4.54.0", "@rollup/rollup-win32-arm64-msvc": "4.54.0", "@rollup/rollup-win32-ia32-msvc": "4.54.0", "@rollup/rollup-win32-x64-gnu": "4.54.0", "@rollup/rollup-win32-x64-msvc": "4.54.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw=="], + "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], @@ -766,6 +771,8 @@ "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], + "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], + "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="], @@ -796,10 +803,14 @@ "unhead": ["unhead@2.1.1", "", { "dependencies": { "hookable": "^5.5.3" } }, "sha512-NOt8n2KybAOxSLfNXegAVai4SGU8bPKqWnqCzNAvnRH2i8mW+0bbFjN/L75LBgCSTiOjJSpANe5w2V34Grr7Cw=="], + "unimport": ["unimport@5.6.0", "", { "dependencies": { "acorn": "^8.15.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "strip-literal": "^3.1.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" } }, "sha512-8rqAmtJV8o60x46kBAJKtHpJDJWkA2xcBqWKPI14MgUb05o1pnpnCnXSxedUXyeq7p8fR5g3pTo2BaswZ9lD9A=="], + "unocss": ["unocss@66.5.12", "", { "dependencies": { "@unocss/astro": "66.5.12", "@unocss/cli": "66.5.12", "@unocss/core": "66.5.12", "@unocss/postcss": "66.5.12", "@unocss/preset-attributify": "66.5.12", "@unocss/preset-icons": "66.5.12", "@unocss/preset-mini": "66.5.12", "@unocss/preset-tagify": "66.5.12", "@unocss/preset-typography": "66.5.12", "@unocss/preset-uno": "66.5.12", "@unocss/preset-web-fonts": "66.5.12", "@unocss/preset-wind": "66.5.12", "@unocss/preset-wind3": "66.5.12", "@unocss/preset-wind4": "66.5.12", "@unocss/transformer-attributify-jsx": "66.5.12", "@unocss/transformer-compile-class": "66.5.12", "@unocss/transformer-directives": "66.5.12", "@unocss/transformer-variant-group": "66.5.12", "@unocss/vite": "66.5.12" }, "peerDependencies": { "@unocss/webpack": "66.5.12", "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["@unocss/webpack", "vite"] }, "sha512-3WdSuM+SOjVpXDtffTuSvYTMuufpFzBehu2b4Tr7DcoIUxGouZn3mdxCLx3PiEuK0ih40Fo7Sjm+J4mccHfwLg=="], "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], + "unplugin-auto-import": ["unplugin-auto-import@20.3.0", "", { "dependencies": { "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "picomatch": "^4.0.3", "unimport": "^5.5.0", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@nuxt/kit": "^4.0.0", "@vueuse/core": "*" }, "optionalPeers": ["@nuxt/kit", "@vueuse/core"] }, "sha512-RcSEQiVv7g0mLMMXibYVKk8mpteKxvyffGuDKqZZiFr7Oq3PB1HwgHdK5O7H4AzbhzHoVKG0NnMnsk/1HIVYzQ=="], + "unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="], "unplugin-vue-components": ["unplugin-vue-components@30.0.0", "", { "dependencies": { "chokidar": "^4.0.3", "debug": "^4.4.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.19", "mlly": "^1.8.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.10", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@babel/parser": "^7.15.8", "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "2 || 3" }, "optionalPeers": ["@babel/parser", "@nuxt/kit"] }, "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw=="], @@ -856,6 +867,10 @@ "@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5", "", {}, "sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ=="], + "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], @@ -866,10 +881,14 @@ "sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "unconfig/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], "unconfig-core/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], + "unimport/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + "unplugin/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="], diff --git a/components.d.ts b/components.d.ts new file mode 100644 index 0000000..94cb260 --- /dev/null +++ b/components.d.ts @@ -0,0 +1,58 @@ +/* eslint-disable */ +// @ts-nocheck +// biome-ignore lint: disable +// oxlint-disable +// ------ +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +import { GlobalComponents } from 'vue' + +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + Add: typeof import('./src/components/icons/Add.vue')['default'] + AddFilled: typeof import('./src/components/icons/AddFilled.vue')['default'] + Bell: typeof import('./src/components/icons/Bell.vue')['default'] + BellFilled: typeof import('./src/components/icons/BellFilled.vue')['default'] + Button: typeof import('primevue/button')['default'] + Checkbox: typeof import('primevue/checkbox')['default'] + DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default'] + Home: typeof import('./src/components/icons/Home.vue')['default'] + HomeFilled: typeof import('./src/components/icons/HomeFilled.vue')['default'] + InputText: typeof import('primevue/inputtext')['default'] + Layout: typeof import('./src/components/icons/Layout.vue')['default'] + LayoutFilled: typeof import('./src/components/icons/LayoutFilled.vue')['default'] + Message: typeof import('primevue/message')['default'] + Password: typeof import('primevue/password')['default'] + RootLayout: typeof import('./src/components/RootLayout.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default'] + VueHead: typeof import('./src/components/VueHead.tsx')['default'] + } +} + +// For TSX support +declare global { + const Add: typeof import('./src/components/icons/Add.vue')['default'] + const AddFilled: typeof import('./src/components/icons/AddFilled.vue')['default'] + const Bell: typeof import('./src/components/icons/Bell.vue')['default'] + const BellFilled: typeof import('./src/components/icons/BellFilled.vue')['default'] + const Button: typeof import('primevue/button')['default'] + const Checkbox: typeof import('primevue/checkbox')['default'] + const DashboardLayout: typeof import('./src/components/DashboardLayout.vue')['default'] + const Home: typeof import('./src/components/icons/Home.vue')['default'] + const HomeFilled: typeof import('./src/components/icons/HomeFilled.vue')['default'] + const InputText: typeof import('primevue/inputtext')['default'] + const Layout: typeof import('./src/components/icons/Layout.vue')['default'] + const LayoutFilled: typeof import('./src/components/icons/LayoutFilled.vue')['default'] + const Message: typeof import('primevue/message')['default'] + const Password: typeof import('primevue/password')['default'] + const RootLayout: typeof import('./src/components/RootLayout.vue')['default'] + const RouterLink: typeof import('vue-router')['RouterLink'] + const RouterView: typeof import('vue-router')['RouterView'] + const TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default'] + const VueHead: typeof import('./src/components/VueHead.tsx')['default'] +} \ No newline at end of file diff --git a/package.json b/package.json index bdd16b4..f6177e4 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@vitejs/plugin-vue": "^6.0.3", "@vitejs/plugin-vue-jsx": "^5.1.3", "unocss": "^66.5.12", + "unplugin-auto-import": "^20.3.0", "unplugin-vue-components": "^30.0.0", "vite": "^7.3.0", "vite-ssr-components": "^0.5.2", diff --git a/src/api/httpClientAdapter.client.ts b/src/api/httpClientAdapter.client.ts new file mode 100644 index 0000000..8330534 --- /dev/null +++ b/src/api/httpClientAdapter.client.ts @@ -0,0 +1,57 @@ +import { TinyRpcClientAdapter, TinyRpcError } from "@hiogawa/tiny-rpc"; +import { Result } from "@hiogawa/utils"; + +const GET_PAYLOAD_PARAM = "payload"; + +export function httpClientAdapter(opts: { + url: string; + pathsForGET?: string[]; +}): TinyRpcClientAdapter { + return { + send: async (data) => { + const url = [opts.url, data.path].join("/"); + const payload = JSON.stringify(data.args); + const method = opts.pathsForGET?.includes(data.path) + ? "GET" + : "POST"; + let req: Request; + if (method === "GET") { + req = new Request( + url + + "?" + + new URLSearchParams({ [GET_PAYLOAD_PARAM]: payload }) + ); + } else { + req = new Request(url, { + method: "POST", + body: payload, + headers: { + "content-type": "application/json; charset=utf-8", + }, + credentials: "include", + }); + } + let res: Response; + res = await fetch(req); + if (!res.ok) { + // throw new Error(`HTTP error: ${res.status}`); + throw new Error( + JSON.stringify({ + status: res.status, + statusText: res.statusText, + data: { message: await res.text() }, + internal: true, + }) + ); + // throw TinyRpcError.deserialize(res.status); + } + const result: Result = JSON.parse( + await res.text() + ); + if (!result.ok) { + throw TinyRpcError.deserialize(result.value); + } + return result.value; + }, + }; +} \ No newline at end of file diff --git a/src/api/httpClientAdapter.server.ts b/src/api/httpClientAdapter.server.ts new file mode 100644 index 0000000..cb6360a --- /dev/null +++ b/src/api/httpClientAdapter.server.ts @@ -0,0 +1,69 @@ +import { TinyRpcClientAdapter, TinyRpcError } from "@hiogawa/tiny-rpc"; +import { Result } from "@hiogawa/utils"; +import { tryGetContext } from "hono/context-storage"; + +const GET_PAYLOAD_PARAM = "payload"; + +export function httpClientAdapter(opts: { + url: string; + pathsForGET?: string[]; +}): TinyRpcClientAdapter { + return { + send: async (data) => { + const url = [opts.url, data.path].join("/"); + const payload = JSON.stringify(data.args); + const method = opts.pathsForGET?.includes(data.path) + ? "GET" + : "POST"; + let req: Request; + if (method === "GET") { + req = new Request( + url + + "?" + + new URLSearchParams({ [GET_PAYLOAD_PARAM]: payload }) + ); + } else { + req = new Request(url, { + method: "POST", + body: payload, + headers: { + "content-type": "application/json; charset=utf-8", + }, + credentials: "include", + }); + } + let res: Response; + if (import.meta.env.SSR) { + const c = tryGetContext(); + if (!c) { + throw new Error("Hono context not found in SSR"); + } + Object.entries(c.req.header()).forEach(([k, v]) => { + req.headers.append(k, v); + }); + res = await c.get("fetch")(req); + } else { + res = await fetch(req); + } + if (!res.ok) { + // throw new Error(`HTTP error: ${res.status}`); + throw new Error( + JSON.stringify({ + status: res.status, + statusText: res.statusText, + data: { message: await res.text() }, + internal: true, + }) + ); + // throw TinyRpcError.deserialize(res.status); + } + const result: Result = JSON.parse( + await res.text() + ); + if (!result.ok) { + throw TinyRpcError.deserialize(result.value); + } + return result.value; + }, + }; +} \ No newline at end of file diff --git a/src/api/rpc/auth.ts b/src/api/rpc/auth.ts index 748204c..f1d8612 100644 --- a/src/api/rpc/auth.ts +++ b/src/api/rpc/auth.ts @@ -164,13 +164,14 @@ const login = async (username: string, password: string) => { }; async function checkAuth() { + console.log("Check auth called"); const context = getContext(); const token = getCookie(context, 'auth_token'); if (!token) { return { authenticated: false, user: null }; } - + try { const payload = await verify(token, JWT_SECRET) as any; @@ -178,10 +179,11 @@ async function checkAuth() { const userRecord = Array.from(mockUsers.values()).find( record => record.user.id === payload.sub ); - + if (!userRecord) { return { authenticated: false, user: null }; } + // console.log("Check auth called 2", userRecord); return { authenticated: true, diff --git a/src/api/rpcclient.ts b/src/api/rpcclient.ts index 9d2757b..7c5e626 100644 --- a/src/api/rpcclient.ts +++ b/src/api/rpcclient.ts @@ -5,6 +5,8 @@ import { } from "@hiogawa/tiny-rpc"; import type { RpcRoutes } from "./rpc"; import { Result } from "@hiogawa/utils"; +import {httpClientAdapter} from "@httpClientAdapter"; +// console.log("httpClientAdapter module:", httpClientAdapter.toString()); declare let __host__: string; const endpoint = "/rpc"; const url = import.meta.env.SSR ? "http://localhost" : ""; @@ -15,65 +17,3 @@ export const client = proxyTinyRpc({ pathsForGET: [], }), }); -const GET_PAYLOAD_PARAM = "payload"; -function httpClientAdapter(opts: { - url: string; - pathsForGET?: string[]; -}): TinyRpcClientAdapter { - return { - send: async (data) => { - const url = [opts.url, data.path].join("/"); - const payload = JSON.stringify(data.args); - const method = opts.pathsForGET?.includes(data.path) - ? "GET" - : "POST"; - let req: Request; - if (method === "GET") { - req = new Request( - url + - "?" + - new URLSearchParams({ [GET_PAYLOAD_PARAM]: payload }) - ); - } else { - req = new Request(url, { - method: "POST", - body: payload, - headers: { - "content-type": "application/json; charset=utf-8", - }, - credentials: "include", - }); - } - let res: Response; - if (import.meta.env.SSR) { - const { getContext } = await import("hono/context-storage"); - const c = getContext(); - Object.entries(c.req.header()).forEach(([k, v]) => { - req.headers.append(k, v); - }); - res = await c.get("fetch")(req); - } else { - res = await fetch(req); - } - if (!res.ok) { - // throw new Error(`HTTP error: ${res.status}`); - throw new Error( - JSON.stringify({ - status: res.status, - statusText: res.statusText, - data: { message: await res.text() }, - internal: true, - }) - ); - // throw TinyRpcError.deserialize(res.status); - } - const result: Result = JSON.parse( - await res.text() - ); - if (!result.ok) { - throw TinyRpcError.deserialize(result.value); - } - return result.value; - }, - }; -} diff --git a/src/components/VueHead.tsx b/src/components/VueHead.tsx new file mode 100644 index 0000000..7429bf0 --- /dev/null +++ b/src/components/VueHead.tsx @@ -0,0 +1,15 @@ +import { useHead, UseHeadInput, UseHeadOptions } from "@unhead/vue"; +import { defineComponent, toRef } from "vue"; +interface VueHeadProps { + input: UseHeadInput; + options?: UseHeadOptions; +} +export const VueHead = defineComponent({ + name: "VueHead", + props: ["input", "options"], + setup(props) { + useHead(toRef(props, "input") as any, props.options); + return () => null; + } +}); +export default VueHead; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 629d6d3..d443448 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -8,11 +8,12 @@ import { contextStorage } from 'hono/context-storage'; import { cors } from "hono/cors"; import { jwtRpc, rpcServer } from './api/rpc'; import isMobile from 'is-mobile'; +import { useAuthStore } from './stores/auth'; const app = new Hono() // app.use(renderer) - +app.use('*', contextStorage()); app.use(cors(), async (c, next) => { c.set("fetch", app.request.bind(app)); const ua = c.req.header("User-Agent") @@ -21,15 +22,20 @@ app.use(cors(), async (c, next) => { }; c.set("isMobile", isMobile({ ua })); await next(); -}, contextStorage(), rpcServer); +}, rpcServer); app.get("/.well-known/*", (c) => { return c.json({ ok: true }); }); app.get("*", async (c) => { const url = new URL(c.req.url); - const { app, router, head } = createApp(); - router.push(url.pathname); - await router.isReady(); + const { app, router, head, pinia } = createApp(); + app.provide("honoContext", c); + await router.push(url.pathname); + await router.isReady().then(() => { + const auth = useAuthStore(); + auth.initialized = false; + auth.init(); + }); return streamText(c, async (stream) => { c.header("Content-Type", "text/html; charset=utf-8"); c.header("Content-Encoding", "Identity"); @@ -43,8 +49,8 @@ app.get("*", async (c) => { await stream.write(buildBootstrapScript()); await stream.write(""); await stream.pipe(appStream); - let json = htmlEscape(JSON.stringify(JSON.stringify(ctx))); - await stream.write(``); + await stream.write(``); + await stream.write(``); await stream.write(""); }); // return c.body(renderToWebStream(app, {})); diff --git a/src/lib/hoc/withErrorBoundary.tsx b/src/lib/hoc/withErrorBoundary.tsx index 6624730..a04717d 100644 --- a/src/lib/hoc/withErrorBoundary.tsx +++ b/src/lib/hoc/withErrorBoundary.tsx @@ -10,10 +10,10 @@ export function withErrorBoundary(WrappedComponent: any) {

500.  - Đã xảy ra lỗi. + Something went wrong.

-

Máy chủ đang gặp sự cố tạm thời và không thể xử lý yêu cầu của bạn. Vui lòng thử lại sau vài phút.

+

The server is currently experiencing temporary issues and cannot process your request. Please try again in a few minutes.

diff --git a/src/main.ts b/src/main.ts index 472e8bf..4e9c76c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,18 +10,21 @@ import Aura from '@primeuix/themes/aura'; import { createPinia } from "pinia"; import { useAuthStore } from './stores/auth'; -const pinia = createPinia(); export function createApp() { + const pinia = createPinia(); const app = createSSRApp(withErrorBoundary(RouterView)); const head = import.meta.env.SSR ? SSRHead() : CSRHead(); app.use(head); app.use(PrimeVue, { + // unstyled: true, theme: { preset: Aura, options: { darkModeSelector: '.my-app-dark', + cssLayer: false, + prefix: 'pv-', } } }); @@ -42,5 +45,5 @@ export function createApp() { }); } - return { app, router, head }; + return { app, router, head, pinia }; } \ No newline at end of file diff --git a/src/routes/NotFound.vue b/src/routes/NotFound.vue new file mode 100644 index 0000000..eec1360 --- /dev/null +++ b/src/routes/NotFound.vue @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/src/routes/index.ts b/src/routes/index.ts index 21ff529..d530b63 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -75,6 +75,11 @@ const routes: RouteData[] = [ component: () => import("./add/Add.vue"), }, ], + }, + { + path: "/:pathMatch(.*)*", + name: "not-found", + component: () => import("./NotFound.vue"), } ], }, @@ -88,6 +93,7 @@ const router = createRouter({ router.beforeEach((to, from, next) => { const auth = useAuthStore(); + console.log("Call on server:", Math.random()); if (to.matched.some((record) => record.meta.requiresAuth)) { if (!auth.user) { next({ name: "login" }); diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 6cb9684..be7d305 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -20,10 +20,15 @@ export const useAuthStore = defineStore('auth', () => { // Check auth status on init (reads from cookie) async function init() { - if (initialized.value) return; + console.log("Auth store init called"); + // if (initialized.value) return; try { - const response = await client.checkAuth(); + const response = await client.checkAuth().then((res) => { + + console.log("call", res); + return res; + }); if (response.authenticated && response.user) { user.value = response.user; // Get CSRF token if authenticated diff --git a/src/type.d.ts b/src/type.d.ts new file mode 100644 index 0000000..9866e88 --- /dev/null +++ b/src/type.d.ts @@ -0,0 +1,10 @@ +/// +/// + +declare module "@httpClientAdapter" { + import { TinyRpcClientAdapter } from "@hiogawa/tiny-rpc"; + export function httpClientAdapter(opts: { + url: string; + pathsForGET?: string[]; + }): TinyRpcClientAdapter; +} \ No newline at end of file diff --git a/ssrPlugin.ts b/ssrPlugin.ts index 265e67d..21dce38 100644 --- a/ssrPlugin.ts +++ b/ssrPlugin.ts @@ -32,6 +32,7 @@ export function clientFirstBuild(): Plugin { // Client build first // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (clientEnvironment) { + // console.log("Client First Build Plugin: Building client...", clientEnvironment.resolve); await builder.build(clientEnvironment); } @@ -107,6 +108,16 @@ export default function ssrPlugin(): Plugin[] { config(config) { config.define = config.define || {}; }, + resolveId(id, importer, options) { + if (!id.startsWith('@httpClientAdapter')) return + + return path.resolve( + __dirname, + options?.ssr + ? "./src/api/httpClientAdapter.server.ts" + : "./src/api/httpClientAdapter.client.ts" + ); + }, async configResolved(config) { const viteConfig = config as any; diff --git a/tsconfig.json b/tsconfig.json index f3df554..93c252f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,4 +16,11 @@ "@/*": ["./src/*"] } }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "auto-imports.d.ts", + "components.d.ts" + ] } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index aab9251..5566f1f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,30 +1,47 @@ import { cloudflare } from "@cloudflare/vite-plugin"; -import { defineConfig, Manifest, Plugin } from "vite"; +import { PrimeVueResolver } from "@primevue/auto-import-resolver"; import vue from "@vitejs/plugin-vue"; import vueJsx from "@vitejs/plugin-vue-jsx"; -import unocss from "unocss/vite"; import path from "node:path"; +import unocss from "unocss/vite"; +import Components from "unplugin-vue-components/vite"; +import AutoImport from "unplugin-auto-import/vite"; +import { defineConfig } from "vite"; import ssrPlugin from "./ssrPlugin"; -import Components from 'unplugin-vue-components/vite'; -import {PrimeVueResolver} from '@primevue/auto-import-resolver'; export default defineConfig((env) => { + // console.log("env:", env, import.meta.env); return { plugins: [ unocss(), vue(), vueJsx(), + AutoImport({ + imports: ["vue", "vue-router", "pinia"], // Common presets + dts: true, // Generate TypeScript declaration file + }), + Components({ + dirs: ["src/components"], + extensions: ["vue", "tsx"], + dts: true, + dtsTsx: true, + directives: false, + resolvers: [PrimeVueResolver()], + }), ssrPlugin(), cloudflare(), - Components({ - resolvers: [ - PrimeVueResolver() - ] - }), ], resolve: { alias: { "@": path.resolve(__dirname, "./src"), + // "httpClientAdapter": path.resolve(__dirname, "./src/api/httpClientAdapter.server.ts") }, }, + optimizeDeps: { + exclude: ["vue"], + }, + + ssr: { + noExternal: ["vue"], + }, }; });