v1.8.0 #429
@@ -2358,8 +2358,9 @@ router.delete("/sessions/:sessionId", authenticateJWT, async (req, res) => {
|
||||
operation: "session_revoke",
|
||||
sessionId,
|
||||
revokedBy: userId,
|
||||
sessionUserId: session.userId,
|
||||
});
|
||||
res.json({ message: "Session revoked successfully" });
|
||||
res.json({ success: true, message: "Session revoked successfully" });
|
||||
} else {
|
||||
res.status(500).json({ error: "Failed to revoke session" });
|
||||
}
|
||||
|
||||
@@ -267,6 +267,11 @@ class AuthManager {
|
||||
.limit(1);
|
||||
|
||||
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;
|
||||
}
|
||||
} catch (dbError) {
|
||||
@@ -278,6 +283,7 @@ class AuthManager {
|
||||
sessionId: payload.sessionId,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
@@ -299,6 +305,22 @@ class AuthManager {
|
||||
try {
|
||||
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;
|
||||
} catch (error) {
|
||||
databaseLogger.error("Failed to delete session", error, {
|
||||
@@ -336,6 +358,22 @@ class AuthManager {
|
||||
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;
|
||||
} catch (error) {
|
||||
databaseLogger.error("Failed to delete user sessions", error, {
|
||||
@@ -440,6 +478,11 @@ class AuthManager {
|
||||
.limit(1);
|
||||
|
||||
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({
|
||||
error: "Session not found",
|
||||
code: "SESSION_NOT_FOUND",
|
||||
@@ -479,10 +522,11 @@ class AuthManager {
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
databaseLogger.error("Session check failed", error, {
|
||||
operation: "session_check_failed",
|
||||
databaseLogger.error("Session check failed in middleware", error, {
|
||||
operation: "middleware_session_check_failed",
|
||||
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 { Toaster } from "@/components/ui/sonner.tsx";
|
||||
|
||||
function isReactNativeWebView(): boolean {
|
||||
return typeof window !== "undefined" && !!(window as any).ReactNativeWebView;
|
||||
}
|
||||
|
||||
const AppContent: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { tabs, currentTab, getTab } = useTabs();
|
||||
@@ -114,7 +118,7 @@ const AppContent: FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
if (!isAuthenticated || isReactNativeWebView()) {
|
||||
return (
|
||||
<div className="h-screen w-screen flex items-center justify-center bg-dark-bg p-4">
|
||||
<Auth
|
||||
|
||||
@@ -239,7 +239,6 @@ export function Auth({
|
||||
|
||||
const [meRes] = await Promise.all([getUserInfo()]);
|
||||
|
||||
setLoggedIn(true);
|
||||
setIsAdmin(!!meRes.is_admin);
|
||||
setUsername(meRes.username || null);
|
||||
setUserId(meRes.userId || null);
|
||||
@@ -252,6 +251,7 @@ export function Auth({
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedIn(true);
|
||||
onAuthSuccess({
|
||||
isAdmin: !!meRes.is_admin,
|
||||
username: meRes.username || null,
|
||||
@@ -411,7 +411,6 @@ export function Auth({
|
||||
localStorage.setItem("jwt", res.token);
|
||||
}
|
||||
|
||||
setLoggedIn(true);
|
||||
setIsAdmin(!!res.is_admin);
|
||||
setUsername(res.username || null);
|
||||
setUserId(res.userId || null);
|
||||
@@ -425,6 +424,7 @@ export function Auth({
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedIn(true);
|
||||
onAuthSuccess({
|
||||
isAdmin: !!res.is_admin,
|
||||
username: res.username || null,
|
||||
@@ -506,7 +506,6 @@ export function Auth({
|
||||
|
||||
getUserInfo()
|
||||
.then((meRes) => {
|
||||
setLoggedIn(true);
|
||||
setIsAdmin(!!meRes.is_admin);
|
||||
setUsername(meRes.username || null);
|
||||
setUserId(meRes.userId || null);
|
||||
@@ -524,6 +523,7 @@ export function Auth({
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedIn(true);
|
||||
onAuthSuccess({
|
||||
isAdmin: !!meRes.is_admin,
|
||||
username: meRes.username || null,
|
||||
@@ -578,21 +578,14 @@ export function Auth({
|
||||
</svg>
|
||||
);
|
||||
|
||||
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>
|
||||
)}
|
||||
{isReactNativeWebView() && mobileAuthSuccess && (
|
||||
<div className="flex flex-col items-center justify-center h-64 gap-4">
|
||||
if (isReactNativeWebView() && mobileAuthSuccess) {
|
||||
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}
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px] gap-4 px-4">
|
||||
<div className="w-16 h-16 rounded-full bg-green-500/20 flex items-center justify-center">
|
||||
<svg
|
||||
className="w-10 h-10 text-green-500"
|
||||
@@ -617,6 +610,22 @@ export function Auth({
|
||||
</p>
|
||||
</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 && (
|
||||
<Alert variant="destructive" className="mb-4">
|
||||
|
||||
Reference in New Issue
Block a user