182 lines
5.4 KiB
JavaScript
182 lines
5.4 KiB
JavaScript
const fs = require('fs')
|
||
const path = require('path')
|
||
const router = require('express').Router()
|
||
|
||
const timer = (time = 100) => (req, res, next) => setTimeout(next, time)
|
||
|
||
const dataDir = path.join(__dirname, 'data')
|
||
|
||
const readJson = (fileName) => {
|
||
const filePath = path.join(dataDir, fileName)
|
||
const content = fs.readFileSync(filePath, 'utf-8')
|
||
return JSON.parse(content)
|
||
}
|
||
|
||
const sendNotFound = (res, message) => {
|
||
res.status(404).json({ error: { message }, data: null })
|
||
}
|
||
|
||
router.use(timer())
|
||
router.use((req, res, next) => {
|
||
res.type('application/json')
|
||
next()
|
||
})
|
||
|
||
// Challenge API endpoints
|
||
router.post('/challenge/auth', (req, res) => {
|
||
res.json(readJson('auth.json'))
|
||
})
|
||
|
||
router.get('/challenge/chains', (req, res) => {
|
||
res.json(readJson('chains.json'))
|
||
})
|
||
|
||
router.get('/challenge/chain/:id', (req, res) => {
|
||
const data = readJson('chains.json')
|
||
const chains = data.body || data
|
||
const chain = chains.find((item) => item.id === req.params.id || item._id === req.params.id)
|
||
if (!chain) {
|
||
return sendNotFound(res, `Цепочка ${req.params.id} не найдена`)
|
||
}
|
||
return res.json({ success: true, body: chain })
|
||
})
|
||
|
||
router.get('/challenge/task/:id', (req, res) => {
|
||
const data = readJson('chains.json')
|
||
const chains = data.body || data
|
||
const task = chains
|
||
.flatMap((chain) => chain.tasks || [])
|
||
.find((item) => item.id === req.params.id || item._id === req.params.id)
|
||
|
||
if (!task) {
|
||
return sendNotFound(res, `Задание ${req.params.id} не найдено`)
|
||
}
|
||
|
||
return res.json({ success: true, body: task })
|
||
})
|
||
|
||
router.post('/challenge/submit', (req, res) => {
|
||
const response = readJson('submit.json')
|
||
const queueId = response.body?.queueId
|
||
|
||
if (queueId) {
|
||
queueBehaviors[queueId] = queueBehaviors[queueId] ?? { nextFailure: false, attemptNumber: 0 }
|
||
queueStates[queueId] = {
|
||
position: 3,
|
||
initialPosition: 3,
|
||
pollCount: 0,
|
||
startTime: Date.now(),
|
||
}
|
||
}
|
||
|
||
res.json(response)
|
||
})
|
||
|
||
// Храним состояние очереди для каждого queueId
|
||
const queueStates = {}
|
||
const queueBehaviors = {}
|
||
|
||
router.get('/challenge/check-status/:queueId', (req, res) => {
|
||
const queueId = req.params.queueId
|
||
|
||
// Инициализируем состояние очереди, если его нет
|
||
if (!queueStates[queueId]) {
|
||
queueStates[queueId] = {
|
||
position: 3,
|
||
initialPosition: 3,
|
||
pollCount: 0,
|
||
startTime: Date.now()
|
||
}
|
||
}
|
||
|
||
const state = queueStates[queueId]
|
||
state.pollCount++
|
||
|
||
// Симулируем движение в очереди
|
||
// Каждый запрос уменьшаем позицию (быстрее)
|
||
if (state.pollCount >= 1 && state.position > 0) {
|
||
state.position--
|
||
state.pollCount = 0
|
||
}
|
||
|
||
// Если позиция 0, переходим к проверке
|
||
if (state.position === 0 && state.pollCount >= 1) {
|
||
const behavior = queueBehaviors[queueId] ?? { nextFailure: false, attemptNumber: 0 }
|
||
const attemptNumber = behavior.attemptNumber + 1
|
||
behavior.attemptNumber = attemptNumber
|
||
const shouldFail = behavior.nextFailure
|
||
behavior.nextFailure = !shouldFail
|
||
|
||
const baseSubmission = {
|
||
_id: `submission-${queueId}-${attemptNumber}`,
|
||
id: `submission-${queueId}-${attemptNumber}`,
|
||
user: 'user-frontend-001',
|
||
task: 'task-html-intro',
|
||
result: '<html><head></head><body><h1>Hello</h1></body></html>',
|
||
queueId,
|
||
submittedAt: new Date(state.startTime).toISOString(),
|
||
checkedAt: new Date().toISOString(),
|
||
attemptNumber,
|
||
}
|
||
|
||
const submission = shouldFail
|
||
? {
|
||
...baseSubmission,
|
||
status: 'needs_revision',
|
||
feedback: 'Добавьте описание внутри <section> и поясните, зачем нужен заголовок.',
|
||
}
|
||
: {
|
||
...baseSubmission,
|
||
status: 'accepted',
|
||
feedback: 'Отличная работа! Теперь можно двигаться дальше.',
|
||
}
|
||
|
||
delete queueStates[queueId]
|
||
|
||
return res.json({
|
||
success: true,
|
||
body: {
|
||
status: 'completed',
|
||
position: 0,
|
||
submission,
|
||
},
|
||
})
|
||
}
|
||
|
||
// Возвращаем текущее состояние очереди с начальной позицией для расчёта прогресса
|
||
const status = state.position > 0
|
||
? { status: 'waiting', position: state.position, initialPosition: state.initialPosition }
|
||
: { status: 'in_progress', position: 0, initialPosition: state.initialPosition }
|
||
|
||
return res.json({ success: true, body: status })
|
||
})
|
||
|
||
router.get('/challenge/user/:userId/stats', (req, res) => {
|
||
const data = readJson('user-stats.json')
|
||
const statsMap = data.body || data
|
||
const stats = statsMap[req.params.userId]
|
||
|
||
if (!stats) {
|
||
return sendNotFound(res, `Статистика пользователя ${req.params.userId} не найдена`)
|
||
}
|
||
|
||
return res.json({ success: true, body: stats })
|
||
})
|
||
|
||
router.get('/challenge/user/:userId/submissions', (req, res) => {
|
||
const data = readJson('user-submissions.json')
|
||
const submissionsMap = data.body || data
|
||
const submissions = submissionsMap[req.params.userId] || []
|
||
return res.json({ success: true, body: submissions })
|
||
})
|
||
|
||
router.get('/challenge/stats', (req, res) => {
|
||
res.json(readJson('system-stats.json'))
|
||
})
|
||
|
||
router.get('/challenge/submissions', (req, res) => {
|
||
res.json(readJson('submissions.json'))
|
||
})
|
||
|
||
module.exports = router
|