Files
registry/ts/models/session.ts

133 lines
3.0 KiB
TypeScript
Raw Normal View History

/**
* Session model for Stack.Gallery Registry
*/
import * as plugins from '../plugins.ts';
import type { ISession } from '../interfaces/auth.interfaces.ts';
import { db } from './db.ts';
@plugins.smartdata.Collection(() => db)
export class Session extends plugins.smartdata.SmartDataDbDoc<Session, Session> implements ISession {
@plugins.smartdata.unI()
public id: string = '';
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public userId: string = '';
@plugins.smartdata.svDb()
public userAgent: string = '';
@plugins.smartdata.svDb()
public ipAddress: string = '';
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public isValid: boolean = true;
@plugins.smartdata.svDb()
public invalidatedAt?: Date;
@plugins.smartdata.svDb()
public invalidatedReason?: string;
@plugins.smartdata.svDb()
public lastActivityAt: Date = new Date();
@plugins.smartdata.svDb()
@plugins.smartdata.index()
public createdAt: Date = new Date();
/**
* Create a new session
*/
public static async createSession(data: {
userId: string;
userAgent: string;
ipAddress: string;
}): Promise<Session> {
const session = new Session();
session.id = await Session.getNewId();
session.userId = data.userId;
session.userAgent = data.userAgent;
session.ipAddress = data.ipAddress;
session.isValid = true;
session.lastActivityAt = new Date();
session.createdAt = new Date();
await session.save();
return session;
}
/**
* Find valid session by ID
*/
public static async findValidSession(sessionId: string): Promise<Session | null> {
const session = await Session.getInstance({
id: sessionId,
isValid: true,
});
if (!session) return null;
// Check if session is expired (7 days)
const maxAge = 7 * 24 * 60 * 60 * 1000;
if (Date.now() - session.createdAt.getTime() > maxAge) {
await session.invalidate('expired');
return null;
}
return session;
}
/**
* Get all valid sessions for a user
*/
public static async getUserSessions(userId: string): Promise<Session[]> {
return await Session.getInstances({
userId,
isValid: true,
});
}
/**
* Invalidate all sessions for a user
*/
public static async invalidateAllUserSessions(
userId: string,
reason: string = 'logout_all'
): Promise<number> {
const sessions = await Session.getUserSessions(userId);
for (const session of sessions) {
await session.invalidate(reason);
}
return sessions.length;
}
/**
* Invalidate this session
*/
public async invalidate(reason: string): Promise<void> {
this.isValid = false;
this.invalidatedAt = new Date();
this.invalidatedReason = reason;
await this.save();
}
/**
* Update last activity
*/
public async touchActivity(): Promise<void> {
this.lastActivityAt = new Date();
await this.save();
}
/**
* Lifecycle hook
*/
public async beforeSave(): Promise<void> {
if (!this.id) {
this.id = await Session.getNewId();
}
}
}