From 601c29af36480854d9485fe706eb80a3f4688a7a Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Sun, 27 Sep 2026 15:49:07 +0330 Subject: [PATCH] logout from other sessions --- .../Areas/User/Pages/MySessions.cshtml | 32 +++++++++++++++++++ .../Areas/User/Pages/MySessions.cshtml.cs | 28 ++++++++++++++++ RMuseum/RMuseum.csproj | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/GanjooRazor/Areas/User/Pages/MySessions.cshtml b/GanjooRazor/Areas/User/Pages/MySessions.cshtml index 48d6a128..13a7ebc5 100644 --- a/GanjooRazor/Areas/User/Pages/MySessions.cshtml +++ b/GanjooRazor/Areas/User/Pages/MySessions.cshtml @@ -1,6 +1,7 @@ @page @model GanjooRazor.Areas.User.Pages.MySessionsModel @using DNTPersianUtils.Core +@using System.Linq @{ Layout = "_UserPanelLayout"; ViewData["Title"] = "نشست‌های من"; @@ -10,6 +11,14 @@

@ViewData["Title"]

دستگاه‌ها و مرورگرهایی که هم‌اکنون با حساب کاربری شما وارد گنجور شده‌اند. اگر دستگاهی را نمی‌شناسید یا دیگر از آن استفاده نمی‌کنید، آن را از این فهرست خارج کنید.
+@if (Model.LastError == "" && Model.Sessions != null && Model.Sessions.Count(s => s.Id != Model.CurrentSessionId) > 0) +{ +
+ + خروج از سایر دستگاه‌ها + +
+} @if (Model.LastError != "") {
@Model.LastError
@@ -42,6 +51,29 @@ else } }); } + + async function logoutOtherSessions() { + var ok = await upConfirm('با این کار از حساب کاربری خود در تمام دستگاه‌ها و مرورگرهای دیگر (به‌جز همین یکی) خارج خواهید شد. آیا ادامه می‌دهید؟'); + if (!ok) return; + + $.ajax({ + type: "DELETE", + url: '?handler=LogoutOthers', + success: function (res) { + var removedCount = (res && res.removedCount) || 0; + $('.up-list-item').each(function () { + if (this.id !== 'session-@Model.CurrentSessionId') { + $(this).remove(); + } + }); + $('#logout-others-btn').closest('div').remove(); + upToast(removedCount > 0 ? 'از ' + removedCount.toLocaleString('fa-IR') + ' دستگاه دیگر خارج شدید.' : 'دستگاه دیگری برای خروج یافت نشد.', 'success'); + }, + error: function (e) { + upToast(e.responseText || 'خروج از سایر دستگاه‌ها با خطا مواجه شد.', 'error'); + } + }); + } @if (Model.Sessions.Count == 0) diff --git a/GanjooRazor/Areas/User/Pages/MySessions.cshtml.cs b/GanjooRazor/Areas/User/Pages/MySessions.cshtml.cs index 22737a9e..4aceaf63 100644 --- a/GanjooRazor/Areas/User/Pages/MySessions.cshtml.cs +++ b/GanjooRazor/Areas/User/Pages/MySessions.cshtml.cs @@ -121,5 +121,33 @@ namespace GanjooRazor.Areas.User.Pages return new JsonResult(new { loggedOutSelf }); } + + /// + /// Logs the user out of every session but this one (a critical-account cleanup shortcut + /// for someone logged in on several computers, instead of removing each session one at a + /// time). This browser's own session is always the one preserved - it never removes the + /// session the request itself is authenticated with, so unlike OnDeleteSessionAsync there + /// is no "logged out myself" case to special-case here. + /// + public async Task OnDeleteLogoutOthersAsync() + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + { + var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/users/delothersessions?userId={Request.Cookies["UserId"]}"); + if (response.StatusCode != HttpStatusCode.OK) + { + return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); + } + int removedCount = int.Parse(await response.Content.ReadAsStringAsync()); + return new JsonResult(new { removedCount }); + } + else + { + return new BadRequestObjectResult(NotLoggedInMessage); + } + } + } } } diff --git a/RMuseum/RMuseum.csproj b/RMuseum/RMuseum.csproj index 8b7da4de..02a97d34 100644 --- a/RMuseum/RMuseum.csproj +++ b/RMuseum/RMuseum.csproj @@ -29,7 +29,7 @@ - +