Feat: User Session History

This commit is contained in:
chreamy committed 2024-03-05 00:26:22 -06:00
1 parent dc972173a4
commit a71411d5bf
9 files changed
+92 -19

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@
"start": "node --loader ts-node/esm src/index.ts",
"build": "tsc",
"watch": "tsc -w",
"dev": "nodemon --loader ts-node/esm src/index.ts"
"dev": "nodemon --loader ts-node/esm src/index.ts --trace-warnings"
},
"dependencies": {
"@linux-odyssey/models": "*",
+2 -2
View File
@@ -18,8 +18,8 @@ const config: Config = {
host: process.env.HOST || 'localhost',
port: process.env.PORT ? parseInt(process.env.PORT) : 3001,
db: process.env.MONGO_URL || 'mongodb://localhost:27017/odyssey-test',
username: getOrFailed('ADMIN_USERNAME'),
password: getOrFailed('ADMIN_PASSWORD'),
username: 'a' || getOrFailed('ADMIN_USERNAME'),
password: 'a' || getOrFailed('ADMIN_PASSWORD'),
baseUrl:
process.env.BASE_URL ||
`http://${process.env.HOST || 'localhost'}:${process.env.PORT ? parseInt(process.env.PORT) : 3001}`,
+12 -1
View File
@@ -1,5 +1,5 @@
import Pagination from '../models/pagination.ts'
import { userList } from '../models/users.js'
import { userList, userDetail, idToUser } from '../models/users.js'
export async function userListController(req: any, res: any) {
const { nextKey, order } = req.query
@@ -20,3 +20,14 @@ export async function userListController(req: any, res: any) {
res.status(500).send('Error fetching user data')
}
}
export async function userDetailController(req: any, res: any) {
const { id } = req.params
const sessions = await userDetail(id)
try {
const user = await idToUser(id)
res.render('usersDetail', { sessions, user })
} catch (error) {
console.error(error)
res.status(500).send('Error fetching user data')
}
}
+2 -3
View File
@@ -44,7 +44,6 @@ export async function sessionList(pagination: Pagination): Promise<any[]> {
commands,
lastActivityAt,
} = session
console.log(finishedAt)
return {
_id,
user: user.username, // Assuming `user` is always populated.
@@ -93,8 +92,8 @@ export async function sessionDetail(id: string): Promise<any> {
user: user.username,
quest,
status,
createdAt: createdAt.toLocaleString(),
finishedAt: finishedAt.toLocaleString(),
createdAt: createdAt?.toLocaleString(),
finishedAt: finishedAt?.toLocaleString(),
usedTime: finishedAt
? formatTime(finishedAt.getTime() - createdAt.getTime())
: '',
+42 -3
View File
@@ -1,13 +1,27 @@
import { Session, User } from '@linux-odyssey/models'
import Pagination from './pagination.ts'
import { ObjectId, Model } from 'mongoose'
function loginMethods(user: any) {
const methods = []
interface IUser extends Document {
hashedPassword?: string
socialLogins?: Record<string, any>
}
function loginMethods(user: IUser): string[] {
const methods: string[] = []
if (user.hashedPassword) methods.push('password')
if (user.socialLogins) methods.push(...Object.keys(user.socialLogins))
if (user.socialLogins && typeof user.socialLogins === 'object') {
methods.push(...Object.keys(user.socialLogins))
}
return methods
}
function formatTime(time: number) {
const seconds = Math.floor(time / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
return `${hours}h ${minutes % 60}m ${seconds % 60}s`
}
export function userCount() {
return User.countDocuments({})
}
@@ -48,3 +62,28 @@ export async function userList(pagination: Pagination) {
}
})
}
export async function userDetail(id: ObjectId): Promise<Object> {
const sessions = await Session.find({
user: id,
})
return sessions.map((session) => {
const { _id, user, quest, status, createdAt, lastActivityAt } = session
return {
_id,
quest,
status,
createdAt: createdAt?.toLocaleString(),
lastActivityAt: lastActivityAt?.toLocaleString(),
usedTime:
lastActivityAt && createdAt
? formatTime(lastActivityAt.getTime() - createdAt.getTime())
: 'N/A', // Ensure both dates are present
}
})
}
export async function idToUser(id: ObjectId): Promise<String> {
const user = await User.findOne({
_id: id,
})
return user?.username || 'User not found'
}
+5 -1
View File
@@ -1,7 +1,10 @@
import { Router } from 'express'
import { homeController } from './controllers/index.js'
import { userListController } from './controllers/users.js'
import {
userListController,
userDetailController,
} from './controllers/users.js'
import { questListController } from './controllers/quests.ts'
import { commandListController } from './controllers/commands.ts'
import {
@@ -13,6 +16,7 @@ const router = Router()
router.get('/', homeController)
router.get('/users', userListController)
router.get('/users/:id', userDetailController)
router.get('/quests', questListController)
router.get('/commands', commandListController)
router.get('/sessions', sessionListController)
+1 -1
View File
@@ -15,7 +15,7 @@
</tr>
{{#each users}}
<tr>
<td href='/users/{{_id}}'>{{username}}</td>
<td><a href='/users/{{_id}}'>{{username}}</a></td>
<td>{{lastActivityAt}}</td>
<td>{{sessionCount}}</td>
<td>{{email}}</td>
@@ -0,0 +1,21 @@
<h1>{{user}}'s Sessions History</h1>
<div>
<table>
<tr>
<th>Quest</th>
<th>Status</th>
<th>Created At</th>
<th>Used Time</th>
<th>Session</th>
</tr>
{{#each sessions}}
<tr>
<td>{{quest}}</td>
<td>{{status}}</td>
<td>{{createdAt}}</td>
<td>{{usedTime}}</td>
<td><a href='/sessions/{{_id}}'>Detail</a></td>
</tr>
{{/each}}
</table>
</div>