✨ User info
This commit is contained in:
		@@ -28,6 +28,7 @@ var auth = keyauth.New(keyauth.Config{
 | 
				
			|||||||
			return false, err
 | 
								return false, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.Locals("principal", user)
 | 
				
			||||||
		c.Locals("permissions", user.Permissions.Data())
 | 
							c.Locals("permissions", user.Permissions.Data())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								pkg/server/profiles_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								pkg/server/profiles_api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"code.smartsheep.studio/hydrogen/passport/pkg/models"
 | 
				
			||||||
 | 
						"github.com/gofiber/fiber/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func aboutMe(c *fiber.Ctx) error {
 | 
				
			||||||
 | 
						user := c.Locals("principal").(models.Account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(user)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,7 @@ func NewServer() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	api := A.Group("/api").Name("API")
 | 
						api := A.Group("/api").Name("API")
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							api.Get("/users/me", auth, aboutMe)
 | 
				
			||||||
		api.Post("/users", doRegister)
 | 
							api.Post("/users", doRegister)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		api.Put("/auth", startChallenge)
 | 
							api.Put("/auth", startChallenge)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,25 @@
 | 
				
			|||||||
import Navbar from "./shared/Navbar.tsx";
 | 
					import Navbar from "./shared/Navbar.tsx";
 | 
				
			||||||
 | 
					import { readProfiles } from "../stores/userinfo.ts";
 | 
				
			||||||
 | 
					import { createSignal, Show } from "solid-js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function RootLayout(props: any) {
 | 
					export default function RootLayout(props: any) {
 | 
				
			||||||
 | 
					  const [ready, setReady] = createSignal(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  readProfiles().then(() => setReady(true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
 | 
					    <Show when={ready()} fallback={
 | 
				
			||||||
 | 
					      <div class="h-screen w-screen flex justify-center items-center">
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          <span class="loading loading-lg loading-infinity"></span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    }>
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <Navbar />
 | 
					        <Navbar />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <main class="h-[calc(100vh-68px)]">{props.children}</main>
 | 
					        <main class="h-[calc(100vh-68px)]">{props.children}</main>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					    </Show>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { For } from "solid-js";
 | 
					import { For, Show } from "solid-js";
 | 
				
			||||||
 | 
					import { userinfo } from "../../stores/userinfo.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface MenuItem {
 | 
					interface MenuItem {
 | 
				
			||||||
  label: string;
 | 
					  label: string;
 | 
				
			||||||
@@ -57,7 +58,9 @@ export default function Navbar() {
 | 
				
			|||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="navbar-end pe-5">
 | 
					      <div class="navbar-end pe-5">
 | 
				
			||||||
 | 
					        <Show when={!userinfo.isLoggedIn}>
 | 
				
			||||||
          <a href="/auth/login" class="btn btn-sm btn-primary">Login</a>
 | 
					          <a href="/auth/login" class="btn btn-sm btn-primary">Login</a>
 | 
				
			||||||
 | 
					        </Show>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import { readProfiles } from "../../stores/userinfo.ts";
 | 
				
			||||||
import { useNavigate } from "@solidjs/router";
 | 
					import { useNavigate } from "@solidjs/router";
 | 
				
			||||||
import { createSignal, For, Match, Show, Switch } from "solid-js";
 | 
					import { createSignal, For, Match, Show, Switch } from "solid-js";
 | 
				
			||||||
import Cookie from "universal-cookie";
 | 
					import Cookie from "universal-cookie";
 | 
				
			||||||
@@ -85,6 +86,7 @@ export default function Login() {
 | 
				
			|||||||
        const data = await res.json();
 | 
					        const data = await res.json();
 | 
				
			||||||
        if (data["is_finished"]) {
 | 
					        if (data["is_finished"]) {
 | 
				
			||||||
          await grantToken(data["session"]["grant_token"]);
 | 
					          await grantToken(data["session"]["grant_token"]);
 | 
				
			||||||
 | 
					          await readProfiles();
 | 
				
			||||||
          navigate("/");
 | 
					          navigate("/");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          setError(null);
 | 
					          setError(null);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
 | 
					import { userinfo } from "../stores/userinfo.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Dashboard() {
 | 
					export default function Dashboard() {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div class="container mx-auto pt-12">
 | 
					    <div class="container mx-auto pt-12">
 | 
				
			||||||
      <h1 class="text-2xl font-bold">Welcome, undefined</h1>
 | 
					      <h1 class="text-2xl font-bold">Welcome, {userinfo.displayName}</h1>
 | 
				
			||||||
      <p>What's a nice day!</p>
 | 
					      <p>What's a nice day!</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								view/src/stores/userinfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								view/src/stores/userinfo.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					import { createStore } from "solid-js/store";
 | 
				
			||||||
 | 
					import Cookie from "universal-cookie";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [userinfo, setUserinfo] = createStore({
 | 
				
			||||||
 | 
					  isLoggedIn: false,
 | 
				
			||||||
 | 
					  displayName: "Citizen",
 | 
				
			||||||
 | 
					  profiles: null,
 | 
				
			||||||
 | 
					  meta: null
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function checkLoggedIn(): boolean {
 | 
				
			||||||
 | 
					  return new Cookie().get("access_token");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getAtk(): string {
 | 
				
			||||||
 | 
					  return new Cookie().get("access_token");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function refreshAtk() {
 | 
				
			||||||
 | 
					  const rtk = new Cookie().get("refresh_token");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const res = await fetch("/api/auth/token", {
 | 
				
			||||||
 | 
					    method: "POST",
 | 
				
			||||||
 | 
					    headers: { "Content-Type": "application/json" },
 | 
				
			||||||
 | 
					    body: JSON.stringify({
 | 
				
			||||||
 | 
					      code: rtk,
 | 
				
			||||||
 | 
					      grant_type: "refresh_token"
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  if (res.status !== 200) {
 | 
				
			||||||
 | 
					    throw new Error(await res.text());
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    const data = await res.json();
 | 
				
			||||||
 | 
					    new Cookie().set("access_token", data["access_token"], { path: "/" });
 | 
				
			||||||
 | 
					    new Cookie().set("refresh_token", data["refresh_token"], { path: "/" });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function readProfiles() {
 | 
				
			||||||
 | 
					  if (!checkLoggedIn()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const res = await fetch("/api/users/me", {
 | 
				
			||||||
 | 
					    headers: { "Authorization": `Bearer ${getAtk()}` }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (res.status !== 200) {
 | 
				
			||||||
 | 
					    // Auto retry after refresh access token
 | 
				
			||||||
 | 
					    await refreshAtk();
 | 
				
			||||||
 | 
					    return await readProfiles();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const data = await res.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setUserinfo({
 | 
				
			||||||
 | 
					    isLoggedIn: true,
 | 
				
			||||||
 | 
					    displayName: data["nick"],
 | 
				
			||||||
 | 
					    profiles: null,
 | 
				
			||||||
 | 
					    meta: data
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { userinfo };
 | 
				
			||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
    "target": "ES2020",
 | 
					    "target": "ES2020",
 | 
				
			||||||
    "useDefineForClassFields": true,
 | 
					    "useDefineForClassFields": true,
 | 
				
			||||||
    "module": "ESNext",
 | 
					    "module": "ESNext",
 | 
				
			||||||
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
 | 
					    "lib": ["ES2020", "ES2015", "DOM", "DOM.Iterable"],
 | 
				
			||||||
    "skipLibCheck": true,
 | 
					    "skipLibCheck": true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Bundler mode */
 | 
					    /* Bundler mode */
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user