mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-08 11:30:12 +08:00
convert analytics to TS
This commit is contained in:
1 parent
9509bbc355
commit
ece0ec7c41
25 files changed
+11595
-661
No files matched your search
+1
-1
@@ -77,7 +77,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
ports:
|
ports:
|
||||||
- '27017:27017'
|
- '27018:27017'
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data/db
|
- ./data:/data/db
|
||||||
|
|
||||||
|
|||||||
Generated
+10802
File diff suppressed because it is too large.
Load diff
+4
-1
@@ -50,5 +50,8 @@
|
|||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.1.0"
|
"packageManager": "yarn@4.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^16.4.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ext": "js,handlebars"
|
"watch": ["src"],
|
||||||
|
"ext": "ts,handlebars,js",
|
||||||
|
"ignore": ["src/**/*.spec.ts"],
|
||||||
|
"exec": "node --loader ts-node/esm src/index.ts"
|
||||||
}
|
}
|
||||||
@@ -6,10 +6,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "GPL",
|
"license": "GPL",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node --loader ts-node/esm src/index.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"watch": "tsc -w",
|
"watch": "tsc -w",
|
||||||
"dev": "nodemon src/index.js"
|
"dev": "nodemon --loader ts-node/esm src/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@linux-odyssey/models": "*",
|
"@linux-odyssey/models": "*",
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
"mongoose": "^8.0.3"
|
"mongoose": "^8.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.0.1"
|
"@types/node": "^20.11.20",
|
||||||
|
"nodemon": "^3.1.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
function getOrFailed(key) {
|
|
||||||
if (!process.env[key]) {
|
|
||||||
throw new Error(`Missing required environment variable: ${key}`)
|
|
||||||
}
|
|
||||||
return process.env[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
host: process.env.HOST || 'localhost',
|
|
||||||
port: process.env.PORT || 3001,
|
|
||||||
db: process.env.MONGO_URL || 'mongodb://localhost:27017/odyssey-test',
|
|
||||||
username: getOrFailed('ADMIN_USERNAME'),
|
|
||||||
password: getOrFailed('ADMIN_PASSWORD'),
|
|
||||||
}
|
|
||||||
|
|
||||||
config.baseUrl = process.env.BASE_URL || `http://${config.host}:${config.port}`
|
|
||||||
|
|
||||||
export default config
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
function getOrFailed(key: string): string {
|
||||||
|
if (!process.env[key]) {
|
||||||
|
throw new Error(`Missing required environment variable: ${key}`)
|
||||||
|
}
|
||||||
|
return process.env[key] as string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
db: string
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
baseUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
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'),
|
||||||
|
username: 'wancat',
|
||||||
|
//password: getOrFailed('ADMIN_PASSWORD'),
|
||||||
|
password: 'InMatrix',
|
||||||
|
baseUrl:
|
||||||
|
process.env.BASE_URL ||
|
||||||
|
`http://${process.env.HOST || 'localhost'}:${process.env.PORT ? parseInt(process.env.PORT) : 3001}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
File renamed without changes.
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable object-shorthand */
|
/* eslint-disable object-shorthand */
|
||||||
import Pagination from '../models/pagination.js'
|
import Pagination from '../models/pagination.ts'
|
||||||
import { errorCommands } from '../models/commands.js'
|
import { errorCommands } from '../models/commands.js'
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export async function commandListController(req, res) {
|
export async function commandListController(req: any, res: any): Promise<void> {
|
||||||
const { nextKey, order } = req.query
|
const { nextKey, order } = req.query as { nextKey?: string; order: string }
|
||||||
const itemsPerPage = 100
|
const itemsPerPage = 100
|
||||||
|
|
||||||
try {
|
try {
|
||||||
+3
-4
@@ -1,8 +1,8 @@
|
|||||||
import { userCount } from '../models/users.js'
|
import { userCount } from '../models/users.js'
|
||||||
import { questList } from '../models/quests.js'
|
import { questList } from '../models/quests.ts'
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export async function homeController(req, res) {
|
export async function homeController(req: any, res: any) {
|
||||||
const quests = await questList()
|
const quests = await questList()
|
||||||
const [totalQuests, totalCompleted] = quests.reduce(
|
const [totalQuests, totalCompleted] = quests.reduce(
|
||||||
([total, completed], quest) => [
|
([total, completed], quest) => [
|
||||||
@@ -17,8 +17,7 @@ export async function homeController(req, res) {
|
|||||||
userCount: await userCount(),
|
userCount: await userCount(),
|
||||||
totalQuests,
|
totalQuests,
|
||||||
totalCompleted,
|
totalCompleted,
|
||||||
|
completedRate: Math.round((totalCompleted / totalQuests) * 10000) / 100,
|
||||||
}
|
}
|
||||||
payload.completedRate =
|
|
||||||
Math.round((payload.totalCompleted / payload.totalQuests) * 10000) / 100
|
|
||||||
res.render('home', payload)
|
res.render('home', payload)
|
||||||
}
|
}
|
||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
import { questList } from '../models/quests.js'
|
import { questList } from '../models/quests.ts'
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export async function questListController(req, res) {
|
export async function questListController(req: any, res: any) {
|
||||||
const quests = await questList()
|
const quests = await questList()
|
||||||
quests.forEach((quest) => {
|
quests.forEach((quest) => {
|
||||||
quest.failed = quest.started - quest.completed
|
quest.failed = quest.started - quest.completed
|
||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
import Pagination from '../models/pagination.js'
|
import Pagination from '../models/pagination.ts'
|
||||||
import { sessionList, sessionDetail } from '../models/sessions.js'
|
import { sessionList, sessionDetail } from '../models/sessions.ts'
|
||||||
|
import { Request, Response } from 'express'
|
||||||
export async function sessionListController(req, res) {
|
export async function sessionListController(req: any, res: any) {
|
||||||
const { nextKey, order } = req.query
|
const { nextKey, order } = req.query
|
||||||
const itemsPerPage = 50
|
const itemsPerPage = 50
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export async function sessionListController(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sessionDetailController(req, res) {
|
export async function sessionDetailController(req: any, res: any) {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const session = await sessionDetail(id)
|
const session = await sessionDetail(id)
|
||||||
res.render('sessionDetail', { session })
|
res.render('sessionDetail', { session })
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import Pagination from '../models/pagination.js'
|
import Pagination from '../models/pagination.ts'
|
||||||
import { userList } from '../models/users.js'
|
import { userList } from '../models/users.js'
|
||||||
|
|
||||||
export async function userListController(req, res) {
|
export async function userListController(req: any, res: any) {
|
||||||
const { nextKey, order } = req.query
|
const { nextKey, order } = req.query
|
||||||
const itemsPerPage = 50
|
const itemsPerPage = 50
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@ import connectDB from '@linux-odyssey/models'
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { engine } from 'express-handlebars'
|
import { engine } from 'express-handlebars'
|
||||||
import basicAuth from 'express-basic-auth'
|
import basicAuth from 'express-basic-auth'
|
||||||
import config from './config.js'
|
import config from './config.ts'
|
||||||
import router from './router.js'
|
import router from './router.ts'
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await connectDB(config.db)
|
await connectDB(config.db)
|
||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
import { Command } from '@linux-odyssey/models'
|
import { Command } from '@linux-odyssey/models'
|
||||||
|
import Pagination from './pagination.ts'
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export async function errorCommands(pagination) {
|
export async function errorCommands(pagination: Pagination) {
|
||||||
const commands = await Command.aggregate([
|
const commands = await Command.aggregate([
|
||||||
pagination.match('_id'),
|
pagination.match('_id'),
|
||||||
{
|
{
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/* eslint-disable lines-between-class-members */
|
|
||||||
/* eslint-disable class-methods-use-this */
|
|
||||||
// eslint-disable-next-line max-classes-per-file
|
|
||||||
import mongoose from 'mongoose'
|
|
||||||
|
|
||||||
export const DESC = 'desc'
|
|
||||||
export const ASC = 'asc'
|
|
||||||
|
|
||||||
class Asc {
|
|
||||||
getOperator() {
|
|
||||||
return '$gt'
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrder() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
getName() {
|
|
||||||
return ASC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Desc {
|
|
||||||
getOperator() {
|
|
||||||
return '$lt'
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrder() {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
getName() {
|
|
||||||
return DESC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Pagination {
|
|
||||||
nextKey = null
|
|
||||||
itemsPerPage = 50
|
|
||||||
order = null
|
|
||||||
|
|
||||||
constructor(order, itemsPerPage = 50, nextKey = null) {
|
|
||||||
this.itemsPerPage = itemsPerPage
|
|
||||||
if (!order || order === DESC) {
|
|
||||||
this.order = new Desc()
|
|
||||||
} else if (order === ASC) {
|
|
||||||
this.order = new Asc()
|
|
||||||
} else {
|
|
||||||
throw new Error('order must be asc or desc')
|
|
||||||
}
|
|
||||||
if (nextKey) {
|
|
||||||
this.nextKey = new mongoose.Types.ObjectId(nextKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match(field) {
|
|
||||||
const stage = this.nextKey
|
|
||||||
? { [field]: { [this.order.getOperator()]: this.nextKey } }
|
|
||||||
: {}
|
|
||||||
return { $match: stage }
|
|
||||||
}
|
|
||||||
|
|
||||||
sort(field) {
|
|
||||||
return { $sort: { [field]: this.order.getOrder() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
limit() {
|
|
||||||
return { $limit: this.itemsPerPage }
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrder() {
|
|
||||||
return this.order.getName()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/* eslint-disable lines-between-class-members */
|
||||||
|
/* eslint-disable class-methods-use-this */
|
||||||
|
// eslint-disable-next-line max-classes-per-file
|
||||||
|
import mongoose, { Types } from 'mongoose'
|
||||||
|
|
||||||
|
export const DESC: string = 'desc'
|
||||||
|
export const ASC: string = 'asc'
|
||||||
|
|
||||||
|
interface Order {
|
||||||
|
getOperator(): string
|
||||||
|
getOrder(): number
|
||||||
|
getName(): string
|
||||||
|
}
|
||||||
|
|
||||||
|
class Asc implements Order {
|
||||||
|
getOperator(): string {
|
||||||
|
return '$gt'
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrder(): number {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return ASC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Desc implements Order {
|
||||||
|
getOperator(): string {
|
||||||
|
return '$lt'
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrder(): 1 | -1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return DESC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Pagination {
|
||||||
|
nextKey: Types.ObjectId | null = null
|
||||||
|
itemsPerPage: number = 50
|
||||||
|
order: Order
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
order: string,
|
||||||
|
itemsPerPage: number = 50,
|
||||||
|
nextKey: string | null = null
|
||||||
|
) {
|
||||||
|
this.itemsPerPage = itemsPerPage
|
||||||
|
if (!order || order === DESC) {
|
||||||
|
this.order = new Desc()
|
||||||
|
} else if (order === ASC) {
|
||||||
|
this.order = new Asc()
|
||||||
|
} else {
|
||||||
|
throw new Error('order must be asc or desc')
|
||||||
|
}
|
||||||
|
if (nextKey) {
|
||||||
|
this.nextKey = new mongoose.Types.ObjectId(nextKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match(field: string) {
|
||||||
|
const stage: { [key: string]: { [key: string]: Types.ObjectId } } = this
|
||||||
|
.nextKey
|
||||||
|
? {
|
||||||
|
[field]: {
|
||||||
|
[this.order ? this.order.getOperator() : '$gt']: this.nextKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
return { $match: stage }
|
||||||
|
}
|
||||||
|
|
||||||
|
sort(field: string): { $sort: Record<string, 1 | -1> } {
|
||||||
|
return { $sort: { [field]: this.order.getOrder() as 1 | -1 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
limit() {
|
||||||
|
return { $limit: this.itemsPerPage }
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrder() {
|
||||||
|
return this.order.getName()
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -1,83 +0,0 @@
|
|||||||
import { Command, Session } from '@linux-odyssey/models'
|
|
||||||
|
|
||||||
function formatTime(time) {
|
|
||||||
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 async function sessionList(pagination) {
|
|
||||||
const sessions = await Session.aggregate([
|
|
||||||
pagination.match('_id'),
|
|
||||||
{
|
|
||||||
$lookup: {
|
|
||||||
from: 'users',
|
|
||||||
localField: 'user',
|
|
||||||
foreignField: '_id',
|
|
||||||
as: 'user',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ $unwind: '$user' },
|
|
||||||
{
|
|
||||||
$lookup: {
|
|
||||||
from: 'commands',
|
|
||||||
localField: '_id',
|
|
||||||
foreignField: 'session',
|
|
||||||
as: 'commands',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pagination.sort('_id'),
|
|
||||||
pagination.limit(),
|
|
||||||
])
|
|
||||||
|
|
||||||
return sessions.map(
|
|
||||||
({
|
|
||||||
_id,
|
|
||||||
user,
|
|
||||||
quest,
|
|
||||||
status,
|
|
||||||
createdAt,
|
|
||||||
finishedAt,
|
|
||||||
commands,
|
|
||||||
lastActivityAt,
|
|
||||||
}) => {
|
|
||||||
return {
|
|
||||||
_id,
|
|
||||||
user: user.username,
|
|
||||||
quest,
|
|
||||||
status,
|
|
||||||
createdAt: createdAt?.toLocaleString(),
|
|
||||||
finishedAt: finishedAt?.toLocaleString(),
|
|
||||||
lastActivityAt: lastActivityAt?.toLocaleString(),
|
|
||||||
usedTime: formatTime(lastActivityAt - createdAt),
|
|
||||||
commandCount: commands?.length,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sessionDetail(id) {
|
|
||||||
const session = await Session.findById(id).populate('user').exec()
|
|
||||||
const commands = (await Command.find({ session: id })).map(
|
|
||||||
({ command, pwd, output, error, createdAt, stage }) => ({
|
|
||||||
command: command?.slice(0, 20),
|
|
||||||
pwd,
|
|
||||||
stage,
|
|
||||||
output: output?.slice(0, 50),
|
|
||||||
error: error?.slice(0, 50),
|
|
||||||
createdAt: createdAt?.toLocaleString(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const { _id, user, quest, status, createdAt, finishedAt } = session
|
|
||||||
return {
|
|
||||||
_id,
|
|
||||||
user: user.username,
|
|
||||||
quest,
|
|
||||||
status,
|
|
||||||
createdAt: createdAt?.toLocaleString(),
|
|
||||||
finishedAt: finishedAt?.toLocaleString(),
|
|
||||||
usedTime: finishedAt ? formatTime(finishedAt - createdAt) : '',
|
|
||||||
commands,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { Command, Session } from '@linux-odyssey/models'
|
||||||
|
import Pagination from './pagination.ts'
|
||||||
|
import { Types } from 'mongoose'
|
||||||
|
|
||||||
|
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 async function sessionList(pagination: Pagination): Promise<any[]> {
|
||||||
|
const sessions = await Session.aggregate([
|
||||||
|
pagination.match('_id'),
|
||||||
|
{
|
||||||
|
$lookup: {
|
||||||
|
from: 'users',
|
||||||
|
localField: 'user',
|
||||||
|
foreignField: '_id',
|
||||||
|
as: 'user',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $unwind: '$user' },
|
||||||
|
{
|
||||||
|
$lookup: {
|
||||||
|
from: 'commands',
|
||||||
|
localField: '_id',
|
||||||
|
foreignField: 'session',
|
||||||
|
as: 'commands',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pagination.sort('_id'),
|
||||||
|
pagination.limit(),
|
||||||
|
])
|
||||||
|
|
||||||
|
return sessions.map((session) => {
|
||||||
|
const {
|
||||||
|
_id,
|
||||||
|
user,
|
||||||
|
quest,
|
||||||
|
status,
|
||||||
|
createdAt,
|
||||||
|
finishedAt,
|
||||||
|
commands,
|
||||||
|
lastActivityAt,
|
||||||
|
} = session
|
||||||
|
console.log(finishedAt)
|
||||||
|
return {
|
||||||
|
_id,
|
||||||
|
user: user.username, // Assuming `user` is always populated.
|
||||||
|
quest,
|
||||||
|
status,
|
||||||
|
createdAt: createdAt?.toLocaleString(),
|
||||||
|
finishedAt: finishedAt?.toLocaleString(),
|
||||||
|
lastActivityAt: lastActivityAt?.toLocaleString(),
|
||||||
|
usedTime:
|
||||||
|
lastActivityAt && createdAt
|
||||||
|
? formatTime(lastActivityAt.getTime() - createdAt.getTime())
|
||||||
|
: 'N/A', // Ensure both dates are present
|
||||||
|
commandCount: commands?.length || 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
interface User {
|
||||||
|
username: string
|
||||||
|
}
|
||||||
|
interface SessionDocument {
|
||||||
|
_id: Types.ObjectId
|
||||||
|
user: User
|
||||||
|
quest: string
|
||||||
|
status: string
|
||||||
|
createdAt: Date
|
||||||
|
finishedAt: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sessionDetail(id: string): Promise<any> {
|
||||||
|
const session = (await Session.findById(id)
|
||||||
|
.populate('user')
|
||||||
|
.exec()) as unknown as SessionDocument
|
||||||
|
const commands = (await Command.find({ session: id })).map(
|
||||||
|
({ command, pwd, output, error, createdAt, stage }) => ({
|
||||||
|
command: command?.slice(0, 20),
|
||||||
|
pwd,
|
||||||
|
stage,
|
||||||
|
output: output?.slice(0, 50),
|
||||||
|
error: error?.slice(0, 50),
|
||||||
|
createdAt: createdAt?.toLocaleString(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const { _id, user, quest, status, createdAt, finishedAt } = session
|
||||||
|
return {
|
||||||
|
_id,
|
||||||
|
user: user.username,
|
||||||
|
quest,
|
||||||
|
status,
|
||||||
|
createdAt: createdAt.toLocaleString(),
|
||||||
|
finishedAt: finishedAt.toLocaleString(),
|
||||||
|
usedTime: finishedAt
|
||||||
|
? formatTime(finishedAt.getTime() - createdAt.getTime())
|
||||||
|
: '',
|
||||||
|
commands,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Session, User } from '@linux-odyssey/models'
|
import { Session, User } from '@linux-odyssey/models'
|
||||||
|
import Pagination from './pagination.ts'
|
||||||
|
|
||||||
function loginMethods(user) {
|
function loginMethods(user: any) {
|
||||||
const methods = []
|
const methods = []
|
||||||
if (user.hashedPassword) methods.push('password')
|
if (user.hashedPassword) methods.push('password')
|
||||||
if (user.socialLogins) methods.push(...Object.keys(user.socialLogins))
|
if (user.socialLogins) methods.push(...Object.keys(user.socialLogins))
|
||||||
@@ -8,10 +9,10 @@ function loginMethods(user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function userCount() {
|
export function userCount() {
|
||||||
return User.find().count()
|
return User.countDocuments({})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function userList(pagination) {
|
export async function userList(pagination: Pagination) {
|
||||||
const users = await Session.aggregate([
|
const users = await Session.aggregate([
|
||||||
pagination.match('user'),
|
pagination.match('user'),
|
||||||
{
|
{
|
||||||
@@ -2,12 +2,12 @@ import { Router } from 'express'
|
|||||||
|
|
||||||
import { homeController } from './controllers/index.js'
|
import { homeController } from './controllers/index.js'
|
||||||
import { userListController } from './controllers/users.js'
|
import { userListController } from './controllers/users.js'
|
||||||
import { questListController } from './controllers/quests.js'
|
import { questListController } from './controllers/quests.ts'
|
||||||
import { commandListController } from './controllers/commands.js'
|
import { commandListController } from './controllers/commands.ts'
|
||||||
import {
|
import {
|
||||||
sessionDetailController,
|
sessionDetailController,
|
||||||
sessionListController,
|
sessionListController,
|
||||||
} from './controllers/sessions.js'
|
} from './controllers/sessions.ts'
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
@@ -2,7 +2,10 @@
|
|||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"outDir": "./dist"
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"allowImportingTsExtensions": true
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Docker from 'dockerode'
|
|||||||
import config, { getQuestImage } from '../config.js'
|
import config, { getQuestImage } from '../config.js'
|
||||||
import logger from '../utils/logger.js'
|
import logger from '../utils/logger.js'
|
||||||
|
|
||||||
const engine = new Docker()
|
const engine = new Docker({ socketPath: '//./pipe/docker_engine' })
|
||||||
|
|
||||||
const containerOptions = {
|
const containerOptions = {
|
||||||
AttachStdin: true,
|
AttachStdin: true,
|
||||||
@@ -16,6 +16,8 @@ const containerOptions = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const network = engine.getNetwork(config.dockerNetwork)
|
||||||
|
|
||||||
export function createContainer(name, questId) {
|
export function createContainer(name, questId) {
|
||||||
const option = {
|
const option = {
|
||||||
...containerOptions,
|
...containerOptions,
|
||||||
@@ -26,8 +28,8 @@ export function createContainer(name, questId) {
|
|||||||
if (!config.isProduction && hostPwd && mountQuest === questId) {
|
if (!config.isProduction && hostPwd && mountQuest === questId) {
|
||||||
logger.info('Mounting quest folder', mountQuest)
|
logger.info('Mounting quest folder', mountQuest)
|
||||||
option.HostConfig.Binds = [
|
option.HostConfig.Binds = [
|
||||||
`${hostPwd}/quests/${mountQuest}/home:/home/commander`,
|
`${hostPwd.replace(/\\/g, '/')}/quests/${mountQuest}/home:/home/commander`,
|
||||||
`${hostPwd}/packages/container:/usr/local/lib/container`,
|
`${hostPwd.replace(/\\/g, '/')}/packages/container:/usr/local/lib/container`,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return engine.createContainer(option)
|
return engine.createContainer(option)
|
||||||
@@ -54,7 +56,7 @@ export async function attachContainer(container, { token }) {
|
|||||||
Tty: true,
|
Tty: true,
|
||||||
Env: [
|
Env: [
|
||||||
`TOKEN=${token}`,
|
`TOKEN=${token}`,
|
||||||
`API_ENDPOINT=${config.backendUrl}`,
|
`API_ENDPOINT=http://backend:3000`,
|
||||||
'ZDOTDIR=/etc/zsh',
|
'ZDOTDIR=/etc/zsh',
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user