From 202e0b7635ba96f9d6bdf0376e1226d9389e21ad Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 17 May 2023 15:25:25 +0200 Subject: [PATCH] accounts page, list all sessions --- apps/login/app/(login)/accounts/page.tsx | 92 ++++++++++++++++++++++++ apps/login/app/(login)/password/page.tsx | 31 ++------ apps/login/lib/zitadel.ts | 11 +++ apps/login/package.json | 1 + apps/login/ui/Avatar.tsx | 10 ++- apps/login/ui/PasswordForm.tsx | 2 +- apps/login/ui/UserAvatar.tsx | 9 ++- apps/login/utils/cookies.ts | 26 +++++-- pnpm-lock.yaml | 6 ++ 9 files changed, 152 insertions(+), 36 deletions(-) create mode 100644 apps/login/app/(login)/accounts/page.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx new file mode 100644 index 00000000000..07f330926d2 --- /dev/null +++ b/apps/login/app/(login)/accounts/page.tsx @@ -0,0 +1,92 @@ +import { listSessions, server } from "#/lib/zitadel"; +import { Avatar, AvatarSize } from "#/ui/Avatar"; +import { getAllSessionIds } from "#/utils/cookies"; +import { + ChevronRightIcon, + ExclamationTriangleIcon, + XCircleIcon, +} from "@heroicons/react/24/outline"; +import moment from "moment"; +import Link from "next/link"; + +async function loadSessions() { + const ids = await getAllSessionIds().catch((error) => { + console.log("err", error); + }); + + if (ids && ids.length) { + return listSessions( + server, + ids.filter((id: string | undefined) => !!id) + ).then((sessions) => { + console.log("ss", sessions.sessions); + return sessions; + }); + } else { + return []; + } +} + +export default async function Page() { + const { sessions } = await loadSessions(); + + return ( +
+

Accounts

+

Use your ZITADEL Account

+ +
+ {sessions ? ( + sessions.map((session: any) => { + return ( + +
+ +
+ +
+ {session.factors.user.displayName} + + {session.factors.user.loginName} + + {session.factors.password?.verifiedAt && ( + + {moment( + new Date(session.factors.password.verifiedAt) + ).fromNow()} + + )} +
+ + +
+ {session.factors.password?.verifiedAt ? ( +
+ ) : ( +
+ )} + + +
+ + ); + }) + ) : ( +
+ + No Sessions available! +
+ )} +
+
+ ); +} diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index c6a36ea1ca3..277fea63e33 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -5,45 +5,28 @@ import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; async function loadSession(loginName: string) { const recent = await getMostRecentCookieWithLoginname(loginName); - console.log("found recent cookie: ", recent); return getSession(server, recent.id, recent.token).then(({ session }) => { - console.log(session); + console.log("ss", session); return session; }); - // const res = await fetch( - // `http://localhost:3000/session?` + - // new URLSearchParams({ - // loginName: loginName, - // }), - // { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // } - // ); - - // if (!res.ok) { - // throw new Error("Failed to load session"); - // } - - // return res.json(); } export default async function Page({ searchParams }: { searchParams: any }) { const { loginName } = searchParams; - console.log(loginName); const sessionFactors = await loadSession(loginName); - console.log(sessionFactors); return (
-

{sessionFactors.factors.user.displayName}

+

{sessionFactors.factors?.user?.displayName ?? "Password"}

Enter your password.

- +
diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 6311d3ef6a6..440dbbf22de 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -110,6 +110,17 @@ export function getSession( return sessionService.getSession({ sessionId, sessionToken }, {}); } +export function listSessions( + server: ZitadelServer, + ids: string[] +): Promise { + const sessionService = session.getSession(server); + const query = { offset: 0, limit: 100, asc: true }; + console.log(ids); + const queries = [{ idsQuery: { ids } }]; + return sessionService.listSessions({ queries: queries }, {}); +} + export type AddHumanUserData = { firstName: string; lastName: string; diff --git a/apps/login/package.json b/apps/login/package.json index 4d2d4067987..68d92b2991e 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -27,6 +27,7 @@ "@zitadel/server": "workspace:*", "clsx": "1.2.1", "date-fns": "2.29.3", + "moment": "^2.29.4", "next": "13.4.2", "next-themes": "^0.2.1", "nice-grpc": "2.0.1", diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 25778cdc5eb..8f69f31e0b8 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -26,10 +26,16 @@ export const Avatar: FC = ({ // const { resolvedTheme } = useTheme(); let credentials = ""; + console.log(name, loginName); if (name) { const split = name.split(" "); - const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : ""); - credentials = initials; + if (split) { + const initials = + split[0].charAt(0) + (split[1] ? split[1].charAt(0) : ""); + credentials = initials; + } else { + return name.charAt(0); + } } else { const username = loginName.split("@")[0]; let separator = "_"; diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx index e57ba884852..e46114ff609 100644 --- a/apps/login/ui/PasswordForm.tsx +++ b/apps/login/ui/PasswordForm.tsx @@ -46,7 +46,7 @@ export default function PasswordForm() { function submitPasswordAndContinue(value: Inputs): Promise { console.log(value); return submitPassword(value).then((resp: any) => { - return router.push(`/success`); + return router.push(`/accounts`); }); } diff --git a/apps/login/ui/UserAvatar.tsx b/apps/login/ui/UserAvatar.tsx index d64679367f5..332de5feadd 100644 --- a/apps/login/ui/UserAvatar.tsx +++ b/apps/login/ui/UserAvatar.tsx @@ -4,16 +4,21 @@ import Link from "next/link"; type Props = { loginName: string; + displayName?: string; showDropdown: boolean; }; -export default function UserAvatar({ loginName, showDropdown }: Props) { +export default function UserAvatar({ + loginName, + displayName, + showDropdown, +}: Props) { return (
diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 3dad52bb67e..45579193efe 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -100,8 +100,6 @@ export async function removeSessionFromCookie( export async function getMostRecentSessionCookie(): Promise { const cookiesList = cookies(); - // const hasSessions = cookiesList.has("sessions"); - // if (hasSessions) { const stringifiedCookie = cookiesList.get("sessions"); if (stringifiedCookie?.value) { @@ -119,13 +117,27 @@ export async function getMostRecentSessionCookie(): Promise { } else { return Promise.reject(); } - // } else { - // return Promise.reject(); - // } } +export async function getAllSessionIds(): Promise { + const cookiesList = cookies(); + const stringifiedCookie = cookiesList.get("sessions"); + + if (stringifiedCookie?.value) { + const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); + return sessions.map((session) => session.id); + } else { + return Promise.reject(); + } +} + +/** + * Returns most recent session filtered by optinal loginName + * @param loginName + * @returns most recent session + */ export async function getMostRecentCookieWithLoginname( - loginName: string + loginName?: string ): Promise { const cookiesList = cookies(); @@ -135,7 +147,7 @@ export async function getMostRecentCookieWithLoginname( const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); const latest = sessions - .filter((cookie) => cookie.loginName === loginName) + .filter((cookie) => (loginName ? cookie.loginName === loginName : true)) .reduce((prev, current) => { return new Date(prev.changeDate).getTime() > new Date(current.changeDate).getTime() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81a52d0d98c..ce8e8b92306 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,7 @@ importers: grpc-tools: 1.11.3 lint-staged: 13.0.3 make-dir-cli: 3.0.0 + moment: ^2.29.4 next: 13.4.2 next-themes: ^0.2.1 nice-grpc: 2.0.1 @@ -65,6 +66,7 @@ importers: '@zitadel/server': link:../../packages/zitadel-server clsx: 1.2.1 date-fns: 2.29.3 + moment: 2.29.4 next: 13.4.2_krg7tz6h6n3fx3eq7tclunioeu next-themes: 0.2.1_cmp7sjki5xcmfyvhcokzzink7a nice-grpc: 2.0.1 @@ -3581,6 +3583,10 @@ packages: hasBin: true dev: true + /moment/2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false