diff --git a/pkg/server/accounts_api.go b/pkg/server/accounts_api.go index 014133d..399462d 100644 --- a/pkg/server/accounts_api.go +++ b/pkg/server/accounts_api.go @@ -55,6 +55,20 @@ func getEvents(c *fiber.Ctx) error { }) } +func killSession(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + id, _ := c.ParamsInt("sessionId", 0) + + if err := database.C.Delete(&models.AuthSession{}, &models.AuthSession{ + BaseModel: models.BaseModel{ID: uint(id)}, + AccountID: user.ID, + }).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + return c.SendStatus(fiber.StatusOK) +} + func doRegister(c *fiber.Ctx) error { var data struct { Name string `json:"name"` diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 2b0fe0f..6c61f3e 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -26,6 +26,7 @@ func NewServer() { { api.Get("/users/me", auth, getPrincipal) api.Get("/users/me/events", auth, getEvents) + api.Delete("/users/me/sessions/:sessionId", auth, killSession) api.Post("/users", doRegister) api.Post("/users/me/confirm", doRegisterConfirm) diff --git a/view/src/pages/dashboard.tsx b/view/src/pages/dashboard.tsx index 66fe5b1..b1c91e1 100644 --- a/view/src/pages/dashboard.tsx +++ b/view/src/pages/dashboard.tsx @@ -1,4 +1,4 @@ -import { getAtk, useUserinfo } from "../stores/userinfo.tsx"; +import { getAtk, readProfiles, useUserinfo } from "../stores/userinfo.tsx"; import { createSignal, For, Show } from "solid-js"; export default function DashboardPage() { @@ -20,6 +20,7 @@ export default function DashboardPage() { const [eventCount, setEventCount] = createSignal(0); const [error, setError] = createSignal(null); + const [submitting, setSubmitting] = createSignal(false); async function readEvents() { const res = await fetch("/api/users/me/events?take=10", { @@ -34,6 +35,21 @@ export default function DashboardPage() { } } + async function killSession(item: any) { + setSubmitting(true); + const res = await fetch(`/api/users/me/sessions/${item.id}`, { + method: "DELETE", + headers: { Authorization: `Bearer ${getAtk()}` } + }); + if (res.status !== 200) { + setError(await res.text()); + } else { + await readProfiles(); + setError(null); + } + setSubmitting(false); + } + readEvents(); return ( @@ -160,6 +176,7 @@ export default function DashboardPage() { Third Client Audiences Date + @@ -169,6 +186,16 @@ export default function DashboardPage() { {item.client_id ? "Linked" : "Non-linked"} {item.audiences?.join(", ")} {new Date(item.created_at).toLocaleString()} + + + } diff --git a/view/src/stores/userinfo.tsx b/view/src/stores/userinfo.tsx index 20bd54b..eaa0ce7 100644 --- a/view/src/stores/userinfo.tsx +++ b/view/src/stores/userinfo.tsx @@ -36,7 +36,7 @@ export async function refreshAtk() { }) }); if (res.status !== 200) { - throw new Error(await res.text()); + console.error(await res.text()) } else { const data = await res.json(); new Cookie().set("access_token", data["access_token"], { path: "/" }); @@ -48,7 +48,7 @@ function checkLoggedIn(): boolean { return new Cookie().get("access_token"); } -export async function readProfiles() { +export async function readProfiles(recovering = true) { if (!checkLoggedIn()) return; const res = await fetch("/api/users/me", { @@ -56,9 +56,14 @@ export async function readProfiles() { }); if (res.status !== 200) { - // Auto retry after refresh access token - await refreshAtk(); - return await readProfiles(); + if (recovering) { + // Auto retry after refresh access token + await refreshAtk(); + return await readProfiles(false); + } else { + clearUserinfo(); + window.location.reload(); + } } const data = await res.json();