Files
challenge-pl/stubs/api/index.js

182 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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