From b0b0fa7a037354d6f6adcd628667ba7a22be79d8 Mon Sep 17 00:00:00 2001 From: Hiro Date: Thu, 25 Dec 2025 20:01:12 -0600 Subject: [PATCH] Last login tracking for internal --- packages/api/src/auth/auth.controller.ts | 4 +++- packages/api/src/user/user.controller.ts | 1 + packages/api/src/user/user.entity.ts | 5 ++++- packages/api/src/user/user.service.ts | 4 ++++ packages/app/src/pages/Settings.tsx | 5 +++++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/api/src/auth/auth.controller.ts b/packages/api/src/auth/auth.controller.ts index 910faf3..17cf971 100644 --- a/packages/api/src/auth/auth.controller.ts +++ b/packages/api/src/auth/auth.controller.ts @@ -29,6 +29,7 @@ export class AuthController { } req.session.user = { id: user.id, first: user.first }; + await this.userService.updateUser(user.id, { lastLoggedIn: new Date() }); return user; } @@ -40,7 +41,7 @@ export class AuthController { throw new BadRequest("Email Already Exists"); } - const user = await this.userService.createUser({ first, last, email, password }); + const user = await this.userService.createUser({ first, last, email, password, lastLoggedIn: new Date() }); req.session.user = { id: user.id, first: user.first }; sendToWebhook({ @@ -62,6 +63,7 @@ export class AuthController { if (!controlled) throw new Unauthorized("Account not found."); req.session.user = { id: controlled.id, first: user.first, isControlled: true }; + await this.userService.updateUser(controlled.id, { lastLoggedIn: new Date() }); return controlled; } diff --git a/packages/api/src/user/user.controller.ts b/packages/api/src/user/user.controller.ts index b2907dd..fc58c9f 100644 --- a/packages/api/src/user/user.controller.ts +++ b/packages/api/src/user/user.controller.ts @@ -15,6 +15,7 @@ export class UserController { @Get() async getUser({ session }: Request): Promise { + await this.userService.touchLastLoggedIn(session.user.id); return this.userService.getUserById(session.user.id); } diff --git a/packages/api/src/user/user.entity.ts b/packages/api/src/user/user.entity.ts index 0d5a76d..5777f8b 100644 --- a/packages/api/src/user/user.entity.ts +++ b/packages/api/src/user/user.entity.ts @@ -1,7 +1,7 @@ import { Entity, Model, Prop } from "@/_lib/mongoose"; import bcrypt from "bcrypt"; -@Entity() +@Entity({ timestamps: true }) export class User extends Model { id: string; @@ -23,4 +23,7 @@ export class User extends Model { @Prop({ type: Boolean, default: false }) synced: boolean; + + @Prop({ type: Date, default: null }) + lastLoggedIn?: Date; } diff --git a/packages/api/src/user/user.service.ts b/packages/api/src/user/user.service.ts index d773a4f..a203230 100644 --- a/packages/api/src/user/user.service.ts +++ b/packages/api/src/user/user.service.ts @@ -23,6 +23,10 @@ export class UserService { return User.findOne({ email }).lean().exec(); } + async touchLastLoggedIn(id: string): Promise { + await User.findByIdAndUpdate(id, { lastLoggedIn: new Date() }).exec(); + } + async updateUser(id: string, data: Partial): Promise { return User.findByIdAndUpdate(id, data).lean().exec(); } diff --git a/packages/app/src/pages/Settings.tsx b/packages/app/src/pages/Settings.tsx index 054b2fe..7a15764 100644 --- a/packages/app/src/pages/Settings.tsx +++ b/packages/app/src/pages/Settings.tsx @@ -281,6 +281,11 @@ export default function SettingsPage() {

Profile

Manage your account details and privacy.

+ {user.isSuccess && user.data?.lastLoggedIn && ( + + Last logged in: {new Date(user.data.lastLoggedIn).toLocaleString()} + + )}
{user.isLoading && Loading...}