✨ User info
This commit is contained in:
		@@ -28,6 +28,7 @@ var auth = keyauth.New(keyauth.Config{
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Locals("principal", user)
 | 
			
		||||
		c.Locals("permissions", user.Permissions.Data())
 | 
			
		||||
 | 
			
		||||
		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.Get("/users/me", auth, aboutMe)
 | 
			
		||||
		api.Post("/users", doRegister)
 | 
			
		||||
 | 
			
		||||
		api.Put("/auth", startChallenge)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,25 @@
 | 
			
		||||
import Navbar from "./shared/Navbar.tsx";
 | 
			
		||||
import { readProfiles } from "../stores/userinfo.ts";
 | 
			
		||||
import { createSignal, Show } from "solid-js";
 | 
			
		||||
 | 
			
		||||
export default function RootLayout(props: any) {
 | 
			
		||||
  const [ready, setReady] = createSignal(false);
 | 
			
		||||
 | 
			
		||||
  readProfiles().then(() => setReady(true));
 | 
			
		||||
 | 
			
		||||
  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>
 | 
			
		||||
        <Navbar />
 | 
			
		||||
 | 
			
		||||
        <main class="h-[calc(100vh-68px)]">{props.children}</main>
 | 
			
		||||
      </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 {
 | 
			
		||||
  label: string;
 | 
			
		||||
@@ -57,7 +58,9 @@ export default function Navbar() {
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="navbar-end pe-5">
 | 
			
		||||
        <Show when={!userinfo.isLoggedIn}>
 | 
			
		||||
          <a href="/auth/login" class="btn btn-sm btn-primary">Login</a>
 | 
			
		||||
        </Show>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { readProfiles } from "../../stores/userinfo.ts";
 | 
			
		||||
import { useNavigate } from "@solidjs/router";
 | 
			
		||||
import { createSignal, For, Match, Show, Switch } from "solid-js";
 | 
			
		||||
import Cookie from "universal-cookie";
 | 
			
		||||
@@ -85,6 +86,7 @@ export default function Login() {
 | 
			
		||||
        const data = await res.json();
 | 
			
		||||
        if (data["is_finished"]) {
 | 
			
		||||
          await grantToken(data["session"]["grant_token"]);
 | 
			
		||||
          await readProfiles();
 | 
			
		||||
          navigate("/");
 | 
			
		||||
        } else {
 | 
			
		||||
          setError(null);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
import { userinfo } from "../stores/userinfo.ts";
 | 
			
		||||
 | 
			
		||||
export default function Dashboard() {
 | 
			
		||||
  return (
 | 
			
		||||
    <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>
 | 
			
		||||
    </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",
 | 
			
		||||
    "useDefineForClassFields": true,
 | 
			
		||||
    "module": "ESNext",
 | 
			
		||||
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
 | 
			
		||||
    "lib": ["ES2020", "ES2015", "DOM", "DOM.Iterable"],
 | 
			
		||||
    "skipLibCheck": true,
 | 
			
		||||
 | 
			
		||||
    /* Bundler mode */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user