From 803f1f0b81b7d4450a9240e92558969633295c0b Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:00:04 +0000 Subject: [PATCH] feat: user session management (#2056) * user session management * WIP * cleanup * license * cleanup * don't cache index * rename current device property * fix --- .../public/locales/en-US/translation.json | 17 +- .../components/activate-license-modal.tsx | 82 ++++++--- .../src/ee/licence/components/oss-details.tsx | 6 +- .../session/components/session-list.tsx | 165 ++++++++++++++++++ .../features/session/queries/session-query.ts | 55 ++++++ .../session/services/session-service.ts | 17 ++ .../features/session/types/session.types.ts | 8 + .../user/components/account-name-form.tsx | 10 +- .../settings/account/account-settings.tsx | 5 + apps/server/package.json | 1 + .../middlewares/audit-context.middleware.ts | 5 + apps/server/src/core/auth/auth.controller.ts | 25 ++- apps/server/src/core/auth/dto/jwt-payload.ts | 1 + .../src/core/auth/services/auth.service.ts | 25 ++- .../src/core/auth/services/token.service.ts | 3 +- .../src/core/auth/strategies/jwt.strategy.ts | 14 ++ apps/server/src/core/core.module.ts | 2 + .../core/session/dto/revoke-session.dto.ts | 7 + .../core/session/session-activity.service.ts | 36 ++++ .../src/core/session/session.controller.ts | 80 +++++++++ .../server/src/core/session/session.module.ts | 14 ++ .../src/core/session/session.service.ts | 127 ++++++++++++++ .../services/workspace-invitation.service.ts | 4 +- .../workspace/services/workspace.service.ts | 18 +- apps/server/src/database/database.module.ts | 3 + .../20260326T121350-user-sessions.ts | 45 +++++ .../repos/session/user-session.repo.ts | 162 +++++++++++++++++ apps/server/src/database/types/db.d.ts | 16 ++ .../server/src/database/types/entity.types.ts | 6 + apps/server/src/ee | 2 +- .../src/integrations/static/static.module.ts | 5 +- pnpm-lock.yaml | 11 +- 32 files changed, 928 insertions(+), 49 deletions(-) create mode 100644 apps/client/src/features/session/components/session-list.tsx create mode 100644 apps/client/src/features/session/queries/session-query.ts create mode 100644 apps/client/src/features/session/services/session-service.ts create mode 100644 apps/client/src/features/session/types/session.types.ts create mode 100644 apps/server/src/core/session/dto/revoke-session.dto.ts create mode 100644 apps/server/src/core/session/session-activity.service.ts create mode 100644 apps/server/src/core/session/session.controller.ts create mode 100644 apps/server/src/core/session/session.module.ts create mode 100644 apps/server/src/core/session/session.service.ts create mode 100644 apps/server/src/database/migrations/20260326T121350-user-sessions.ts create mode 100644 apps/server/src/database/repos/session/user-session.repo.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index f35ec179..98f8eefc 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -708,5 +708,20 @@ "Resend verification email": "Resend verification email", "Verification email sent. Please check your inbox.": "Verification email sent. Please check your inbox.", "Failed to resend verification email. Please try again.": "Failed to resend verification email. Please try again.", - "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces." + "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces.", + "Load more": "Load more", + "Log out of all devices": "Log out of all devices", + "Log out of all sessions except this device": "Log out of all sessions except this device", + "This Device": "This Device", + "Unknown device": "Unknown device", + "No active sessions": "No active sessions", + "Session revoked": "Session revoked", + "All other sessions revoked": "All other sessions revoked", + "Last used": "Last used", + "Created": "Created", + "Rename": "Rename", + "Publish": "Publish", + "Security": "Security", + "Enforce SSO": "Enforce SSO", + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password." } diff --git a/apps/client/src/ee/licence/components/activate-license-modal.tsx b/apps/client/src/ee/licence/components/activate-license-modal.tsx index 28b3d0d6..766d3f25 100644 --- a/apps/client/src/ee/licence/components/activate-license-modal.tsx +++ b/apps/client/src/ee/licence/components/activate-license-modal.tsx @@ -1,6 +1,6 @@ import { z } from "zod/v4"; -import React from "react"; -import { Button, Group, Modal, Textarea } from "@mantine/core"; +import React, { useRef } from "react"; +import { Button, Divider, Group, Modal, Stack, Textarea } from "@mantine/core"; import { useForm } from "@mantine/form"; import { zod4Resolver } from "mantine-form-zod-resolver"; import { useTranslation } from "react-i18next"; @@ -49,6 +49,7 @@ interface ActivateLicenseFormProps { export function ActivateLicenseForm({ onClose }: ActivateLicenseFormProps) { const { t } = useTranslation(); const activateLicenseMutation = useActivateMutation(); + const fileInputRef = useRef(null); const form = useForm({ validate: zod4Resolver(formSchema), @@ -63,29 +64,68 @@ export function ActivateLicenseForm({ onClose }: ActivateLicenseFormProps) { onClose?.(); } + function handleFileUpload(event: React.ChangeEvent) { + const file = event.target.files?.[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + const content = (e.target?.result as string)?.trim(); + if (content) { + form.setFieldValue("licenseKey", content); + handleSubmit({ licenseKey: content }); + } + }; + reader.readAsText(file); + + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + } + return (
-