✨ 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) { | ||||
|   return ( | ||||
|     <div> | ||||
|       <Navbar /> | ||||
|   const [ready, setReady] = createSignal(false); | ||||
|  | ||||
|       <main class="h-[calc(100vh-68px)]">{props.children}</main> | ||||
|     </div> | ||||
|   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"> | ||||
|         <a href="/auth/login" class="btn btn-sm btn-primary">Login</a> | ||||
|         <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