v1.8.0 #429
@@ -2358,8 +2358,9 @@ router.delete("/sessions/:sessionId", authenticateJWT, async (req, res) => {
|
|||||||
operation: "session_revoke",
|
operation: "session_revoke",
|
||||||
sessionId,
|
sessionId,
|
||||||
revokedBy: userId,
|
revokedBy: userId,
|
||||||
|
sessionUserId: session.userId,
|
||||||
});
|
});
|
||||||
res.json({ message: "Session revoked successfully" });
|
res.json({ success: true, message: "Session revoked successfully" });
|
||||||
} else {
|
} else {
|
||||||
res.status(500).json({ error: "Failed to revoke session" });
|
res.status(500).json({ error: "Failed to revoke session" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,6 +267,11 @@ class AuthManager {
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (sessionRecords.length === 0) {
|
if (sessionRecords.length === 0) {
|
||||||
|
databaseLogger.warn("Session not found during JWT verification", {
|
||||||
|
operation: "jwt_verify_session_not_found",
|
||||||
|
sessionId: payload.sessionId,
|
||||||
|
userId: payload.userId,
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
@@ -278,6 +283,7 @@ class AuthManager {
|
|||||||
sessionId: payload.sessionId,
|
sessionId: payload.sessionId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
@@ -299,6 +305,22 @@ class AuthManager {
|
|||||||
try {
|
try {
|
||||||
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
await db.delete(sessions).where(eq(sessions.id, sessionId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { saveMemoryDatabaseToFile } = await import(
|
||||||
|
"../database/db/index.js"
|
||||||
|
);
|
||||||
|
await saveMemoryDatabaseToFile();
|
||||||
|
} catch (saveError) {
|
||||||
|
databaseLogger.error(
|
||||||
|
"Failed to save database after session revocation",
|
||||||
|
saveError,
|
||||||
|
{
|
||||||
|
operation: "session_revoke_db_save_failed",
|
||||||
|
sessionId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseLogger.error("Failed to delete session", error, {
|
databaseLogger.error("Failed to delete session", error, {
|
||||||
@@ -336,6 +358,22 @@ class AuthManager {
|
|||||||
await db.delete(sessions).where(eq(sessions.userId, userId));
|
await db.delete(sessions).where(eq(sessions.userId, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { saveMemoryDatabaseToFile } = await import(
|
||||||
|
"../database/db/index.js"
|
||||||
|
);
|
||||||
|
await saveMemoryDatabaseToFile();
|
||||||
|
} catch (saveError) {
|
||||||
|
databaseLogger.error(
|
||||||
|
"Failed to save database after revoking all user sessions",
|
||||||
|
saveError,
|
||||||
|
{
|
||||||
|
operation: "user_sessions_revoke_db_save_failed",
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return deletedCount;
|
return deletedCount;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseLogger.error("Failed to delete user sessions", error, {
|
databaseLogger.error("Failed to delete user sessions", error, {
|
||||||
@@ -440,6 +478,11 @@ class AuthManager {
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (sessionRecords.length === 0) {
|
if (sessionRecords.length === 0) {
|
||||||
|
databaseLogger.warn("Session not found in middleware", {
|
||||||
|
operation: "middleware_session_not_found",
|
||||||
|
sessionId: payload.sessionId,
|
||||||
|
userId: payload.userId,
|
||||||
|
});
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
error: "Session not found",
|
error: "Session not found",
|
||||||
code: "SESSION_NOT_FOUND",
|
code: "SESSION_NOT_FOUND",
|
||||||
@@ -479,10 +522,11 @@ class AuthManager {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseLogger.error("Session check failed", error, {
|
databaseLogger.error("Session check failed in middleware", error, {
|
||||||
operation: "session_check_failed",
|
operation: "middleware_session_check_failed",
|
||||||
sessionId: payload.sessionId,
|
sessionId: payload.sessionId,
|
||||||
});
|
});
|
||||||
|
return res.status(500).json({ error: "Session check failed" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import { Auth } from "@/ui/mobile/authentication/Auth.tsx";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Toaster } from "@/components/ui/sonner.tsx";
|
import { Toaster } from "@/components/ui/sonner.tsx";
|
||||||
|
|
||||||
|
function isReactNativeWebView(): boolean {
|
||||||
|
return typeof window !== "undefined" && !!(window as any).ReactNativeWebView;
|
||||||
|
}
|
||||||
|
|
||||||
const AppContent: FC = () => {
|
const AppContent: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { tabs, currentTab, getTab } = useTabs();
|
const { tabs, currentTab, getTab } = useTabs();
|
||||||
@@ -114,7 +118,7 @@ const AppContent: FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated || isReactNativeWebView()) {
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen flex items-center justify-center bg-dark-bg p-4">
|
<div className="h-screen w-screen flex items-center justify-center bg-dark-bg p-4">
|
||||||
<Auth
|
<Auth
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ export function Auth({
|
|||||||
|
|
||||||
const [meRes] = await Promise.all([getUserInfo()]);
|
const [meRes] = await Promise.all([getUserInfo()]);
|
||||||
|
|
||||||
setLoggedIn(true);
|
|
||||||
setIsAdmin(!!meRes.is_admin);
|
setIsAdmin(!!meRes.is_admin);
|
||||||
setUsername(meRes.username || null);
|
setUsername(meRes.username || null);
|
||||||
setUserId(meRes.userId || null);
|
setUserId(meRes.userId || null);
|
||||||
@@ -252,6 +251,7 @@ export function Auth({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoggedIn(true);
|
||||||
onAuthSuccess({
|
onAuthSuccess({
|
||||||
isAdmin: !!meRes.is_admin,
|
isAdmin: !!meRes.is_admin,
|
||||||
username: meRes.username || null,
|
username: meRes.username || null,
|
||||||
@@ -411,7 +411,6 @@ export function Auth({
|
|||||||
localStorage.setItem("jwt", res.token);
|
localStorage.setItem("jwt", res.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoggedIn(true);
|
|
||||||
setIsAdmin(!!res.is_admin);
|
setIsAdmin(!!res.is_admin);
|
||||||
setUsername(res.username || null);
|
setUsername(res.username || null);
|
||||||
setUserId(res.userId || null);
|
setUserId(res.userId || null);
|
||||||
@@ -425,6 +424,7 @@ export function Auth({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoggedIn(true);
|
||||||
onAuthSuccess({
|
onAuthSuccess({
|
||||||
isAdmin: !!res.is_admin,
|
isAdmin: !!res.is_admin,
|
||||||
username: res.username || null,
|
username: res.username || null,
|
||||||
@@ -506,7 +506,6 @@ export function Auth({
|
|||||||
|
|
||||||
getUserInfo()
|
getUserInfo()
|
||||||
.then((meRes) => {
|
.then((meRes) => {
|
||||||
setLoggedIn(true);
|
|
||||||
setIsAdmin(!!meRes.is_admin);
|
setIsAdmin(!!meRes.is_admin);
|
||||||
setUsername(meRes.username || null);
|
setUsername(meRes.username || null);
|
||||||
setUserId(meRes.userId || null);
|
setUserId(meRes.userId || null);
|
||||||
@@ -524,6 +523,7 @@ export function Auth({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoggedIn(true);
|
||||||
onAuthSuccess({
|
onAuthSuccess({
|
||||||
isAdmin: !!meRes.is_admin,
|
isAdmin: !!meRes.is_admin,
|
||||||
username: meRes.username || null,
|
username: meRes.username || null,
|
||||||
@@ -578,21 +578,14 @@ export function Auth({
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
if (isReactNativeWebView() && mobileAuthSuccess) {
|
||||||
<div
|
return (
|
||||||
className={`w-full max-w-md flex flex-col bg-dark-bg overflow-y-auto my-2 ${className || ""}`}
|
<div
|
||||||
style={{ maxHeight: "calc(100vh - 1rem)" }}
|
className={`w-full max-w-md flex flex-col bg-dark-bg overflow-y-auto my-2 ${className || ""}`}
|
||||||
{...props}
|
style={{ maxHeight: "calc(100vh - 1rem)" }}
|
||||||
>
|
{...props}
|
||||||
{isReactNativeWebView() && !mobileAuthSuccess && (
|
>
|
||||||
<Alert className="mb-4 border-blue-500 bg-blue-500/10">
|
<div className="flex flex-col items-center justify-center min-h-[400px] gap-4 px-4">
|
||||||
<Smartphone className="h-4 w-4" />
|
|
||||||
<AlertTitle>{t("auth.mobileApp")}</AlertTitle>
|
|
||||||
<AlertDescription>{t("auth.loggingInToMobileApp")}</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{isReactNativeWebView() && mobileAuthSuccess && (
|
|
||||||
<div className="flex flex-col items-center justify-center h-64 gap-4">
|
|
||||||
<div className="w-16 h-16 rounded-full bg-green-500/20 flex items-center justify-center">
|
<div className="w-16 h-16 rounded-full bg-green-500/20 flex items-center justify-center">
|
||||||
<svg
|
<svg
|
||||||
className="w-10 h-10 text-green-500"
|
className="w-10 h-10 text-green-500"
|
||||||
@@ -617,6 +610,22 @@ export function Auth({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`w-full max-w-md flex flex-col bg-dark-bg overflow-y-auto my-2 ${className || ""}`}
|
||||||
|
style={{ maxHeight: "calc(100vh - 1rem)" }}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{isReactNativeWebView() && !mobileAuthSuccess && (
|
||||||
|
<Alert className="mb-4 border-blue-500 bg-blue-500/10">
|
||||||
|
<Smartphone className="h-4 w-4" />
|
||||||
|
<AlertTitle>{t("auth.mobileApp")}</AlertTitle>
|
||||||
|
<AlertDescription>{t("auth.loggingInToMobileApp")}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{!mobileAuthSuccess && error && (
|
{!mobileAuthSuccess && error && (
|
||||||
<Alert variant="destructive" className="mb-4">
|
<Alert variant="destructive" className="mb-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user