feat: add pending signal to status

This commit is contained in:
wancat committed 2023-10-29 17:42:31 +08:00
1 parent aec0cf84ad
commit d724f97403
11 files changed
+90 -30

No files matched your search

+8 -2
View File
@@ -15,6 +15,7 @@ class SessionManager {
}) })
) )
this.pwd = ref('') this.pwd = ref('')
this.status = ref('inactive')
} }
getSession() { getSession() {
@@ -26,6 +27,7 @@ class SessionManager {
this.session.value = session this.session.value = session
this.graph.value = new FileGraph(session.graph) this.graph.value = new FileGraph(session.graph)
this.hints.value = session.hints this.hints.value = session.hints
this.status.value = session.status
} }
handleGraphUpdate(event) { handleGraphUpdate(event) {
@@ -37,8 +39,12 @@ class SessionManager {
} }
} }
handleHintUpdate(event) { handleHintUpdate(hints) {
this.hints.value = [...this.hints.value, ...event] this.hints.value = [...this.hints.value, ...hints]
}
handleStatusUpdate(status) {
this.status.value = status
} }
setTasks(tasks) { setTasks(tasks) {
+11 -8
View File
@@ -1,5 +1,5 @@
<script setup> <script setup>
import { onMounted, ref } from 'vue' import { computed, onMounted } from 'vue'
import socket from '../utils/socket' import socket from '../utils/socket'
import sessionManager from '../utils/session' import sessionManager from '../utils/session'
import GameHeaderPart from '../components/GameHeaderPart.vue' import GameHeaderPart from '../components/GameHeaderPart.vue'
@@ -11,7 +11,9 @@ import VisualizationPart from '../components/VisualizationPart.vue'
import ControlPalette from '../components/ControlPalette.vue' import ControlPalette from '../components/ControlPalette.vue'
import CompleteModal from '../components/CompleteModal.vue' import CompleteModal from '../components/CompleteModal.vue'
const completed = ref(false) const completed = computed(() => {
return sessionManager.status.value === 'finished'
})
onMounted(async () => { onMounted(async () => {
await sessionManager.lastOrCreate() await sessionManager.lastOrCreate()
@@ -33,12 +35,7 @@ onMounted(async () => {
}) })
socket.on('message', console.log) socket.on('message', console.log)
socket.on('status', (event) => { socket.on('status', (event) => {
console.log('status', event) sessionManager.handleStatusUpdate(event)
if (event === 'finished') {
setTimeout(() => {
completed.value = true
}, 5000)
}
}) })
}) })
</script> </script>
@@ -48,6 +45,12 @@ onMounted(async () => {
<div class="h-[5vh] w-full"> <div class="h-[5vh] w-full">
<GameHeaderPart /> <GameHeaderPart />
</div> </div>
<!-- current status indicator -->
<input
type="hidden"
id="currentStatus"
:value="sessionManager.status.value"
/>
<!-- main --> <!-- main -->
<div id="main" class="h-[95vh] w-full flex p-3 space-x-3"> <div id="main" class="h-[95vh] w-full flex p-3 space-x-3">
<!-- Topic and Command List --> <!-- Topic and Command List -->
+1 -1
View File
@@ -36,7 +36,7 @@ services:
- BASE_URL=http://localhost:5173 - BASE_URL=http://localhost:5173
- MONGO_URL=mongodb://db:27017/odyssey-test - MONGO_URL=mongodb://db:27017/odyssey-test
- DOCKER_NETWORK=linux-odyssey-players - DOCKER_NETWORK=linux-odyssey-players
# - HOST_PWD=${PWD} - HOST_PWD=${PWD}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+17 -14
View File
@@ -2,6 +2,7 @@
const fs = require('fs').promises const fs = require('fs').promises
const { exit } = require('process') const { exit } = require('process')
const minimist = require('minimist') const minimist = require('minimist')
const axios = require('axios')
const { colorize, printResponses } = require('./print.js') const { colorize, printResponses } = require('./print.js')
const discoverFiles = require('./discover.js') const discoverFiles = require('./discover.js')
@@ -40,6 +41,13 @@ async function main() {
} = process.env } = process.env
if (!CMD_NAME) exit(0) if (!CMD_NAME) exit(0)
const api = axios.create({
baseURL: `${API_ENDPOINT}/api/v1`,
headers: {
Authorization: `Bearer ${TOKEN}`,
},
})
const additionalData = await handleCommand(CMD_NAME) const additionalData = await handleCommand(CMD_NAME)
const output = await readOrNone(CMD_OUTPUT_FILE) const output = await readOrNone(CMD_OUTPUT_FILE)
@@ -53,24 +61,19 @@ async function main() {
...additionalData, ...additionalData,
} }
try { try {
const res = await fetch(`${API_ENDPOINT}/api/v1/commands`, { const res = await api.post('/commands', payload)
method: 'POST', if (!res.data) return
headers: { const { responses, hints, end } = res.data
'Content-Type': 'application/json', if (responses) {
Authorization: `Bearer ${TOKEN}`, await printResponses(responses, 60)
}, await api.post('/commands/completed')
body: JSON.stringify(payload),
})
const data = await res.json()
if (data.responses) {
await printResponses(data.responses, 60)
} }
if (data.hints) { if (hints) {
for (const hint of data.hints) { for (const hint of hints) {
console.log(colorize(hint, 'yellow')) console.log(colorize(hint, 'yellow'))
} }
} }
if (data.end) { if (end) {
console.log(colorize('Quest completed!', 'blue')) console.log(colorize('Quest completed!', 'blue'))
} }
} catch (err) { } catch (err) {
+1 -1
View File
@@ -4,7 +4,7 @@
"main": "index.js", "main": "index.js",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chokidar": "^3.5.3", "axios": "^1.6.0",
"minimist": "^1.2.8" "minimist": "^1.2.8"
} }
} }
@@ -1,6 +1,8 @@
/* eslint-disable import/prefer-default-export */
import { Command, Session } from '@linux-odyssey/models' import { Command, Session } from '@linux-odyssey/models'
import CommandHandler from '../../game/commandHandler.js' import CommandHandler from '../../game/commandHandler.js'
import { pushToSession } from '../socket.js'
const commandCompleteCallbacks = new Map()
export async function newCommand(req, res) { export async function newCommand(req, res) {
console.log('new command:', req.body) console.log('new command:', req.body)
@@ -44,8 +46,35 @@ export async function newCommand(req, res) {
if (response) { if (response) {
c.stage = response.stage c.stage = response.stage
await c.save() await c.save()
commandCompleteCallbacks.set(session.id, response.callback)
pushToSession(session.id, 'status', 'pending')
} }
res.status(201).json(response) res.status(201).json(response)
await session.save() await session.save()
} }
export async function completedCommand(req, res) {
const sessionId = req.user.session_id
const session = await Session.findById(sessionId)
if (!session) {
res.status(404).json({ message: 'session not found' })
return
}
if (session.status === 'inactive') {
res.status(400).json({ message: 'session is not active' })
return
}
const callback = commandCompleteCallbacks.get(session.id)
if (!callback) {
res.status(400).json({ message: 'no callback found' })
return
}
callback()
commandCompleteCallbacks.delete(session.id)
res.json({ message: 'ok' })
}
@@ -44,6 +44,12 @@ export async function createSession(req, res) {
return return
} }
// deactive all active sessions
await Session.updateMany(
{ user: req.user._id, quest, status: 'active' },
{ status: 'inactive' }
)
try { try {
const container = await createContainer( const container = await createContainer(
`quest-${quest.id}-${req.user.username}-${Date.now()}` `quest-${quest.id}-${req.user.username}-${Date.now()}`
+1
View File
@@ -4,5 +4,6 @@ import * as commandController from '../controllers/commandController.js'
const router = Router() const router = Router()
router.post('/', commandController.newCommand) router.post('/', commandController.newCommand)
router.post('/completed', commandController.completedCommand)
export default router export default router
+1
View File
@@ -19,6 +19,7 @@ router.use('/sessions', authRequired, sessions)
router.use( router.use(
'/commands', '/commands',
passport.authenticate('jwt', { session: false }), passport.authenticate('jwt', { session: false }),
authRequired,
commands commands
) )
+5 -3
View File
@@ -34,7 +34,6 @@ export default class SessionHandler {
addNewTasks() { addNewTasks() {
const newTasks = this.getNewTasks() const newTasks = this.getNewTasks()
this.session.tasks.push(...newTasks) this.session.tasks.push(...newTasks)
pushToSession(this.session.id, 'tasks', this.session.tasks)
} }
execute(stage) { execute(stage) {
@@ -45,12 +44,10 @@ export default class SessionHandler {
}) })
this.session.hints.push(...stage.hints) this.session.hints.push(...stage.hints)
pushToSession(this.session.id, 'hints', stage.hints)
if (stage.id === 'END') { if (stage.id === 'END') {
this.session.status = 'finished' this.session.status = 'finished'
this.session.finishedAt = new Date() this.session.finishedAt = new Date()
pushToSession(this.session.id, 'status', 'finished')
} }
this.addNewTasks() this.addNewTasks()
@@ -58,6 +55,11 @@ export default class SessionHandler {
return { return {
responses: stage.responses, responses: stage.responses,
hints: stage.hints, hints: stage.hints,
callback: () => {
pushToSession(this.session.id, 'tasks', this.session.tasks)
pushToSession(this.session.id, 'hints', stage.hints)
pushToSession(this.session.id, 'status', this.session.status)
},
} }
} }
} }
+9
View File
@@ -1407,6 +1407,15 @@ axios@^1.5.0:
form-data "^4.0.0" form-data "^4.0.0"
proxy-from-env "^1.1.0" proxy-from-env "^1.1.0"
axios@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-jest@^29.6.1: babel-jest@^29.6.1:
version "29.6.1" version "29.6.1"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a"