✨ User info
This commit is contained in:
parent
4f33b9e0f6
commit
9b5500305f
@ -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) {
|
||||||
return (
|
const [ready, setReady] = createSignal(false);
|
||||||
<div>
|
|
||||||
<Navbar />
|
|
||||||
|
|
||||||
<main class="h-[calc(100vh-68px)]">{props.children}</main>
|
readProfiles().then(() => setReady(true));
|
||||||
</div>
|
|
||||||
|
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 {
|
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">
|
||||||
<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>
|
||||||
</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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user