diff --git a/components.d.ts b/components.d.ts index 40b7d2a..ef49267 100644 --- a/components.d.ts +++ b/components.d.ts @@ -19,6 +19,7 @@ declare module 'vue' { Button: typeof import('primevue/button')['default'] Checkbox: typeof import('primevue/checkbox')['default'] CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default'] + Credit: typeof import('./src/components/icons/Credit.vue')['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'] @@ -48,6 +49,7 @@ declare global { const Button: typeof import('primevue/button')['default'] const Checkbox: typeof import('primevue/checkbox')['default'] const CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default'] + const Credit: typeof import('./src/components/icons/Credit.vue')['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'] diff --git a/src/api/rpc/auth.ts b/src/api/rpc/auth.ts index 900fe0c..918666e 100644 --- a/src/api/rpc/auth.ts +++ b/src/api/rpc/auth.ts @@ -224,11 +224,11 @@ async function getCSRFToken() { const payload = await verify(token, JWT_SECRET) as any; const stored = csrfTokens.get(payload.sessionId); - if (!stored) { - throw new Error('CSRF token not found'); - } + // if (!stored) { + // throw new Error('CSRF token not found'); + // } - return { csrfToken: stored.token }; + return { csrfToken: stored?.token || null }; } export const authMethods = { diff --git a/src/api/rpc/index.ts b/src/api/rpc/index.ts index af46bff..d70e044 100644 --- a/src/api/rpc/index.ts +++ b/src/api/rpc/index.ts @@ -6,7 +6,7 @@ import { import { tinyassert } from "@hiogawa/utils"; import { MiddlewareHandler, type Context, type Next } from "hono"; import { getContext } from "hono/context-storage"; -import { register } from "module"; +import { csrf } from 'hono/csrf' import { z } from "zod"; import { authMethods } from "./auth"; import { jwt } from "hono/jwt"; @@ -320,7 +320,9 @@ export const rpcServer = async (c: Context, next: Next) => { if (c.req.path !== endpoint && !c.req.path.startsWith(endpoint + "/")) { return await next(); } - // c.get("redis").has(`auth_token:${}`) + const cert = c.req.header() + console.log("RPC Request Path:", c.req.raw.cf); + // if (!cert) return c.text('Forbidden', 403) const handler = exposeTinyRpc({ routes, adapter: httpServerAdapter({ endpoint }), diff --git a/src/components/DashboardLayout.vue b/src/components/DashboardLayout.vue index 1a1b422..194bff9 100644 --- a/src/components/DashboardLayout.vue +++ b/src/components/DashboardLayout.vue @@ -1,37 +1,36 @@ + \ No newline at end of file diff --git a/src/components/icons/AddFilled.vue b/src/components/icons/AddFilled.vue deleted file mode 100644 index d7b3c5b..0000000 --- a/src/components/icons/AddFilled.vue +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/src/components/icons/Bell.vue b/src/components/icons/Bell.vue index 1c1623a..3cfdf82 100644 --- a/src/components/icons/Bell.vue +++ b/src/components/icons/Bell.vue @@ -1,3 +1,18 @@ \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/BellFilled.vue b/src/components/icons/BellFilled.vue deleted file mode 100644 index ed02e02..0000000 --- a/src/components/icons/BellFilled.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/icons/Credit.vue b/src/components/icons/Credit.vue new file mode 100644 index 0000000..b106cf6 --- /dev/null +++ b/src/components/icons/Credit.vue @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/src/components/icons/Home.vue b/src/components/icons/Home.vue index 37eee92..de9ab31 100644 --- a/src/components/icons/Home.vue +++ b/src/components/icons/Home.vue @@ -1,3 +1,18 @@ \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/HomeFilled.vue b/src/components/icons/HomeFilled.vue deleted file mode 100644 index 48c4b57..0000000 --- a/src/components/icons/HomeFilled.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/icons/Layout.vue b/src/components/icons/Layout.vue index b66a45b..c5302fa 100644 --- a/src/components/icons/Layout.vue +++ b/src/components/icons/Layout.vue @@ -1,3 +1,19 @@ \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/LayoutFilled.vue b/src/components/icons/LayoutFilled.vue deleted file mode 100644 index ee44361..0000000 --- a/src/components/icons/LayoutFilled.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/icons/Upload.vue b/src/components/icons/Upload.vue index 3cafb23..9ebc8be 100644 --- a/src/components/icons/Upload.vue +++ b/src/components/icons/Upload.vue @@ -1,3 +1,18 @@ \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/UploadFilled.vue b/src/components/icons/UploadFilled.vue deleted file mode 100644 index 0717c75..0000000 --- a/src/components/icons/UploadFilled.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/components/icons/Video.vue b/src/components/icons/Video.vue index 7679b34..cee8c15 100644 --- a/src/components/icons/Video.vue +++ b/src/components/icons/Video.vue @@ -1,3 +1,16 @@ \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/src/components/icons/VideoFilled.vue b/src/components/icons/VideoFilled.vue deleted file mode 100644 index e6ca26b..0000000 --- a/src/components/icons/VideoFilled.vue +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 5e68d99..ca7d69a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -30,6 +30,7 @@ app.get("/.well-known/*", (c) => { return c.json({ ok: true }); }); app.get("*", async (c) => { + const nonce = crypto.randomUUID(); const url = new URL(c.req.url); const { app, router, head, pinia, bodyClass } = createApp(); app.provide("honoContext", c); @@ -59,8 +60,8 @@ app.get("*", async (c) => { await Promise.all(styleTags.filter(tag => usedStyles.has(tag.name.replace(/-(variables|style)$/, ""))).map(tag => stream.write(``))); await stream.write(``); await stream.pipe(appStream); - await stream.write(``); - await stream.write(``); + Object.assign(ctx, { $p: pinia.state.value }); + await stream.write(``); await stream.write(""); }); }) diff --git a/src/main.ts b/src/main.ts index b685182..2b30978 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,6 +24,7 @@ export function createApp() { preset: Aura, options: { darkModeSelector: '.my-app-dark', + cssLayer: false, // cssLayer: { // name: 'primevue', // order: 'theme, base, primevue' @@ -32,15 +33,18 @@ export function createApp() { } }); app.use(ToastService); - app.directive('no-hydrate', { + app.directive('nh', { created(el) { el.__v_skip = true; } }); app.directive("tooltip", Tooltip) if (!import.meta.env.SSR) { - if ((window as any).__PINIA_STATE__ ) { - pinia.state.value = (window as any).__PINIA_STATE__; + Object.entries(JSON.parse(document.getElementById("__APP_DATA__")?.innerText || "{}")).forEach(([key, value]) => { + (window as any)[key] = value; + }); + if ((window as any).$p ) { + pinia.state.value = (window as any).$p; } } app.use(pinia); diff --git a/src/routes/auth/layout.vue b/src/routes/auth/layout.vue index ff2818a..7b8db21 100644 --- a/src/routes/auth/layout.vue +++ b/src/routes/auth/layout.vue @@ -11,7 +11,10 @@ {{ content[route.name as keyof typeof content]?.subtitle || '' }}

diff --git a/src/routes/index.ts b/src/routes/index.ts index 64beed0..6a18c76 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -67,33 +67,57 @@ const routes: RouteData[] = [ path: "", name: "overview", component: () => import("./add/Add.vue"), - beforeEnter: (to, from, next) => { - const head = inject(headSymbol); - (head as any).push({ + meta: { + head: { title: 'Overview - Holistream', - }); - next(); + }, } }, { path: "upload", name: "upload", component: () => import("./add/Add.vue"), + meta: { + head: { + title: 'Upload - Holistream', + }, + } }, { path: "video", name: "video", component: () => import("./add/Add.vue"), + meta: { + head: { + title: 'Videos - Holistream', + meta: [ + { name: 'description', content: 'Manage your video content.' }, + ], + }, + } }, { - path: "add", - name: "add", + path: "plans", + name: "plans", component: () => import("./add/Add.vue"), + meta: { + head: { + title: 'Plans & Billing', + meta: [ + { name: 'description', content: 'Manage your plans and billing information.' }, + ], + }, + } }, { path: "notification", name: "notification", component: () => import("./add/Add.vue"), + meta: { + head: { + title: 'Notification - Holistream', + }, + } }, ], }, @@ -115,6 +139,8 @@ const router = createRouter({ router.beforeEach((to, from, next) => { const auth = useAuthStore(); + const head = inject(headSymbol); + (head as any).push(to.meta.head || {}); 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 2b6b4e3..b3e3f4f 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -55,47 +55,30 @@ export const useAuthStore = defineStore('auth', () => { }).finally(() => { loading.value = false; }); - // try { - // const response = await client.login(username, password); - // user.value = response.user; - // csrfToken.value = response.csrfToken; - // router.push('/'); - // } catch (e: any) { - // // console.log(JSON.parse(e.message)) - // // error.value = e.message || 'Login failed'; - // error.value = 'Login failed'; - // throw e; - // } finally { - // loading.value = false; - // } } async function register(username: string, email: string, password: string) { loading.value = true; error.value = null; - try { - const response = await client.register({ username, email, password }); + return client.register({ username, email, password }).then((response) => { user.value = response.user; csrfToken.value = response.csrfToken; router.push('/'); - } catch (e: any) { + }).catch((e: any) => { // error.value = e.message || 'Registration failed'; error.value = 'Registration failed'; throw e; - } finally { + }).finally(() => { loading.value = false; - } + }); } async function logout() { - try { - await client.logout(); - } catch (e) { - // Ignore errors on logout - } - user.value = null; - csrfToken.value = null; - router.push('/'); + return client.logout().then(() => { + user.value = null; + csrfToken.value = null; + router.push('/'); + }) } return { user, loading, error, csrfToken, initialized, init, login, register, logout, $reset: () => { diff --git a/uno.config.ts b/uno.config.ts index 0b858da..ff0c40b 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -93,7 +93,9 @@ export default defineConfig({ ], ["animate-loadingBar", ["animation", "loadingBar 1.5s linear infinite"]], ], - transformers: [transformerVariantGroup(), transformerCompileClass()], + transformers: [transformerVariantGroup(), transformerCompileClass({ + classPrefix: "_", + })], preflights: [ { getCSS: (context) => {