auth querying consolidation
This commit is contained in:
@@ -14,6 +14,7 @@ import { MetaProvider } from "@solidjs/meta";
|
|||||||
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
||||||
import { BarsProvider, useBars } from "./context/bars";
|
import { BarsProvider, useBars } from "./context/bars";
|
||||||
import { DarkModeProvider } from "./context/darkMode";
|
import { DarkModeProvider } from "./context/darkMode";
|
||||||
|
import { AuthProvider } from "./context/auth";
|
||||||
import { createWindowWidth, isMobile } from "~/lib/resize-utils";
|
import { createWindowWidth, isMobile } from "~/lib/resize-utils";
|
||||||
import { MOBILE_CONFIG } from "./config";
|
import { MOBILE_CONFIG } from "./config";
|
||||||
import CustomScrollbar from "./components/CustomScrollbar";
|
import CustomScrollbar from "./components/CustomScrollbar";
|
||||||
@@ -210,7 +211,13 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
<DarkModeProvider>
|
<DarkModeProvider>
|
||||||
<BarsProvider>
|
<BarsProvider>
|
||||||
<Router root={(props) => <AppLayout>{props.children}</AppLayout>}>
|
<Router
|
||||||
|
root={(props) => (
|
||||||
|
<AuthProvider>
|
||||||
|
<AppLayout>{props.children}</AppLayout>
|
||||||
|
</AuthProvider>
|
||||||
|
)}
|
||||||
|
>
|
||||||
<FileRoutes />
|
<FileRoutes />
|
||||||
</Router>
|
</Router>
|
||||||
</BarsProvider>
|
</BarsProvider>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Typewriter } from "./Typewriter";
|
import { Typewriter } from "./Typewriter";
|
||||||
import { useBars } from "~/context/bars";
|
import { useBars } from "~/context/bars";
|
||||||
|
import { useAuth } from "~/context/auth";
|
||||||
|
import { revalidateAuth } from "~/lib/auth-query";
|
||||||
import { onMount, createSignal, Show, For, onCleanup } from "solid-js";
|
import { onMount, createSignal, Show, For, onCleanup } from "solid-js";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import { insertSoftHyphens, glitchText } from "~/lib/client-utils";
|
import { insertSoftHyphens, glitchText } from "~/lib/client-utils";
|
||||||
@@ -10,54 +12,8 @@ import { ActivityHeatmap } from "./ActivityHeatmap";
|
|||||||
import { DarkModeToggle } from "./DarkModeToggle";
|
import { DarkModeToggle } from "./DarkModeToggle";
|
||||||
import { SkeletonBox, SkeletonText } from "./SkeletonLoader";
|
import { SkeletonBox, SkeletonText } from "./SkeletonLoader";
|
||||||
import { env } from "~/env/client";
|
import { env } from "~/env/client";
|
||||||
import {
|
import { A, useNavigate, useLocation } from "@solidjs/router";
|
||||||
A,
|
|
||||||
useNavigate,
|
|
||||||
useLocation,
|
|
||||||
query,
|
|
||||||
createAsync,
|
|
||||||
revalidate
|
|
||||||
} from "@solidjs/router";
|
|
||||||
import { BREAKPOINTS } from "~/config";
|
import { BREAKPOINTS } from "~/config";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
|
||||||
|
|
||||||
const getUserState = query(async () => {
|
|
||||||
"use server";
|
|
||||||
const { getPrivilegeLevel, getUserID } = await import("~/server/utils");
|
|
||||||
const { ConnectionFactory } = await import("~/server/utils");
|
|
||||||
const event = getRequestEvent()!;
|
|
||||||
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
|
|
||||||
const userId = await getUserID(event.nativeEvent);
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
return {
|
|
||||||
isAuthenticated: false,
|
|
||||||
email: null,
|
|
||||||
privilegeLevel: "anonymous" as const
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const conn = ConnectionFactory();
|
|
||||||
const res = await conn.execute({
|
|
||||||
sql: "SELECT email FROM User WHERE id = ?",
|
|
||||||
args: [userId]
|
|
||||||
});
|
|
||||||
|
|
||||||
const email = res.rows[0] ? (res.rows[0].email as string | null) : null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isAuthenticated: true,
|
|
||||||
email,
|
|
||||||
privilegeLevel
|
|
||||||
};
|
|
||||||
}, "bars-user-state");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this function after login/logout to refresh the user state in the sidebar
|
|
||||||
*/
|
|
||||||
export function revalidateUserState() {
|
|
||||||
revalidate(getUserState.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDomainName(url: string): string {
|
function formatDomainName(url: string): string {
|
||||||
const domain = url.split("://")[1]?.split(":")[0] ?? url;
|
const domain = url.split("://")[1]?.split(":")[0] ?? url;
|
||||||
@@ -247,7 +203,7 @@ export function RightBarContent() {
|
|||||||
export function LeftBar() {
|
export function LeftBar() {
|
||||||
const { leftBarVisible, setLeftBarVisible } = useBars();
|
const { leftBarVisible, setLeftBarVisible } = useBars();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const userState = createAsync(() => getUserState());
|
const { isAuthenticated, email, isAdmin } = useAuth();
|
||||||
let ref: HTMLDivElement | undefined;
|
let ref: HTMLDivElement | undefined;
|
||||||
|
|
||||||
const [recentPosts, setRecentPosts] = createSignal<any[] | undefined>(
|
const [recentPosts, setRecentPosts] = createSignal<any[] | undefined>(
|
||||||
@@ -277,6 +233,7 @@ export function LeftBar() {
|
|||||||
setSignOutLoading(true);
|
setSignOutLoading(true);
|
||||||
try {
|
try {
|
||||||
await api.auth.signOut.mutate();
|
await api.auth.signOut.mutate();
|
||||||
|
revalidateAuth(); // Clear auth state immediately
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Sign out failed:", error);
|
console.error("Sign out failed:", error);
|
||||||
@@ -540,9 +497,7 @@ export function LeftBar() {
|
|||||||
Blog
|
Blog
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<Show
|
<Show when={isMounted() && isAdmin()}>
|
||||||
when={isMounted() && userState()?.privilegeLevel === "admin"}
|
|
||||||
>
|
|
||||||
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
<a href="/analytics" onClick={handleLinkClick}>
|
<a href="/analytics" onClick={handleLinkClick}>
|
||||||
Analytics
|
Analytics
|
||||||
@@ -557,7 +512,7 @@ export function LeftBar() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
when={isMounted() && userState()?.isAuthenticated}
|
when={isMounted() && isAuthenticated()}
|
||||||
fallback={
|
fallback={
|
||||||
<a href="/login" onClick={handleLinkClick}>
|
<a href="/login" onClick={handleLinkClick}>
|
||||||
Login
|
Login
|
||||||
@@ -566,16 +521,16 @@ export function LeftBar() {
|
|||||||
>
|
>
|
||||||
<A href="/account" onClick={handleLinkClick}>
|
<A href="/account" onClick={handleLinkClick}>
|
||||||
Account
|
Account
|
||||||
<Show when={userState()?.email}>
|
<Show when={email()}>
|
||||||
<span class="text-subtext0 text-sm font-normal">
|
<span class="text-subtext0 text-sm font-normal">
|
||||||
{" "}
|
{" "}
|
||||||
({userState()!.email})
|
({email()})
|
||||||
</span>
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
</A>
|
</A>
|
||||||
</Show>
|
</Show>
|
||||||
</li>
|
</li>
|
||||||
<Show when={isMounted() && userState()?.isAuthenticated}>
|
<Show when={isMounted() && isAuthenticated()}>
|
||||||
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
|
||||||
<button
|
<button
|
||||||
onClick={handleSignOut}
|
onClick={handleSignOut}
|
||||||
|
|||||||
94
src/context/auth.tsx
Normal file
94
src/context/auth.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* Auth Context Provider
|
||||||
|
* Provides convenient access to auth state throughout the app
|
||||||
|
*
|
||||||
|
* Security Note:
|
||||||
|
* - Context is for UI display only (showing/hiding buttons, user email, etc.)
|
||||||
|
* - Server endpoints ALWAYS validate independently from cookies
|
||||||
|
* - Never trust client-side state for authorization decisions
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createContext, useContext, Accessor, ParentComponent } from "solid-js";
|
||||||
|
import { createAsync } from "@solidjs/router";
|
||||||
|
import { getUserState, revalidateAuth, type UserState } from "~/lib/auth-query";
|
||||||
|
|
||||||
|
interface AuthContextType {
|
||||||
|
/** Current user state (for UI display) */
|
||||||
|
userState: Accessor<UserState | undefined>;
|
||||||
|
|
||||||
|
/** Is user authenticated (convenience) */
|
||||||
|
isAuthenticated: Accessor<boolean>;
|
||||||
|
|
||||||
|
/** User email (null if not authenticated) */
|
||||||
|
email: Accessor<string | null>;
|
||||||
|
|
||||||
|
/** User display name (null if not set) */
|
||||||
|
displayName: Accessor<string | null>;
|
||||||
|
|
||||||
|
/** User ID (null if not authenticated) */
|
||||||
|
userId: Accessor<string | null>;
|
||||||
|
|
||||||
|
/** Is user admin (for UI display only - server still validates) */
|
||||||
|
isAdmin: Accessor<boolean>;
|
||||||
|
|
||||||
|
/** Is email verified */
|
||||||
|
isEmailVerified: Accessor<boolean>;
|
||||||
|
|
||||||
|
/** Refresh auth state from server */
|
||||||
|
refreshAuth: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthContext = createContext<AuthContextType>();
|
||||||
|
|
||||||
|
export const AuthProvider: ParentComponent = (props) => {
|
||||||
|
// Get server state via SolidStart query
|
||||||
|
const serverAuth = createAsync(() => getUserState());
|
||||||
|
|
||||||
|
// Convenience accessors with safe defaults
|
||||||
|
const isAuthenticated = () => serverAuth()?.isAuthenticated ?? false;
|
||||||
|
const email = () => serverAuth()?.email ?? null;
|
||||||
|
const displayName = () => serverAuth()?.displayName ?? null;
|
||||||
|
const userId = () => serverAuth()?.userId ?? null;
|
||||||
|
const isAdmin = () => serverAuth()?.privilegeLevel === "admin";
|
||||||
|
const isEmailVerified = () => serverAuth()?.emailVerified ?? false;
|
||||||
|
|
||||||
|
const value: AuthContextType = {
|
||||||
|
userState: serverAuth,
|
||||||
|
isAuthenticated,
|
||||||
|
email,
|
||||||
|
displayName,
|
||||||
|
userId,
|
||||||
|
isAdmin,
|
||||||
|
isEmailVerified,
|
||||||
|
refreshAuth: revalidateAuth
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={value}>{props.children}</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to access auth state anywhere in the app
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* function MyComponent() {
|
||||||
|
* const { isAuthenticated, email, refreshAuth } = useAuth();
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <Show when={isAuthenticated()}>
|
||||||
|
* <p>Welcome, {email()}!</p>
|
||||||
|
* <button onClick={() => refreshAuth()}>Refresh</button>
|
||||||
|
* </Show>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function useAuth() {
|
||||||
|
const context = useContext(AuthContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useAuth must be used within AuthProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
81
src/lib/auth-query.ts
Normal file
81
src/lib/auth-query.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Shared Auth Query
|
||||||
|
* Single source of truth for authentication state across the app
|
||||||
|
*
|
||||||
|
* Security Model:
|
||||||
|
* - Server query reads from httpOnly cookies (secure)
|
||||||
|
* - Client context syncs from this query (UI convenience)
|
||||||
|
* - Server endpoints always validate independently (never trust client)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { query, revalidate as revalidateKey } from "@solidjs/router";
|
||||||
|
import { getRequestEvent } from "solid-js/web";
|
||||||
|
|
||||||
|
export interface UserState {
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
userId: string | null;
|
||||||
|
email: string | null;
|
||||||
|
displayName: string | null;
|
||||||
|
emailVerified: boolean;
|
||||||
|
privilegeLevel: "admin" | "user" | "anonymous";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global auth state query - single source of truth
|
||||||
|
* Called on server during SSR, cached by SolidStart router
|
||||||
|
*/
|
||||||
|
export const getUserState = query(async (): Promise<UserState> => {
|
||||||
|
"use server";
|
||||||
|
const { getPrivilegeLevel, getUserID, ConnectionFactory } =
|
||||||
|
await import("~/server/utils");
|
||||||
|
const event = getRequestEvent()!;
|
||||||
|
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
|
||||||
|
const userId = await getUserID(event.nativeEvent);
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return {
|
||||||
|
isAuthenticated: false,
|
||||||
|
userId: null,
|
||||||
|
email: null,
|
||||||
|
displayName: null,
|
||||||
|
emailVerified: false,
|
||||||
|
privilegeLevel: "anonymous"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn = ConnectionFactory();
|
||||||
|
const res = await conn.execute({
|
||||||
|
sql: "SELECT email, display_name, email_verified FROM User WHERE id = ?",
|
||||||
|
args: [userId]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.rows.length === 0) {
|
||||||
|
return {
|
||||||
|
isAuthenticated: false,
|
||||||
|
userId: null,
|
||||||
|
email: null,
|
||||||
|
displayName: null,
|
||||||
|
emailVerified: false,
|
||||||
|
privilegeLevel: "anonymous"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = res.rows[0] as any;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isAuthenticated: true,
|
||||||
|
userId,
|
||||||
|
email: user.email ?? null,
|
||||||
|
displayName: user.display_name ?? null,
|
||||||
|
emailVerified: user.email_verified === 1,
|
||||||
|
privilegeLevel
|
||||||
|
};
|
||||||
|
}, "global-auth-state");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revalidate auth state globally
|
||||||
|
* Call this after login, logout, token refresh, email verification
|
||||||
|
*/
|
||||||
|
export function revalidateAuth() {
|
||||||
|
revalidateKey(getUserState.key);
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import { getClientCookie } from "~/lib/cookies.client";
|
import { getClientCookie } from "~/lib/cookies.client";
|
||||||
import { getTimeUntilExpiry } from "~/lib/client-utils";
|
import { getTimeUntilExpiry } from "~/lib/client-utils";
|
||||||
|
import { revalidateAuth } from "~/lib/auth-query";
|
||||||
|
|
||||||
class TokenRefreshManager {
|
class TokenRefreshManager {
|
||||||
private refreshTimer: ReturnType<typeof setTimeout> | null = null;
|
private refreshTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
@@ -108,6 +109,7 @@ class TokenRefreshManager {
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log("[Token Refresh] Token refreshed successfully");
|
console.log("[Token Refresh] Token refreshed successfully");
|
||||||
|
revalidateAuth(); // Refresh auth state after token refresh
|
||||||
this.scheduleNextRefresh(); // Schedule next refresh
|
this.scheduleNextRefresh(); // Schedule next refresh
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import { createSignal, Show, For, createEffect, ErrorBoundary } from "solid-js";
|
import { createSignal, Show, For, createEffect, ErrorBoundary } from "solid-js";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { redirect, query, createAsync, useNavigate } from "@solidjs/router";
|
import { redirect, query, createAsync, useNavigate } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
|
|
||||||
const checkAdmin = query(async (): Promise<boolean> => {
|
const checkAdmin = query(async (): Promise<boolean> => {
|
||||||
"use server";
|
"use server";
|
||||||
const { getUserID } = await import("~/server/auth");
|
const { getUserState } = await import("~/lib/auth-query");
|
||||||
const { env } = await import("~/env/server");
|
const userState = await getUserState();
|
||||||
const event = getEvent()!;
|
|
||||||
const userId = await getUserID(event);
|
|
||||||
|
|
||||||
if (!userId || userId !== env.ADMIN_ID) {
|
if (userState.privilegeLevel !== "admin") {
|
||||||
throw redirect("/");
|
throw redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,32 +2,30 @@ import { Show, lazy } from "solid-js";
|
|||||||
import { query, redirect } from "@solidjs/router";
|
import { query, redirect } from "@solidjs/router";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getUserState } from "~/lib/auth-query";
|
||||||
import { Spinner } from "~/components/Spinner";
|
import { Spinner } from "~/components/Spinner";
|
||||||
import "../post.css";
|
import "../post.css";
|
||||||
|
|
||||||
const PostForm = lazy(() => import("~/components/blog/PostForm"));
|
const PostForm = lazy(() => import("~/components/blog/PostForm"));
|
||||||
|
|
||||||
const getAuthState = query(async () => {
|
const checkAdminAccess = query(async () => {
|
||||||
"use server";
|
"use server";
|
||||||
const { getPrivilegeLevel, getUserID } = await import("~/server/utils");
|
// Reuse shared auth query for consistency
|
||||||
const event = getEvent()!;
|
const userState = await getUserState();
|
||||||
const privilegeLevel = await getPrivilegeLevel(event);
|
|
||||||
const userID = await getUserID(event);
|
|
||||||
|
|
||||||
if (privilegeLevel !== "admin") {
|
if (userState.privilegeLevel !== "admin") {
|
||||||
throw redirect("/401");
|
throw redirect("/401");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { privilegeLevel, userID };
|
return { userID: userState.userId! };
|
||||||
}, "create-post-auth");
|
}, "create-post-admin-check");
|
||||||
|
|
||||||
export const route = {
|
export const route = {
|
||||||
load: () => getAuthState()
|
load: () => checkAdminAccess()
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CreatePost() {
|
export default function CreatePost() {
|
||||||
const authState = createAsync(() => getAuthState());
|
const authState = createAsync(() => checkAdminAccess());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,20 +2,17 @@ import { Show, lazy } from "solid-js";
|
|||||||
import { useParams, query, redirect } from "@solidjs/router";
|
import { useParams, query, redirect } from "@solidjs/router";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
|
||||||
import "../post.css";
|
import "../post.css";
|
||||||
|
|
||||||
const PostForm = lazy(() => import("~/components/blog/PostForm"));
|
const PostForm = lazy(() => import("~/components/blog/PostForm"));
|
||||||
|
|
||||||
const getPostForEdit = query(async (id: string) => {
|
const getPostForEdit = query(async (id: string) => {
|
||||||
"use server";
|
"use server";
|
||||||
const { getPrivilegeLevel, getUserID, ConnectionFactory } =
|
const { getUserState } = await import("~/lib/auth-query");
|
||||||
await import("~/server/utils");
|
const { ConnectionFactory } = await import("~/server/utils");
|
||||||
const event = getEvent()!;
|
const userState = await getUserState();
|
||||||
const privilegeLevel = await getPrivilegeLevel(event);
|
|
||||||
const userID = await getUserID(event);
|
|
||||||
|
|
||||||
if (privilegeLevel !== "admin") {
|
if (userState.privilegeLevel !== "admin") {
|
||||||
throw redirect("/401");
|
throw redirect("/401");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +32,12 @@ const getPostForEdit = query(async (id: string) => {
|
|||||||
const post = results.rows[0];
|
const post = results.rows[0];
|
||||||
const tags = tagRes.rows;
|
const tags = tagRes.rows;
|
||||||
|
|
||||||
return { post, tags, privilegeLevel, userID };
|
return {
|
||||||
|
post,
|
||||||
|
tags,
|
||||||
|
privilegeLevel: userState.privilegeLevel,
|
||||||
|
userID: userState.userId
|
||||||
|
};
|
||||||
}, "post-for-edit");
|
}, "post-for-edit");
|
||||||
|
|
||||||
export const route = {
|
export const route = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
query
|
query
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { revalidateUserState } from "~/components/Bars";
|
import { revalidateAuth } from "~/lib/auth-query";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import GoogleLogo from "~/components/icons/GoogleLogo";
|
import GoogleLogo from "~/components/icons/GoogleLogo";
|
||||||
import GitHub from "~/components/icons/GitHub";
|
import GitHub from "~/components/icons/GitHub";
|
||||||
@@ -206,7 +206,7 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
if (response.ok && result.result?.data?.success) {
|
if (response.ok && result.result?.data?.success) {
|
||||||
setShowPasswordSuccess(true);
|
setShowPasswordSuccess(true);
|
||||||
revalidateUserState(); // Refresh user state in sidebar
|
revalidateAuth(); // Refresh auth state globally
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate("/account", { replace: true });
|
navigate("/account", { replace: true });
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import { createSignal, For, Show } from "solid-js";
|
import { createSignal, For, Show } from "solid-js";
|
||||||
import { query, createAsync } from "@solidjs/router";
|
import { query, createAsync } from "@solidjs/router";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
|
import { getUserState } from "~/lib/auth-query";
|
||||||
|
|
||||||
const getAuthState = query(async () => {
|
const checkAdminAccess = query(async () => {
|
||||||
"use server";
|
"use server";
|
||||||
const { getPrivilegeLevel } = await import("~/server/utils");
|
const userState = await getUserState();
|
||||||
const event = getRequestEvent()!;
|
return { privilegeLevel: userState.privilegeLevel };
|
||||||
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
|
|
||||||
|
|
||||||
return { privilegeLevel };
|
|
||||||
}, "test-auth-state");
|
}, "test-auth-state");
|
||||||
|
|
||||||
type EndpointTest = {
|
type EndpointTest = {
|
||||||
@@ -840,7 +837,7 @@ const routerSections: RouterSection[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function TestPage() {
|
export default function TestPage() {
|
||||||
const authState = createAsync(() => getAuthState());
|
const authState = createAsync(() => checkAdminAccess());
|
||||||
|
|
||||||
const [expandedSections, setExpandedSections] = createSignal<Set<string>>(
|
const [expandedSections, setExpandedSections] = createSignal<Set<string>>(
|
||||||
new Set()
|
new Set()
|
||||||
|
|||||||
Reference in New Issue
Block a user