Compare commits

..

2 Commits

Author SHA1 Message Date
f2ec0bce07 Merge branch 'master' into feat/freetracker-auth-stubs
Some checks failed
platform/multy-stub/pipeline/head There was a failure building this commit
2024-12-31 10:03:46 +03:00
b70ac7c183 Feat: freetracker auth stubs 2024-12-14 05:03:21 +03:00
1444 changed files with 2152 additions and 40670 deletions

Binary file not shown.

View File

@@ -1,9 +0,0 @@
# Application settings
TZ=Europe/Moscow
APP_PORT=8044
MONGO_INITDB_ROOT_USERNAME=qqq
MONGO_INITDB_ROOT_PASSWORD=qqq
# MongoDB connection string
MONGO_ADDR=mongodb://qqq:qqq@127.0.0.1:27018

View File

@@ -1,38 +1,16 @@
FROM node:22 AS builder
WORKDIR /usr/src/app/
# Сначала копируем только файлы, необходимые для установки зависимостей
COPY ./package.json /usr/src/app/package.json
COPY ./package-lock.json /usr/src/app/package-lock.json
# Устанавливаем все зависимости
RUN npm ci
# Затем копируем исходный код проекта и файлы конфигурации
COPY ./tsconfig.json /usr/src/app/tsconfig.json
COPY ./server /usr/src/app/server
# Сборка проекта
RUN npm run build
# Вторая стадия - рабочий образ
FROM node:22
FROM node:20
RUN mkdir -p /usr/src/app/server/log/
WORKDIR /usr/src/app/
# Копирование только package.json/package-lock.json для продакшн зависимостей
COPY ./server /usr/src/app/server
COPY ./package.json /usr/src/app/package.json
COPY ./package-lock.json /usr/src/app/package-lock.json
COPY ./.serverrc.js /usr/src/app/.serverrc.js
# COPY ./.env /usr/src/app/.env
# Установка только продакшн зависимостей
RUN npm ci --production
# Копирование собранного приложения из билдера
COPY --from=builder /usr/src/app/dist /usr/src/app/dist
COPY --from=builder /usr/src/app/server /usr/src/app/server
# RUN npm i --omit=dev
RUN npm ci
EXPOSE 8044
CMD ["npm", "run", "up:prod"]

View File

@@ -1,12 +1,6 @@
#!/bin/sh
docker stop ms-mongo
docker volume remove ms_volume8
docker volume create ms_volume8
docker run --rm \
-v ms_volume8:/data/db \
--name ms-mongo \
-p 27018:27017 \
-e MONGO_INITDB_ROOT_USERNAME=qqq \
-e MONGO_INITDB_ROOT_PASSWORD=qqq \
-d mongo:8.0.3
docker volume remove ms_volume
docker volume create ms_volume
docker run --rm -v ms_volume:/data/db --name ms-mongo -p 27017:27017 -d mongo:8.0.3

25
docker-compose.yaml Normal file
View File

@@ -0,0 +1,25 @@
version: "3"
volumes:
ms_volume8:
ms_logs:
services:
mongoDb:
image: mongo:8.0.3
volumes:
- ms_volume8:/data/db
restart: always
# ports:
# - 27017:27017
multy-stubs:
# build: .
image: bro.js/ms/bh:$TAG
restart: always
volumes:
- ms_logs:/usr/src/app/server/log
ports:
- 8044:8044
environment:
- TZ=Europe/Moscow
- MONGO_ADDR=mongodb

View File

@@ -1,30 +0,0 @@
version: "3"
volumes:
ms_volume8:
ms_logs:
services:
multy-stubs:
image: bro.js/ms/bh:$TAG
restart: always
volumes:
- ms_logs:/usr/src/app/server/log
ports:
- 8044:8044
environment:
- TZ=Europe/Moscow
- MONGO_ADDR=${MONGO_ADDR}
# depends_on:
# mongoDb:
# condition: service_started
# mongoDb:
# image: mongo:8.0.3
# volumes:
# - ms_volume8:/data/db
# restart: always
# environment:
# - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
# - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
# ports:
# - 27018:27017

View File

@@ -4,7 +4,7 @@ import pluginJs from "@eslint/js";
export default [
{ ignores: ['server/routers/old/*'] },
{ files: ["**/*.js"], languageOptions: { } },
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
{ languageOptions: { globals: globals.node } },
pluginJs.configs.recommended,
{

4835
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,15 @@
{
"name": "multi-stub",
"version": "2.0.0",
"version": "1.0.1",
"description": "",
"main": "server/index.ts",
"type": "commonjs",
"main": "index.js",
"scripts": {
"start": "cross-env NODE_ENV=\"development\" ts-node-dev .",
"build": "tsc",
"up:prod": "node dist/server/index.js",
"start": "cross-env PORT=8033 npx nodemon ./server",
"up:prod": "cross-env NODE_ENV=\"production\" node ./server",
"deploy:d:stop": "docker compose down",
"deploy:d:build": "docker compose build",
"deploy:d:up": "docker compose up -d",
"redeploy": "npm run deploy:d:stop && npm run deploy:d:build && npm run deploy:d:up",
"eslint": "npx eslint ./server",
"eslint:fix": "npx eslint ./server --fix",
"test": "jest"
@@ -21,36 +23,25 @@
"license": "MIT",
"homepage": "https://bitbucket.org/online-mentor/multi-stub#readme",
"dependencies": {
"@langchain/community": "^0.3.56",
"@langchain/core": "^0.3.77",
"@langchain/langgraph": "^0.4.9",
"ai": "^4.1.13",
"axios": "^1.7.7",
"bcrypt": "^5.1.0",
"bcryptjs": "^3.0.3",
"body-parser": "^1.19.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"axios": "^1.7.9",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.3",
"cookie-parser": "^1.4.7",
"cross-env": "^7.0.3",
"crypto-js": "^4.2.0",
"dotenv": "^16.4.7",
"express": "5.0.1",
"express-jwt": "^8.5.1",
"express-session": "^1.18.1",
"gigachat": "^0.0.16",
"jsdom": "^25.0.1",
"jsonwebtoken": "^9.0.2",
"langchain": "^0.3.34",
"langchain-gigachat": "^0.0.14",
"mongodb": "^6.20.0",
"mongoose": "^8.18.2",
"mongoose-sequence": "^6.0.1",
"morgan": "^1.10.1",
"multer": "^1.4.5-lts.1",
"mongodb": "^6.12.0",
"mongoose": "^8.9.2",
"morgan": "^1.10.0",
"pbkdf2-password": "^1.2.1",
"rotating-file-stream": "^3.2.5",
"socket.io": "^4.8.1",
"zod": "^3.24.3"
"uuid": "^11.0.3"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
@@ -60,8 +51,6 @@
"jest": "^29.7.0",
"mockingoose": "^2.16.2",
"nodemon": "3.1.9",
"supertest": "^7.0.0",
"ts-node-dev": "2.0.0",
"typescript": "5.7.3"
"supertest": "^7.0.0"
}
}

View File

@@ -1,87 +0,0 @@
## Правила оформления студенческих бэкендов в `multi-stub`
Этот документ описывает, как подключать новый студенческий бэкенд к общему серверу и как работать с JSONзаглушками. Правила написаны так, чтобы их мог автоматически выполнять помощник Cursor.
### 1. Общая структура проекта студента
- **Размещение проекта**
- Каждый студенческий бэкенд живёт в своей подпапке в `server/routers/<project-name>`.
- В корне подпапки должен быть основной файл роутера `index.js` (или `index.ts`), который экспортирует `express.Router()`.
- Подключение к общему серверу выполняется в `server/index.ts` через импорт и `app.use(<mountPath>, <router>)`.
- **Использование JSONзаглушек**
- Если проект переносится из фронтенд‑репозитория и должен только отдавать данные, то в подпапке проекта должна быть папка `json/` со всеми нужными `.json` файлами.
- HTTPобработчики в роутере могут просто читать и возвращать содержимое этих файлов (например, через `require('./json/...')` или `import data from './json/...json'` с включённым `resolveJsonModule` / соответствующей конфигурацией bundler'а).
### 2. Правила для Cursor при указании директории заглушек
Когда пользователь явно указывает директорию с заглушками (например: `server/routers/<project-name>/json`), помощник Cursor должен последовательно выполнить следующие шаги.
- **2.1. Проверка валидности импортов JSONфайлов**
- Найти все `.js` / `.ts` файлы внутри подпапки проекта.
- В каждом таком файле найти импорты/require, которые ссылаются на `.json` файлы (относительные пути вроде `'./json/.../file.json'`).
- Для каждого такого импорта:
- **Проверить, что файл реально существует** по указанному пути относительно файла-импортёра.
- **Проверить расширение**: путь должен заканчиваться на `.json` (без опечаток).
- **Проверить регистр и точное совпадение имени файла** (важно для кросс‑платформенности, даже если локально используется Windows).
- Если найдены ошибки (файл не существует, опечатка в имени, неправильный относительный путь и т.п.):
- Сформировать понятный список проблем: в каком файле, какая строка/импорт и что именно не так.
- Предложить автоматически исправить пути (если по контексту можно однозначно угадать нужный `*.json` файл).
- **2.2. Проверка подключения основного роутера проекта**
- Определить основной файл роутера проекта:
- По умолчанию это `server/routers/<project-name>/index.js` (или `index.ts`).
- Открыть `server/index.ts` и убедиться, что:
- Есть импорт роутера из соответствующей подпапки, например:
- `import <SomeUniqueName>Router from './routers/<project-name>'`
- или `const <SomeUniqueName>Router = require('./routers/<project-name>')`
- Имя переменной роутера **уникально** среди всех импортов роутеров (нет другого импорта с таким же именем).
- Есть вызов `app.use('<mount-path>', <SomeUniqueName>Router)`:
- `<mount-path>` должен быть осмысленным, совпадать с названием проекта или оговариваться пользователем.
- Если импорт или `app.use` отсутствуют:
- Сформировать предложение по добавлению корректного импорта и `app.use(...)`.
- Убедиться, что используемое имя роутера не конфликтует с уже существующими.
- Если обнаружен конфликт имён:
- Предложить переименовать новый роутер в уникальное имя и обновить соответствующие места в `server/index.ts`.
### 3. Предложение «оживить» JSONзаглушки
После того как проверка импортов и подключения роутера завершена, помощник Cursor должен **задать пользователю вопрос**, не хочет ли он превратить заглушки в полноценный бэкенд.
- **3.1. Формулировка предложения**
- Спросить у пользователя примерно так:
- «Обнаружены JSONзаглушки в директории `<указанная-папка>`. Хотите, чтобы я попытался автоматически:
1) построить модели данных (mongooseсхемы) на основе структуры JSON;
2) создать CRUDэндпоинты и/или более сложные маршруты, опираясь на существующие данные;
3) заменить прямую отдачу `*.json` файлов на работу через базу данных?»
- **3.2. Поведение при согласии пользователя**
- Проанализировать структуру JSONфайлов:
- Определить основные сущности и поля.
- Выделить типы полей (строки, числа, даты, массивы, вложенные объекты и т.п.).
- На основе анализа предложить:
- Набор `mongoose`‑схем (`models`) с аккуратной сериализацией (виртуальное поле `id`, скрытие `_id` и `__v`).
- Набор маршрутов `express` для работы с этими моделями (минимум: чтение списков и элементов; по возможности — создание/обновление/удаление).
- Перед внесением изменений:
- Показать пользователю краткий план того, какие файлы будут созданы/изменены.
- Выполнить изменения только после явного подтверждения пользователя.
### 4. Минимальные требования к новому студенческому бэкенду
- **Обязательные элементы**
- Подпапка в `server/routers/<project-name>`.
- Основной роутер `index.js` / `index.ts`, экспортирующий `express.Router()`.
- Подключение к общему серверу в `server/index.ts` (импорт + `app.use()` с уникальным именем роутера).
- **Если используются JSONзаглушки**
- Папка `json/` внутри проекта.
- Все пути в импортирующих файлах должны указывать на реально существующие `*.json` файлы.
- Не должно быть «магических» абсолютных путей; только относительные пути от файла до нужного JSON.
- **Если проект «оживлён»**
- Папка `model/` с моделью(ями) данных (например, через `mongoose`).
- Роуты, которые вместо прямой отдачи файлов работают с моделями и, при необходимости, с внешними сервисами.
Следуя этим правилам, можно подключать новые студенческие проекты в единый бэкенд, минимизировать типичные ошибки с путями к JSON и упростить автоматическое развитие заглушек до полноценного API.

2
server/data/const.js Normal file
View File

@@ -0,0 +1,2 @@
exports.TODO_LIST_MODEL_NAME = 'TODO_LIST'
exports.TODO_ITEM_MODEL_NAME = 'TODO_ITEM'

View File

@@ -0,0 +1,23 @@
const { Schema, model } = require('mongoose')
const { TODO_ITEM_MODEL_NAME } = require('../../const')
const schema = new Schema({
title: String,
done: { type: Boolean, default: false },
closed: Date,
created: {
type: Date, default: () => new Date().toISOString(),
},
})
schema.set('toJSON', {
virtuals: true,
versionKey: false,
})
schema.virtual('id').get(function () {
return this._id.toHexString()
})
exports.ItemModel = model(TODO_ITEM_MODEL_NAME, schema)

View File

@@ -1,22 +1,18 @@
const { Schema, model } = require('mongoose')
const { TODO_LIST_MODEL_NAME, TODO_ITEM_MODEL_NAME, TODO_AUTH_USER_MODEL_NAME } = require('../../const')
const { TODO_LIST_MODEL_NAME, TODO_ITEM_MODEL_NAME } = require('../../const')
const schema = new Schema({
title: String,
created: {
type: Date, default: () => new Date().toISOString(),
},
createdBy: { type: Schema.Types.ObjectId, ref: TODO_AUTH_USER_MODEL_NAME },
items: [{ type: Schema.Types.ObjectId, ref: TODO_ITEM_MODEL_NAME }],
})
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform: function (doc, ret) {
delete ret._id
}
})
schema.virtual('id').get(function () {

13
server/error.js Normal file
View File

@@ -0,0 +1,13 @@
const noToken = 'No authorization token was found'
module.exports = (err, req, res, next) => {
if (err.message === noToken) {
res.status(400).send({
success: false, error: 'Токен авторизации не найден',
})
}
res.status(400).send({
success: false, error: err.message || 'Что-то пошло не так',
})
}

View File

@@ -1,28 +0,0 @@
import { ErrorLog } from './models/ErrorLog'
const noToken = 'No authorization token was found'
export const errorHandler = (err, req, res, next) => {
// Сохраняем ошибку в базу данных
const errorLog = new ErrorLog({
message: err.message || 'Неизвестная ошибка',
stack: err.stack,
path: req.path,
method: req.method,
query: req.query,
body: req.body
})
errorLog.save()
.catch(saveErr => console.error('Ошибка при сохранении лога ошибки:', saveErr))
if (err.message === noToken) {
res.status(400).send({
success: false, error: 'Токен авторизации не найден',
})
}
res.status(400).send({
success: false, error: err.message || 'Что-то пошло не так',
})
}

97
server/index.js Normal file
View File

@@ -0,0 +1,97 @@
const express = require("express")
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const session = require("express-session")
const morgan = require("morgan")
const path = require("path")
const rfs = require("rotating-file-stream")
const app = express()
require("dotenv").config()
exports.app = app
const accessLogStream = rfs.createStream("access.log", {
size: "10M",
interval: "1d",
compress: "gzip",
path: path.join(__dirname, "log"),
})
const errorLogStream = rfs.createStream("error.log", {
size: "10M",
interval: "1d",
compress: "gzip",
path: path.join(__dirname, "log"),
})
const config = require("../.serverrc")
const { setIo } = require("./io")
app.use(cookieParser())
app.use(
morgan("combined", {
stream: accessLogStream,
skip: function (req, res) {
return res.statusCode >= 400
},
})
)
// log all requests to access.log
app.use(
morgan("combined", {
stream: errorLogStream,
skip: function (req, res) {
console.log('statusCode', res.statusCode, res.statusCode <= 400)
return res.statusCode < 400
},
})
)
const server = setIo(app)
const sess = {
secret: "super-secret-key",
resave: true,
saveUninitialized: true,
cookie: {},
}
if (app.get("env") === "production") {
app.set("trust proxy", 1)
sess.cookie.secure = true
}
app.use(session(sess))
app.use(
bodyParser.json({
limit: "50mb",
})
)
app.use(
bodyParser.urlencoded({
limit: "50mb",
extended: true,
})
)
app.use(require("./root"))
/**
* Добавляйте сюда свои routers.
*/
app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1"))
app.use("/epja-2024-1", require("./routers/epja-2024-1"))
app.use("/todo", require("./routers/todo/routes"))
app.use("/dogsitters-finder", require("./routers/dogsitters-finder"))
app.use("/kazan-explore", require("./routers/kazan-explore"))
app.use("/edateam", require("./routers/edateam-legacy"))
app.use("/dry-wash", require("./routers/dry-wash"))
app.use("/freetracker", require("./routers/freetracker"))
app.use("/dhs-testing", require("./routers/dhs-testing"))
app.use("/gamehub", require("./routers/gamehub"))
app.use(require("./error"))
server.listen(config.port, () =>
console.log(`Listening on http://localhost:${config.port}`)
)

View File

@@ -1,155 +0,0 @@
import express from 'express'
import cookieParser from 'cookie-parser'
import session from 'express-session'
import morgan from 'morgan'
import path from 'path'
import 'dotenv/config'
import root from './server'
import { errorHandler } from './error'
import kfuM241Router from './routers/kfu-m-24-1'
import epja20241Router from './routers/epja-2024-1'
import todoRouter from './routers/todo'
import dogsittersFinderRouter from './routers/dogsitters-finder'
import kazanExploreRouter from './routers/kazan-explore'
import edateamRouter from './routers/edateam-legacy'
import dryWashRouter from './routers/dry-wash'
import freetrackerRouter from './routers/freetracker'
import dhsTestingRouter from './routers/dhs-testing'
import gamehubRouter from './routers/gamehub'
import escRouter from './routers/esc'
import connectmeRouter from './routers/connectme'
import questioneerRouter from './routers/questioneer'
import procurementRouter from './routers/procurement'
import smokeTrackerRouter from './routers/smoke-tracker'
import { setIo } from './io'
export const app = express()
// Динамический импорт rotating-file-stream
const initServer = async () => {
const rfs = await import('rotating-file-stream')
const accessLogStream = rfs.createStream("access.log", {
size: "10M",
interval: "1d",
compress: "gzip",
path: path.join(__dirname, "log"),
})
const errorLogStream = rfs.createStream("error.log", {
size: "10M",
interval: "1d",
compress: "gzip",
path: path.join(__dirname, "log"),
})
app.use(cookieParser())
app.use(
morgan("combined", {
stream: accessLogStream,
skip: function (req, res) {
return res.statusCode >= 400
},
})
)
// log all requests to access.log
app.use(
morgan("combined", {
stream: errorLogStream,
skip: function (req, res) {
console.log('statusCode', res.statusCode, res.statusCode <= 400)
return res.statusCode < 400
},
})
)
console.log('warming up 🔥')
const sess = {
secret: "super-secret-key",
resave: true,
saveUninitialized: true,
cookie: {},
}
if (app.get("env") !== "development") {
app.set("trust proxy", 1)
}
app.use(session(sess))
app.use(
express.json({
limit: "50mb",
})
)
app.use(
express.urlencoded({
limit: "50mb",
extended: true,
})
)
app.use(root)
/**
* Добавляйте сюда свои routers.
*/
app.use("/kfu-m-24-1", kfuM241Router)
app.use("/epja-2024-1", epja20241Router)
app.use("/v1/todo", todoRouter)
app.use("/dogsitters-finder", dogsittersFinderRouter)
app.use("/kazan-explore", kazanExploreRouter)
app.use("/edateam", edateamRouter)
app.use("/dry-wash", dryWashRouter)
app.use("/freetracker", freetrackerRouter)
app.use("/dhs-testing", dhsTestingRouter)
app.use("/gamehub", gamehubRouter)
app.use("/esc", escRouter)
app.use('/connectme', connectmeRouter)
app.use('/questioneer', questioneerRouter)
app.use('/procurement', procurementRouter)
app.use('/smoke-tracker', smokeTrackerRouter)
app.use(errorHandler)
// Создаем обычный HTTP сервер
const server = app.listen(process.env.PORT ?? 8044, () => {
console.log(`🚀 Сервер запущен на http://localhost:${process.env.PORT ?? 8044}`)
})
// Обработка сигналов завершения процесса
process.on('SIGTERM', () => {
console.log('🛑 Получен сигнал SIGTERM. Выполняется корректное завершение...')
server.close(() => {
console.log('✅ Сервер успешно остановлен')
process.exit(0)
})
})
process.on('SIGINT', () => {
console.log('🛑 Получен сигнал SIGINT. Выполняется корректное завершение...')
server.close(() => {
console.log('✅ Сервер успешно остановлен')
process.exit(0)
})
})
// Обработка необработанных исключений
process.on('uncaughtException', (err) => {
console.error('❌ Необработанное исключение:', err)
server.close(() => {
process.exit(1)
})
})
// Обработка необработанных отклонений промисов
process.on('unhandledRejection', (reason, promise) => {
console.error('⚠️ Необработанное отклонение промиса:', reason)
server.close(() => {
process.exit(1)
})
})
return server
}
initServer().catch(console.error)

13
server/io.js Normal file
View File

@@ -0,0 +1,13 @@
const { Server } = require('socket.io')
const { createServer } = require('http')
let io = null
module.exports.setIo = (app) => {
const server = createServer(app)
io = new Server(server, {})
return server
}
module.exports.getIo = () => io

View File

@@ -1,13 +0,0 @@
import { Server } from 'socket.io'
import { createServer } from 'http'
let io = null
export const setIo = (app) => {
const server = createServer(app)
io = new Server(server, {})
return server
}
export const getIo = () => io

View File

@@ -1,16 +0,0 @@
import mongoose from 'mongoose'
const ErrorLogSchema = new mongoose.Schema({
message: { type: String, required: true },
stack: { type: String },
path: { type: String },
method: { type: String },
query: { type: Object },
body: { type: Object },
createdAt: { type: Date, default: Date.now },
})
// Индекс для быстрого поиска по дате создания
ErrorLogSchema.index({ createdAt: 1 })
export const ErrorLog = mongoose.model('ErrorLog', ErrorLogSchema)

View File

@@ -1,55 +0,0 @@
const mongoose = require('mongoose');
// Типы вопросов
export const QUESTION_TYPES = {
SINGLE_CHOICE: 'single_choice', // Один вариант
MULTIPLE_CHOICE: 'multiple_choice', // Несколько вариантов
TEXT: 'text', // Текстовый ответ
RATING: 'rating', // Оценка по шкале
TAG_CLOUD: 'tag_cloud' // Облако тегов
};
// Типы отображения
export const DISPLAY_TYPES = {
DEFAULT: 'default',
TAG_CLOUD: 'tag_cloud',
VOTING: 'voting',
POLL: 'poll'
};
// Схема варианта ответа
const optionSchema = new mongoose.Schema({
text: { type: String, required: true },
count: { type: Number, default: 0 } // счетчик голосов
});
// Схема вопроса
const questionSchema = new mongoose.Schema({
text: { type: String, required: true },
type: {
type: String,
enum: Object.values(QUESTION_TYPES),
required: true
},
options: [optionSchema],
required: { type: Boolean, default: false }
});
// Схема опроса
const questionnaireSchema = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String },
questions: [questionSchema],
displayType: {
type: String,
enum: Object.values(DISPLAY_TYPES),
default: DISPLAY_TYPES.DEFAULT
},
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
adminLink: { type: String, required: true }, // ссылка для редактирования
publicLink: { type: String, required: true } // ссылка для голосования
});
export const Questionnaire = mongoose.model('Questionnaire', questionnaireSchema);

33
server/root.js Normal file
View File

@@ -0,0 +1,33 @@
const fs = require('fs')
const path = require('path')
const router = require('express').Router()
const mongoose = require('mongoose')
const pkg = require('../package.json')
require('./utils/mongoose')
const folderPath = path.resolve(__dirname, './routers')
const folders = fs.readdirSync(folderPath)
router.get('/', async (req, res) => {
// throw new Error('check error message')
res.send(`
<h1>multy stub is working v${pkg.version}</h1>
<ul>
${folders.map((f) => `<li>${f}</li>`).join('')}
</ul>
<h2>models</h2>
<ul>${
(await Promise.all(
(await mongoose.modelNames()).map(async (name) => {
const count = await mongoose.model(name).countDocuments()
return `<li>${name} - ${count}</li>`
}
)
)).map(t => t).join(' ')
}</ul>
`)
})
module.exports = router

View File

@@ -1,8 +0,0 @@
const { Router } = require('express')
const router = Router()
router.get('/cities', (request, response) => {
response.send(require('./json/cities.json'))
})
module.exports = router

View File

@@ -1,85 +0,0 @@
{
"data": [
{
"id": 1,
"title": "Моска"
},
{
"id": 2,
"title": "Санкт-петербург"
},
{
"id": 3,
"title": "Новосибирска"
},
{
"id": 4,
"title": "Екатеринбург"
},
{
"id": 5,
"title": "Казань"
},
{
"id": 6,
"title": "Нижний новгород"
},
{
"id": 7,
"title": "Челябинск"
},
{
"id": 8,
"title": "Самара"
},
{
"id": 9,
"title": "Омск"
},
{
"id": 10,
"title": "Ростов-на-дону"
},
{
"id": 11,
"title": "Уфа"
},
{
"id": 12,
"title": "Красноярск"
},
{
"id": 13,
"title": "Пермь"
},
{
"id": 14,
"title": "Воронеж"
},
{
"id": 15,
"title": "Волгоград"
},
{
"id": 16,
"title": "Краснодар"
},
{
"id": 17,
"title": "Тюмень"
},
{
"id": 18,
"title": "Ижевск"
},
{
"id": 19,
"title": "Барнаул"
},
{
"id": 20,
"title": "Владивосток"
}
],
"count": 20
}

View File

@@ -1,2 +0,0 @@
exports.DSF_AUTH_USER_MODEL_NAME = 'DSF_AUTH_USER'
exports.DSF_INTERACTION_MODEL_NAME = 'DSF_INTERACTION'

View File

@@ -7,29 +7,29 @@ router.get("/users", (request, response) => {
router.post("/auth", (request, response) => {
const { phoneNumber, password } = request.body;
console.log(phoneNumber, password);
if (phoneNumber === "89999999999" || phoneNumber === "89559999999") {
response.send(require("./json/auth/success.json"));
if (phoneNumber === "89999999999") {
response.send(require("./json/auth/dogsitter.success.json"));
} else if (phoneNumber === "89555555555") {
response.status(400).send(require("./json/auth/error.json"));
} else {
response.status(401).send(require("./json/auth/error.json"));
response.send(require("./json/auth/owner.success.json"));
}
});
router.post("/auth/2fa", (request, response) => {
const { phoneNumber, code } = request.body;
if (code === "0000" && phoneNumber === "89999999999") {
response.send(require("./json/2fa/dogsitter.success.json"));
} else if (code === "0000" && phoneNumber === "89559999999") {
response.send(require("./json/2fa/owner.success.json"));
const { code } = request.body;
if (code === "0000") {
response.send(require("./json/2fa/success.json"));
} else {
response.status(401).send(require("./json/2fa/error.json"));
response.status(400).send(require("./json/2fa/error.json"));
}
});
router.post("/register", (request, response) => {
const { firstName, secondName, phoneNumber, password, role } = request.body;
console.log(phoneNumber, password, role);
if (phoneNumber === "89999999999" || phoneNumber === "89559999999") {
response.status(401).send(require("./json/register/error.json"));
if (phoneNumber === "89283244141" || phoneNumber === "89872855893") {
response.status(400).send(require("./json/register/error.json"));
} else if (role === "dogsitter") {
response.send(require("./json/register/dogsitter.success.json"));
} else {
@@ -37,192 +37,4 @@ router.post("/register", (request, response) => {
}
});
router.get("/auth/session", (request, response) => {
const authHeader = request.headers.authorization;
if (!authHeader) {
return response.status(401).json({ error: "Authorization header missing" });
}
// Берём сам токен из заголовка
const token = authHeader.split(" ")[1];
if (!token) {
return response.status(401).json({ error: "Bearer token missing" });
}
const jwt = require("jsonwebtoken");
const secretKey = "secret";
try {
const decoded = jwt.verify(token, secretKey);
if (decoded.role === "dogsitter") {
response.send(require("./json/role/dogsitter.success.json"));
} else {
response.send(require("./json/role/owner.success.json"));
}
} catch (e) {
console.log("token e:", e);
return response.status(403).json({ error: "Invalid token" });
}
});
// Проверка взаимодействия между пользователем и догситтером
router.get("/interactions/check", (req, res) => {
const { owner_id, dogsitter_id } = req.query;
const usersFilePath = path.resolve(__dirname, "./json/users/users.json");
delete require.cache[require.resolve(usersFilePath)];
const usersFile = require(usersFilePath);
const interactions = usersFile.interactions || [];
const exists = interactions.some(
(interaction) =>
interaction.owner_id === Number(owner_id) &&
interaction.dogsitter_id === Number(dogsitter_id)
);
res.json({ exists });
});
// Добавление нового взаимодействия
router.post("/interactions", (req, res) => {
const { owner_id, dogsitter_id, interaction_type } = req.body;
if (!owner_id || !dogsitter_id || !interaction_type) {
return res.status(400).json({ error: "Missing required fields" });
}
const usersFilePath = path.resolve(__dirname, "./json/users/users.json");
delete require.cache[require.resolve(usersFilePath)];
const usersFile = require(usersFilePath);
if (!usersFile.interactions) {
usersFile.interactions = [];
}
// Проверяем, существует ли уже такое взаимодействие
const exists = usersFile.interactions.some(
(interaction) =>
interaction.owner_id === Number(owner_id) &&
interaction.dogsitter_id === Number(dogsitter_id)
);
if (!exists) {
usersFile.interactions.push({
owner_id: Number(owner_id),
dogsitter_id: Number(dogsitter_id),
interaction_type,
});
fs.writeFileSync(
usersFilePath,
JSON.stringify(usersFile, null, 2),
"utf8"
);
console.log(
`Добавлено взаимодействие: owner_id=${owner_id}, dogsitter_id=${dogsitter_id}`
);
}
res.json({ success: true });
});
router.get("/dogsitter-viewing", (req, res) => {
const { id } = req.query;
console.log(`Получен запрос для dogsitter с ID: ${id}`);
const usersFile = require("./json/users/users.json");
const users = usersFile.data; // Извлекаем массив из свойства "data"
const user = users.find((user) => user.id === Number(id));
if (user) {
res.json(user); // Возвращаем найденного пользователя
} else {
res.status(404).json({ error: "User not found" }); // Если пользователь не найден
}
});
const fs = require('fs');
const path = require('path');
router.post('/dogsitter-viewing/rating/:id', (req, res) => {
const { id } = req.params;
const { rating } = req.body;
if (!rating || rating < 1 || rating > 5) {
return res.status(400).json({ error: 'Некорректная оценка' });
}
const usersFilePath = path.resolve(__dirname, "./json/users/users.json");
delete require.cache[require.resolve(usersFilePath)];
const usersFile = require(usersFilePath);
const users = usersFile.data;
const userIndex = users.findIndex(user => user.id === Number(id));
if (userIndex === -1) {
return res.status(404).json({ error: 'Догситтер не найден' });
}
if (!users[userIndex].ratings) {
users[userIndex].ratings = [];
}
users[userIndex].ratings.push(rating);
if (users[userIndex].ratings.length > 100) {
users[userIndex].ratings.shift();
}
const total = users[userIndex].ratings.reduce((sum, r) => sum + r, 0);
users[userIndex].rating = parseFloat((total / users[userIndex].ratings.length).toFixed(2));
fs.writeFileSync(usersFilePath, JSON.stringify({ data: users }, null, 2), 'utf8');
console.log(`Обновлен рейтинг догситтера ${id}: ${users[userIndex].rating}`);
res.json({ rating: users[userIndex].rating, ratings: users[userIndex].ratings });
});
router.patch('/users/:id', (req, res) => {
const { id } = req.params;
const updateData = req.body;
console.log('Полученные данные для обновления:', updateData);
const usersFilePath = path.resolve(__dirname, "./json/users/users.json");
delete require.cache[require.resolve(usersFilePath)];
const usersFile = require(usersFilePath);
const users = usersFile.data;
const userIndex = users.findIndex((user) => user.id === Number(id));
if (userIndex === -1) {
return res.status(404).json({ error: 'User not found' });
}
users[userIndex] = { ...users[userIndex], ...updateData };
fs.writeFileSync(
usersFilePath,
JSON.stringify({ data: users }, null, 2),
'utf8'
);
console.log('Обновлённые данные пользователя:', users[userIndex]);
res.json(users[userIndex]);
});
module.exports = router
module.exports = router;

View File

@@ -1,3 +0,0 @@
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImRvZ3NpdHRlciIsImlhdCI6MTUxNjIzOTAyMn0.7q66wTNyLZp3TGFYF_JdU-yhlWViJulTxP_PCQzO4OI"
}

View File

@@ -1,5 +1,4 @@
{
"status": "error",
"message": "Invalid code",
"statusCode": 401
"message": "Invalid code."
}

View File

@@ -1,3 +0,0 @@
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Mywicm9sZSI6Im93bmVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.sI9839YXveTpEWhdpr5QbCYllt6hHYO7NsrQDcrXZIQ"
}

View File

@@ -0,0 +1,4 @@
{
"status": "success",
"message": "Two-factor authentication passed."
}

View File

@@ -0,0 +1,12 @@
{
"data": {
"id": 1,
"phoneNumber": 89283244141,
"firstName": "Вася",
"secondName": "Пупкин",
"role": "dogsitter",
"location": "Россия, республика Татарстан, Казань, улица Пушкина, 12",
"price": 1500,
"aboutMe": "Я люблю собак"
}
}

View File

@@ -1,5 +1,3 @@
{
"message": "Неверный логин или пароль",
"error": "Unauthorized",
"statusCode": 401
"error": "Пользователь не найден"
}

View File

@@ -0,0 +1,9 @@
{
"data": {
"id": 3,
"phoneNumber": 89872855893,
"firstName": "Гадий",
"secondName": "Петрович",
"role": "owner"
}
}

View File

@@ -1,5 +0,0 @@
{
"status": "success",
"message": "Первый фактор аутентификации пройден",
"statusCode": 200
}

View File

@@ -1,3 +1,12 @@
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwicm9sZSI6ImRvZ3NpdHRlciIsImlhdCI6MTUxNjIzOTAyMn0.T9V3-f3rD1deA5a2J-tYNw0cACEpzKHbhMPkc7gh8c0"
"data": {
"id": 5,
"phoneNumber": 89555555555,
"firstName": "Масяня",
"secondName": "Карлова",
"role": "dogsitter",
"location": "Россия, республика Татарстан, Казань, улица Пушкина, 12",
"price": 100,
"aboutMe": "Все на свете - собаки"
}
}

View File

@@ -1,5 +1,3 @@
{
"message": "Такой пользователь уже был зарегистрирован",
"error": "Unauthorized",
"statusCode": 401
"error": "Пользователь с таким номером телефона уже существует"
}

View File

@@ -1,3 +1,9 @@
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Niwicm9sZSI6Im93bmVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.qgOhk9tNcaMRbarRWISTgvGx5Eq_X8fcA5lhdVs2tQI"
"data": {
"id": 6,
"phoneNumber": 89888888888,
"firstName": "Генадий",
"secondName": "Паровозов",
"role": "owner"
}
}

View File

@@ -1,4 +0,0 @@
{
"id": 1,
"role": "dogsitter"
}

View File

@@ -1,5 +0,0 @@
{
"message": "Неверный jwt token",
"error": "Forbidden",
"statusCode": 403
}

View File

@@ -1,4 +0,0 @@
{
"id": 3,
"role": "owner"
}

View File

@@ -1,20 +1,13 @@
{
"data": [
[
{
"id": 1,
"phone_number": "89999999999",
"phone_number": 89283244141,
"first_name": "Вася",
"second_name": "Пупкин",
"role": "dogsitter",
"location": "Россия, республика Татарстан, Казань, Пушкина, 12",
"price": "1500",
"about_me": "Я люблю собак!",
"rating": 5,
"ratings": [
5,
5
],
"tg": "jullllllie"
"location": "Россия, республика Татарстан, Казань, улица Пушкина, 12",
"price": 1500,
"about_me": "Я люблю собак"
},
{
"id": 2,
@@ -23,18 +16,12 @@
"second_name": "Пуськин",
"role": "dogsitter",
"location": "Россия, республика Татарстан, Казань, улица Абсалямова, 19",
"price": 2000,
"about_me": "Я не люблю собак. И вообще я котоман.",
"rating": 4,
"ratings": [
4,
4
],
"tg": "vanya006"
"price": 1000000,
"about_me": "Я не люблю собак. И вообще я котоман."
},
{
"id": 3,
"phone_number": 89559999999,
"phone_number": 89872855893,
"first_name": "Гадий",
"second_name": "Петрович",
"role": "owner"
@@ -46,24 +33,7 @@
"second_name": "Максим",
"role": "dogsitter",
"location": "Россия, республика Татарстан, Казань, проспект Ямашева, 83",
"price": 1750,
"about_me": "Миллион алых роз",
"rating": 4.5,
"ratings": [
4,
5
],
"tg": "maks100500"
"price": 1000000,
"about_me": "Миллион алых роз"
}
],
"interactions": [
{
"owner_id": 3,
"dogsitter_id": 4
},
{
"owner_id": 1,
"dogsitter_id": 2
}
]
}
]

View File

@@ -1,24 +0,0 @@
const { Schema, model } = require("mongoose");
const { DSF_AUTH_USER_MODEL_NAME, DSF_INTERACTION_MODEL_NAME } = require("../../const");
const interactionSchema = new Schema({
owner_id: {
type: Schema.Types.ObjectId,
ref: DSF_AUTH_USER_MODEL_NAME,
required: true
},
dogsitter_id: {
type: Schema.Types.ObjectId,
ref: DSF_AUTH_USER_MODEL_NAME,
required: true
},
timestamp: {
type: Date,
default: Date.now
}
});
interactionSchema.index({ owner_id: 1, dogsitter_id: 1 });
module.exports.Interaction = model(DSF_INTERACTION_MODEL_NAME, interactionSchema);

View File

@@ -1,83 +0,0 @@
const { Schema, model } = require("mongoose");
const { DSF_AUTH_USER_MODEL_NAME } = require("../../const");
const userSchema = new Schema({
phone_number: {
type: String,
required: true,
unique: true,
match: /^\+?\d{10,15}$/
},
first_name: {
type: String,
required: true,
trim: true
},
second_name: {
type: String,
required: true,
trim: true
},
role: {
type: String,
enum: ["dogsitter", "owner"],
required: true
},
location: {
type: String,
required: function() {
return this.role === "dogsitter";
}
},
price: {
type: Number,
min: 0,
required: function() {
return this.role === "dogsitter";
}
},
about_me: {
type: String,
maxlength: 500
},
rating: {
type: Number,
min: 0,
max: 5,
default: 0
},
ratings: {
type: [Number],
default: [],
validate: {
validator: function(arr) {
return arr.every(v => v >= 0 && v <= 5);
},
message: "Рейтинг должен быть в диапазоне от 0 до 5!"
}
},
tg: {
type: String,
match: /^[a-zA-Z0-9_]{5,32}$/
},
created: {
type: Date,
default: Date.now
}
});
userSchema.virtual("id").get(function() {
return this._id.toHexString();
});
userSchema.set("toJSON", {
virtuals: true,
versionKey: false,
transform: function(doc, ret) {
delete ret._id;
delete ret.__v;
}
});
module.exports.User = model(DSF_AUTH_USER_MODEL_NAME, userSchema);

View File

@@ -1,149 +0,0 @@
const { Router } = require('express')
const { expressjwt } = require('express-jwt')
const { getAnswer } = require('../../utils/common')
const { User, Interaction } = require('./model')
const { TOKEN_KEY } = require('./const')
const { requiredValidate } = require('./utils')
const router = Router()
// Получение списка пользователей
router.get('/users', async (req, res) => {
const users = await User.find()
.select('-__v -ratings -phone_number')
.lean()
console.log('get users successfull')
res.send(getAnswer(null, users))
})
// Получение конкретного пользователя
router.get('/dogsitter-viewing', async (req, res) => {
const { userId } = req.params
const user = await User.findById(userId)
.select('-__v -ratings')
.lean()
if (!user) {
return res.status(404).send(getAnswer(new Error('Пользователь не найден')))
}
res.send(getAnswer(null, user))
})
router.use(expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }))
// Добавление оценки пользователю
router.post('/dogsitter-viewing/rating', requiredValidate('value'), async (req, res) => {
const { userId } = req.params
const { value } = req.body
const authUserId = req.auth.id
try {
const user = await User.findById(userId)
if (!user) throw new Error('Пользователь не найден')
if (user.role !== 'dogsitter') throw new Error('Нельзя оценивать этого пользователя')
if (user.id === authUserId) throw new Error('Нельзя оценивать самого себя')
user.ratings.push(Number(value))
user.rating = user.ratings.reduce((a, b) => a + b, 0) / user.ratings.length
const updatedUser = await user.save()
res.send(getAnswer(null, {
id: updatedUser.id,
rating: updatedUser.rating.toFixed(1),
totalRatings: updatedUser.ratings.length
}))
} catch (error) {
res.status(400).send(getAnswer(error))
}
})
// Обновление информации пользователя
router.patch('/users', async (req, res) => {
const { userId } = req.params
const updates = req.body
try {
const user = await User.findByIdAndUpdate(userId, updates, { new: true })
.select('-__v -ratings')
if (!user) throw new Error('Пользователь не найден')
res.send(getAnswer(null, user))
} catch (error) {
res.status(400).send(getAnswer(error))
}
})
// Создание объекта взаимодействия
router.post('/interactions',
expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }),
requiredValidate('dogsitter_id'),
async (req, res) => {
try {
const { dogsitter_id } = req.body
const owner_id = req.auth.id // ID из JWT токена
// Проверка существования пользователей
const [owner, dogsitter] = await Promise.all([
User.findById(owner_id),
User.findById(dogsitter_id)
])
if (!owner || owner.role !== 'owner') {
throw new Error('Владелец не найден или имеет неверную роль')
}
if (!dogsitter || dogsitter.role !== 'dogsitter') {
throw new Error('Догситтер не найден или имеет неверную роль')
}
// Создание взаимодействия
const interaction = await Interaction.create({
owner_id,
dogsitter_id
})
res.send(getAnswer(null, {
id: interaction.id,
timestamp: interaction.timestamp
}))
} catch (error) {
res.status(400).send(getAnswer(error))
}
}
)
router.get('/interactions/check', async (req, res) => {
const { owner_id, dogsitter_id } = req.query;
if (!owner_id || !dogsitter_id) {
return res.status(400).send(getAnswer('Missing owner_id or dogsitter_id'));
}
try {
// Поиск взаимодействий по owner_id и dogsitter_id
const interactions = await Interaction.find({ owner_id, dogsitter_id })
.select('-__v') // Выбираем только нужные поля
.lean();
if (interactions.length === 0) {
return res.status(404).send(getAnswer('No interactions found'));
}
res.send(getAnswer(null, interactions));
} catch (error) {
console.error('Error checking interactions:', error);
res.status(500).send(getAnswer('Internal Server Error'));
}
});
module.exports = router

View File

@@ -1,58 +1,22 @@
const router = require("express").Router();
const { MasterModel } = require("./model/master");
const mongoose = require("mongoose");
const { OrderModel } = require("./model/order");
const router = require('express').Router()
const {MasterModel} = require('./model/master')
const mongoose = require("mongoose")
router.post("/masters/list", async (req, res, next) => {
router.get('/masters', async (req, res,next) => {
try {
const { startDate, endDate } = req.body;
if (!startDate || !endDate) {
throw new Error("Missing startDate or endDate");
}
const start = new Date(startDate);
const end = new Date(endDate);
const masters = await MasterModel.find({});
const orders = await OrderModel.find({
$or: [
{ startWashTime: { $lt: end }, endWashTime: { $gt: start } }
]
});
const mastersWithOrders = masters.map((master) => {
const masterOrders = orders.filter((order) => {
return (
order?.master && order.master.toString() === master._id.toString()
);
});
const schedule = masterOrders.map((order) => ({
id: order._id,
startWashTime: order.startWashTime,
endWashTime: order.endWashTime,
}));
return {
id: master._id,
name: master.name,
schedule: schedule,
phone: master.phone,
};
});
res.status(200).send({ success: true, body: mastersWithOrders });
const master = await MasterModel.find({})
res.status(200).send({success: true, body: master})
} catch (error) {
next(error);
next(error)
}
});
})
router.delete("/masters/:id", async (req, res, next) => {
router.delete('/masters/:id', async (req, res,next) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error("ID is required");
if (!mongoose.Types.ObjectId.isValid(id)){
throw new Error('ID is required')
}
try {
@@ -60,52 +24,58 @@ router.delete("/masters/:id", async (req, res, next) => {
new: true,
});
if (!master) {
throw new Error("master not found");
throw new Error('master not found')
}
res.status(200).send({ success: true, body: master });
res.status(200).send({success: true, body: master})
} catch (error) {
next(error);
next(error)
}
});
})
router.post("/masters", async (req, res, next) => {
const { name, phone } = req.body;
if (!name || !phone) {
throw new Error("Enter name and phone");
router.post('/masters', async (req, res,next) => {
const {name, phone} = req.body
if (!name || !phone ){
throw new Error('Enter name and phone')
}
try {
const master = await MasterModel.create({ name, phone });
res.status(200).send({ success: true, body: master });
const master = await MasterModel.create({name, phone})
res.status(200).send({success: true, body: master})
} catch (error) {
next(error);
next(error)
}
});
})
router.patch("/masters/:id", async (req, res, next) => {
router.patch('/masters/:id', async (req, res, next) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error("ID is required");
throw new Error('ID is required')
}
const { name, phone } = req.body;
if (!name && !phone) {
throw new Error("Enter name and phone");
throw new Error('Enter name and phone')
}
try {
const updateData = {};
if (name) updateData.name = name;
if (phone) updateData.phone = phone;
const master = await MasterModel.findByIdAndUpdate(id, updateData, {
new: true,
});
const master = await MasterModel.findByIdAndUpdate(
id,
updateData,
{ new: true }
);
if (!master) {
throw new Error("master not found");
throw new Error('master not found')
}
res.status(200).send({ success: true, body: master });
@@ -114,4 +84,4 @@ router.patch("/masters/:id", async (req, res, next) => {
}
});
module.exports = router;
module.exports = router

View File

@@ -1,21 +1,9 @@
const router = require('express').Router()
const { OrderModel } = require('./model/order')
router.post('/orders', async (req, res, next) => {
const {startDate, endDate} = req.body
if (!startDate || !endDate) {
throw new Error('startDate and endDate are required')
}
const orders = await OrderModel.find({
$or: [
{startWashTime: { $gte: new Date(startDate), $lte: new Date(endDate) }},
{endWashTime: { $gte: new Date(startDate), $lte: new Date(endDate) }},
]
})
res.status(200).send({ success: true, body: orders })
router.get('/orders', (req, res) => {
res
.status(200)
.send(require(`./json/arm-orders/success.json`))
})
module.exports = router

View File

@@ -1,23 +0,0 @@
const getGigaToken = async () => {
const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev')
const data = await response.json()
return data.features['dry-wash-bh'].GIGA_TOKEN.value
}
const getSystemPrompt = async () => {
const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev')
const data = await response.json()
return data.features['dry-wash-bh'].SYSTEM_PROMPT.value
}
const getGigaChatModel = async () => {
const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev')
const data = await response.json()
return data.features['dry-wash-bh'].GIGA_CHAT_MODEL.value
}
module.exports = {
getGigaToken,
getSystemPrompt,
getGigaChatModel
}

View File

@@ -1,12 +1,10 @@
const router = require('express').Router()
const armMasterRouter = require('./arm-master')
const armOrdersRouter = require('./arm-orders')
const orderRouter = require('./order')
router.use('/arm', armMasterRouter)
router.use('/arm', armOrdersRouter)
router.use('/order', orderRouter)
module.exports = router

View File

@@ -2,59 +2,24 @@
"success": true,
"body": [
{
"phone": "+79876543210",
"carNumber": "А123ВЕ16",
"carBody": 1,
"carColor": "#ffff00",
"startWashTime": "2025-05-12T08:21:00.000Z",
"endWashTime": "2025-05-12T08:22:00.000Z",
"location": "55.792799704829854,49.11034340707925 Республика Татарстан (Татарстан), Казань, улица Чернышевского",
"id": "order1",
"carNumber": "A123BC",
"startWashTime": "2024-11-24T10:30:00.000Z",
"endWashTime": "2024-11-24T16:30:00.000Z",
"orderDate": "2024-11-24T08:41:46.366Z",
"status": "progress",
"notes": "",
"created": "2025-01-18T17:43:21.488Z",
"updated": "2025-01-18T17:43:21.492Z"
"phone": "79001234563",
"location": "Казань, ул. Баумана, 1"
},
{
"phone": "89876543210",
"carNumber": "К456МН23",
"carBody": 2,
"carColor": "#ffffff",
"startWashTime": "2025-01-12T08:21:00Z",
"endWashTime": "2025-01-12T08:22:00Z",
"location": "55.808430668108585,49.198608125449255 Республика Татарстан (Татарстан), Казань, улица Академика Губкина, 50/1",
"status": "pending",
"notes": "заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки заметки",
"created": "2025-01-18T17:46:10.388Z",
"updated": "2025-01-18T17:46:10.395Z",
"id": "678be8e211e62f4a61790cca"
},
{
"phone": "4098765432105",
"carNumber": "О789РС777",
"carBody": 3,
"carColor": "красный",
"startWashTime": "2025-08-12T08:21:00.000Z",
"endWashTime": "2025-08-12T08:22:00.000Z",
"location": "55.78720449830353,49.12111640202319 Республика Татарстан (Татарстан), Казань, улица Пушкина, 5/43",
"status": "cancelled",
"notes": "Заказ отменен по запросу самого клиента",
"created": "2025-01-18T17:47:46.294Z",
"updated": "2025-01-18T17:47:46.295Z",
"id": "678be8e211e62f4a61790ccb"
},
{
"phone": "+79876543210",
"carNumber": "Т123УХ716",
"carBody": 99,
"carColor": "чайная роза",
"startWashTime": "2025-01-11T11:21:00.000Z",
"endWashTime": "2025-01-12T11:22:00.000Z",
"location": "55.77063673480112,49.22182909159608 Республика Татарстан (Татарстан), Казань, Советский район, микрорайон Азино-2",
"id": "order2",
"carNumber": "A245BC",
"startWashTime": "2024-11-24T11:30:00.000Z",
"endWashTime": "2024-11-24T17:30:00.000Z",
"orderDate": "2024-11-24T07:40:46.366Z",
"status": "progress",
"notes": "Клиент остался доволен, предложить в следующий раз акцию",
"created": "2025-01-18T17:55:05.691Z",
"updated": "2025-01-18T17:55:05.695Z",
"id": "678be8e211e62f4a61790ccc"
"phone": "79001234567",
"location": "Казань, ул. Баумана, 43"
}
]
}

View File

@@ -1,11 +0,0 @@
const orderStatus = {
CANCELLED: 'cancelled',
PROGRESS: 'progress',
PENDING: 'pending',
WORKING: 'working',
COMPLETE: 'complete',
}
module.exports = {
orderStatus
}

View File

@@ -11,9 +11,6 @@ const schema = new Schema({
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform(_doc, ret) {
delete ret._id;
}
})
schema.virtual('id').get(function () {

View File

@@ -1,29 +0,0 @@
const { Schema, model } = require('mongoose')
const schema = new Schema({
image: String,
imageRating: String,
imageDescription: String,
orderId: {
type: Schema.Types.ObjectId,
ref: 'dry-wash-order'
},
created: {
type: Date,
default: () => new Date().toISOString(),
},
})
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform(_doc, ret) {
delete ret._id
}
})
schema.virtual('id').get(function () {
return this._id.toHexString()
})
exports.OrderCarImgModel = model('dry-wash-order-car-image', schema)

View File

@@ -1,75 +1,26 @@
const { Schema, model } = require('mongoose')
const { orderStatus } = require('./const')
const { OrderNumberModel } = require('./order.number')
const schema = new Schema({
phone: {
type: String,
required: true
},
carNumber: {
type: String,
required: true
},
carBody: {
type: Number,
required: true
},
carColor: Schema.Types.Mixed,
startWashTime: {
type: Date,
required: true
},
endWashTime: {
type: Date,
required: true
},
location: {
type: String,
required: true
},
orderNumber: {
type: String,
unique: true
},
status: {
type: String,
required: true,
enum: Object.values(orderStatus)
},
master: {
type: Schema.Types.ObjectId,
ref: 'dry-wash-master'
},
notes: String,
startWashTime: {type: String, required: true},
endWashTime: {type: String, required: true},
orderDate: {type: String, required: true},
location: {type: String, required: true},
phone: {type: String, required: true},
status: {type: String, required: true},
carNumber: {type: String, required: true},
created: {
type: Date,
default: () => new Date().toISOString(),
type: Date, default: () => new Date().toISOString(),
},
updated: {
type: Date,
default: () => new Date().toISOString(),
type: Date, default: () => new Date().toISOString(),
},
})
schema.pre('save', async function (next) {
if (this.isNew) {
const counter = await OrderNumberModel.findOneAndUpdate(
{ _id: 'orderNumber' },
{ $inc: { sequenceValue: 1 } },
{ new: true, upsert: true }
)
this.orderNumber = counter.sequenceValue.toString()
}
next()
master: {type: Schema.Types.ObjectId, ref: 'dry-wash-master'},
notes: String,
})
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform(_doc, ret) {
delete ret._id
}
})
schema.virtual('id').get(function () {

View File

@@ -1,14 +0,0 @@
const { Schema, model } = require('mongoose')
const schema = new Schema({
_id: {
type: String,
required: true,
},
sequenceValue: {
type: Number,
default: 0
}
})
exports.OrderNumberModel = model('dry-wash-order-number', schema)

View File

@@ -1,462 +0,0 @@
const mongoose = require("mongoose")
const router = require('express').Router()
const multer = require('multer')
const { MasterModel } = require('./model/master')
const { OrderModel } = require('./model/order')
const { OrderCarImgModel } = require('./model/order.car-img')
const { orderStatus } = require('./model/const')
const { getGigaToken, getSystemPrompt, getGigaChatModel } = require('./get-token')
const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value)
const isValidCarNumber = (value) => /^[авекмнорстух][0-9]{3}[авекмнорстух]{2}[0-9]{2,3}$/i.test(value)
const isValidCarBodyType = (value) => typeof value === 'number' && value > 0 && value < 100
const isValidCarColor = (value) => {
if (typeof value === 'number') {
return value >= 0 && value <= 7
} else if (typeof value === 'string') {
return /^[#a-z0-9а-я-\s,.()]+$/i.test(value)
}
return false
}
const isValidISODate = (value) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:.\d{1,3})?Z$/.test(value)
const latitudeRe = /^(-?[1-8]?\d(?:\.\d{1,18})?|90(?:\.0{1,18})?)$/
const longitudeRe = /^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?|180(?:\.0{1,18})?)$/
const addressRe = /^[а-я0-9\s,.'-/()]*$/i
const isValidLocation = (value) => {
if (value.length > 200) {
return false
}
const [coordinates, address] = value.split(' ')
const [latitude, longitude] = coordinates.split(',')
return latitudeRe.test(latitude) && longitudeRe.test(longitude) && addressRe.test(address)
}
const isValidOrderStatus = (value) => Object.values(orderStatus).includes(value)
const isValidOrderNotes = (value) => value.length < 500
const allowedMimeTypes = ['image/jpeg', 'image/png']
const sizeLimitInMegaBytes = 15
const VALIDATION_MESSAGES = {
order: {
notFound: 'Order not found'
},
orderId: {
invalid: 'Valid order ID is required',
},
orderStatus: {
invalid: 'Invalid order status'
},
orderNotes: {
invalid: 'Invalid order notes'
},
master: {
notFound: 'Master not found'
},
masterId: {
invalid: 'Invalid master ID',
},
phoneNumber: {
required: 'Phone number is required',
invalid: 'Invalid phone number'
},
carNumber: {
required: 'Car number is required',
invalid: 'Invalid car number'
},
carBody: {
required: 'Car body type is required',
invalid: 'Invalid car body type'
},
carColor: {
invalid: 'Invalid car color'
},
carImg: {
required: 'Car image file is required',
invalid: {
type: `Invalid car image file type. Allowed types: ${allowedMimeTypes}`,
size: `Invalid car image file size. Limit is ${sizeLimitInMegaBytes}MB`
}
},
washingBegin: {
required: 'Begin time of washing is required',
invalid: 'Invalid begin time of washing'
},
washingEnd: {
required: 'End time of washing is required',
invalid: 'Invalid end time of washing'
},
washingLocation: {
required: 'Location of washing is required',
invalid: 'Invalid location of washing'
},
}
router.post('/create', async (req, res, next) => {
const bodyErrors = []
const { customer } = req.body
if (!customer.phone) {
bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.required)
} else if (!isValidPhoneNumber(customer.phone)) {
bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.invalid)
}
const { car } = req.body
if (!car.number) {
bodyErrors.push(VALIDATION_MESSAGES.carNumber.required)
} else if (!isValidCarNumber(car.number)) {
bodyErrors.push(VALIDATION_MESSAGES.carNumber.invalid)
}
if (!car.body) {
bodyErrors.push(VALIDATION_MESSAGES.carBody.required)
} else if (!isValidCarBodyType(car.body)) {
bodyErrors.push(VALIDATION_MESSAGES.carBody.invalid)
}
if (!isValidCarColor(car.color)) {
bodyErrors.push(VALIDATION_MESSAGES.carColor.invalid)
}
const { washing } = req.body
if (!washing.begin) {
bodyErrors.push(VALIDATION_MESSAGES.washingBegin.required)
} else if (!isValidISODate(washing.begin)) {
bodyErrors.push(VALIDATION_MESSAGES.washingBegin.invalid)
}
if (!washing.end) {
bodyErrors.push(VALIDATION_MESSAGES.washingEnd.required)
} else if (!isValidISODate(washing.end)) {
bodyErrors.push(VALIDATION_MESSAGES.washingEnd.invalid)
}
if (!washing.location) {
bodyErrors.push(VALIDATION_MESSAGES.washingLocation.required)
} else if (!isValidLocation(washing.location)) {
bodyErrors.push(VALIDATION_MESSAGES.washingLocation.invalid)
}
if (bodyErrors.length > 0) {
throw new Error(bodyErrors.join(', '))
}
try {
const order = await OrderModel.create({
phone: customer.phone,
carNumber: car.number,
carBody: car.body,
carColor: car.color,
startWashTime: washing.begin,
endWashTime: washing.end,
location: washing.location,
status: orderStatus.PROGRESS,
notes: '',
created: new Date().toISOString(),
})
res.status(200).send({ success: true, body: order })
} catch (error) {
next(error)
}
})
router.get('/:id', async (req, res, next) => {
const { id } = req.params
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
}
try {
const order = await OrderModel.findById(id)
if (!order) {
throw new Error(VALIDATION_MESSAGES.order.notFound)
}
const imgProps = await OrderCarImgModel.findOne({ orderId: order.id })
res.status(200).send({ success: true, body: { ...order.toObject(), ...imgProps?.toObject() } })
} catch (error) {
next(error)
}
})
router.patch('/:id', async (req, res, next) => {
const { id } = req.params
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
}
const bodyErrors = []
const { status } = req.body
if (status) {
if (!isValidOrderStatus(status)) {
bodyErrors.push(VALIDATION_MESSAGES.orderStatus.invalid)
}
}
const { master: masterId } = req.body
if (masterId) {
if (!mongoose.Types.ObjectId.isValid(masterId)) {
bodyErrors.push(VALIDATION_MESSAGES.masterId.invalid)
} else {
try {
const master = await MasterModel.findById(masterId)
if (!master) {
bodyErrors.push(VALIDATION_MESSAGES.master.notFound)
}
} catch (error) {
next(error)
}
}
}
const { notes } = req.body
if (notes) {
if (!isValidOrderNotes(notes)) {
bodyErrors.push(VALIDATION_MESSAGES.orderNotes.invalid)
}
}
if (bodyErrors.length > 0) {
throw new Error(bodyErrors.join(', '))
}
try {
const updateData = {}
if (status) {
updateData.status = status
}
if (masterId) {
updateData.master = masterId
}
if (notes) {
updateData.notes = notes
}
updateData.updated = new Date().toISOString()
const order = await OrderModel.findByIdAndUpdate(
id,
updateData,
{ new: true }
)
if (!order) {
throw new Error(VALIDATION_MESSAGES.order.notFound)
}
res.status(200).send({ success: true, body: order })
} catch (error) {
next(error)
}
})
router.delete('/:id', async (req, res, next) => {
const { id } = req.params
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
}
try {
const order = await OrderModel.findByIdAndDelete(id, {
new: true,
})
if (!order) {
throw new Error(VALIDATION_MESSAGES.order.notFound)
}
res.status(200).send({ success: true, body: order })
} catch (error) {
next(error)
}
})
const storage = multer.memoryStorage()
const upload = multer({
storage: storage,
limits: { fileSize: sizeLimitInMegaBytes * 1024 * 1024 },
fileFilter: (req, file, cb) => {
if (allowedMimeTypes.includes(file.mimetype)) {
cb(null, true)
} else {
cb(new Error(VALIDATION_MESSAGES.carImg.invalid.type), false)
}
}
})
const { v4: uuidv4 } = require("uuid")
const axios = require('axios')
const GIGA_CHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth'
const GIGA_CHAT_API = 'https://gigachat.devices.sberbank.ru/api/v1'
const getToken = async (req, res) => {
const gigaToken = await getGigaToken()
const rqUID = uuidv4()
const body = new URLSearchParams({
scope: "GIGACHAT_API_PERS",
})
const response = await fetch(GIGA_CHAT_OAUTH, {
method: "POST",
headers: {
Authorization: `Basic ${gigaToken}`,
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
RqUID: rqUID,
},
body,
})
if (!response.ok) {
const errorData = await response.json()
console.error("Ошибка запроса: ", errorData)
return res.status(response.status).json(errorData)
}
return await response.json()
}
const uploadImage = async (file, accessToken) => {
const formData = new FormData()
const blob = new Blob([file.buffer], { type: file.mimetype })
formData.append('file', blob, file.originalname)
formData.append('purpose', 'general')
const config = {
maxBodyLength: Infinity,
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json',
'Authorization': `Bearer ${accessToken}`
}
}
try {
const response = await axios.post(`${GIGA_CHAT_API}/files`, formData, config)
return response.data.id
} catch (error) {
console.error(error)
}
}
const COLORS_MAP = ['white', 'black', 'silver', 'gray', 'beige-brown', 'red', 'blue', 'green']
const getColorName = (colorKey) => {
if (typeof colorKey === 'number' && COLORS_MAP[colorKey]) {
return COLORS_MAP[colorKey]
}
return colorKey
}
const analyzeImage = async (fileId, token, imgProps) => {
const response = await fetch(`${GIGA_CHAT_API}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
model: (await getGigaChatModel()) ?? "GigaChat-Max",
stream: false,
update_interval: 0,
messages: [
{
role: "system",
content: (await getSystemPrompt()) ?? `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения.
Тебе предоставляют фотографию, где явно выделяется одна машина (например, она расположена в центре и имеет больший размер в кадре по сравнению с остальными).
ВАЖНО: Твой ответ ДОЛЖЕН быть СТРОГО в формате JSON и содержать ТОЛЬКО следующие поля:
{
"value": число от 0 до 10 (целое или с одним знаком после запятой),
"description": "текстовое описание на русском языке"
}.
Правила:
1. Поле "value":
- Должно быть числом от 0 до 10 (0 = машина абсолютно чистая, 10 = машина максимально грязная) ИЛИ undefined (если не удалось оценить);
2. Поле "description":
- Должно содержать 2-3 предложения на русском языке;
- Обязательно указать конкретные признаки загрязнения;
- Объяснить, почему выставлен именно такой балл.
- Должно быть связано только с автомобилем.
НЕ ДОБАВЛЯЙ никаких дополнительных полей или комментариев вне JSON структуры. НЕ ИСПОЛЬЗУЙ markdown форматирование. ОТВЕТ ДОЛЖЕН БЫТЬ ВАЛИДНЫМ JSON. Если на фотографии нельзя явно выделить одну машину, то ОЦЕНКА ДОЛЖНА ИМЕТЬ ЗНАЧЕНИЕ undefined и в описании должно быть указано, что по фотографии не удалось оценить степень загрязнения автомобиля, при этом НЕ ОПИСЫВАЙ НИЧЕГО ДРУГОГО КРОМЕ АВТОМОБИЛЯ`,
},
{
role: "user",
content: `Дай оценку для приложенного файла изображения согласно структуре, ответ должен быть на русском языке. Учти, что владелец указал, что исходный цвет машины: ${getColorName(imgProps.color)}`,
attachments: [fileId],
},
],
}),
})
const data = await response.json()
console.log(data)
try {
return JSON.parse(data.choices[0].message.content)
} catch (error) {
console.error(error)
return { description: data.choices[0].message.content }
}
}
const convertFileToBase64 = (file) => {
const base64Image = file.buffer.toString('base64')
return `data:${file.mimetype};base64,${base64Image}`
}
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => {
const { id: orderId } = req.params
if (!mongoose.Types.ObjectId.isValid(orderId)) {
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
}
const order = await OrderModel.findById(orderId)
if (!order) {
throw new Error(VALIDATION_MESSAGES.order.notFound)
}
if (!req.file) {
throw new Error(VALIDATION_MESSAGES.carImg.required)
}
try {
await OrderCarImgModel.deleteMany({ orderId })
const { access_token } = await getToken(req, res)
const fileId = await uploadImage(req.file, access_token)
const { value, description } = await analyzeImage(fileId, access_token, { carColor: order.carColor }) ?? {}
const orderCarImg = await OrderCarImgModel.create({
image: convertFileToBase64(req.file),
imageRating: value,
imageDescription: description,
orderId: order.id,
created: new Date().toISOString(),
})
res.status(200).send({ success: true, body: orderCarImg })
} catch (error) {
console.error(error)
}
})
router.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
switch (err.message) {
case 'File too large':
return res.status(400).json({ success: false, error: VALIDATION_MESSAGES.carImg.invalid.size })
default:
return res.status(400).json({ success: false, error: err.message })
}
}
throw new Error(err.message)
})
module.exports = router

View File

@@ -0,0 +1,15 @@
const router = require('express').Router();
router.get('/recipe-data', (request, response) => {
response.send(require('./json/recipe-data/success.json'))
})
router.get('/userpage-data', (req, res)=>{
res.send(require('./json/userpage-data/success.json'))
})
router.get('/homepage-data', (req, res)=>{
res.send(require('./json/homepage-data/success.json'))
})
module.exports = router;

View File

@@ -1,21 +0,0 @@
import { Router } from 'express';
import recipeData from './json/recipe-data/success.json';
import userpageData from './json/userpage-data/success.json';
import homepageData from './json/homepage-data/success.json';
const router = Router();
router.get('/recipe-data', (request, response) => {
response.send(recipeData)
})
router.get('/userpage-data', (req, res)=>{
res.send(userpageData)
})
router.get('/homepage-data', (req, res)=>{
res.send(homepageData)
})
export default router;

View File

@@ -1,12 +0,0 @@
const router = require("express").Router();
router.get('/game-links', (request, response) => {
response.send(require('./json/game-links/success.json'))
})
router.get('/4u2k-links', (request, response) => {
response.send(require('./json/4u2k-links/success.json'))
})
;
module.exports = router;

View File

@@ -1,31 +0,0 @@
{
"data":[
{
"type": "video",
"links": {
"l1": "https://www.youtube.com/embed/DsQMLrPdLf8?si=l9X57nHqaSYlxDFf",
"l2": "https://www.youtube.com/embed/Dk8AAU_UdVk?si=N8NdYMUCfawdsJGE",
"l3": "https://www.youtube.com/embed/HKfDfWrCwEA?si=qPugjiKR8V9eZ-yG",
"l4": "https://www.youtube.com/embed/tD-6xHAHrQ4?si=ZFe41gSK8d5gqahW"
}
},
{
"type": "podcast",
"links": {
"l1": "https://www.youtube.com/embed/RtVs87Nd1MQ?si=i4giUCtbp4Ouqv2W",
"l2": "https://www.youtube.com/embed/DfTU5LA_kw8?si=m7fI5Ie9yIGDFCrU",
"l3": "https://www.youtube.com/embed/Sp-1fX1Q15I?si=xyealVly9IBMW7Xi",
"l4": "https://www.youtube.com/embed/rLYFJYfluRs?si=MjW1beQ-Q9-TAehF"
}
},
{
"type": "entertainment",
"links": {
"l1": "https://www.youtube.com/embed/DiuuglRCchQ?si=8wTVXKbV-mbHuSjW",
"l2": "https://www.youtube.com/embed/zmZcIX5PEyo?si=Hbrv32kl0fqcmtV9",
"l3": "https://www.youtube.com/embed/Te-TZUjmzFQ?si=fNG16eruoFEY2KNq",
"l4": "https://www.youtube.com/embed/si-MQ5qg3zE?si=67mfO6gV80n1ULqo"
}
}
]
}

View File

@@ -1,20 +0,0 @@
{
"data":[
{
"title": "ABC",
"description": "Мой брат Колян сбацал про меня байку на англицком и несколько фишек кинул для шухера. Англицкий ты вроде знаешь, впряжешься за меня, а?",
"link": "https://www.oxfordonlineenglish.com/english-level-test/reading"
},
{
"title": "Алё, меня слышно?",
"description": "Мой кент на мобилу текст записал с иностранкой. Понимаешь, о чём тут говорят?",
"link": "https://test-english.com/listening/"
},
{
"title": "Анонимное тестирование",
"description": "Ты язык-то нормально знаешь? Проверься, никто угарать не будет",
"link": "https://www.ego4u.com/en/cram-up/tests"
}
]
}

View File

@@ -2,6 +2,5 @@ const router = require('express').Router();
router.use('/performer', require('./dashboard-performer'))
router.use('/auth', require('./auth'))
router.use('/landing', require('./landing'))
module.exports = router;

View File

@@ -1,29 +0,0 @@
const Router = require('express').Router;
const router = Router()
const values = {
'blocks': 'success',
'application': 'success'
}
const timer = (_req, _res, next) => {
setTimeout(() => next(), 500)
}
router.use(timer)
router.get(
'/blocks',
(req, res) =>
res.send(require(`./json/blocks-${values['blocks']}.json`))
)
router.post(
'/application',
(req, res) => {
res.send(require(`./json/application-${values['application']}.json`))
}
)
module.exports = router

View File

@@ -1,7 +0,0 @@
{
"success": false,
"body": { },
"errors": [
"Что-то пошло не так"
]
}

View File

@@ -1,4 +0,0 @@
{
"success": true,
"body": { }
}

View File

@@ -1,9 +0,0 @@
{
"success": false,
"body": {
"blocks": []
},
"errors": [
"Что-то пошло не так"
]
}

View File

@@ -1,22 +0,0 @@
{
"success": true,
"body": {
"blocks": [
{
"titleKey":"block1.title",
"textKey":"block1.subtitle",
"imageName":"truck1"
},
{
"titleKey":"block2.title",
"textKey":"block2.subtitle",
"imageName":"truck2"
},
{
"titleKey":"block3.title",
"textKey":"block3.subtitle",
"imageName":"truck3"
}
]
}
}

View File

@@ -4,254 +4,20 @@ router.get("/game-page", (request, response) => {
response.send(require("./json/gamepage/success.json"));
});
router.get("/update-like", (request, response) => {
response.send(require("./json/gamepage/success.json"));
});
router.get("/add-to-cart", (request, response) => {
response.send(require("./json/home-page-data/games-in-cart.json"));
});
router.get("/categories", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
response.send(require("./json/categories/success.json"));
});
router.get("/favourites", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
});
// router.get("/shopping-cart", (request, response) => {
// response.send(require("./json/shopping-cart/success.json"));
// });
router.get("/shopping-cart", (request, response) => {
response.send(require("./json/home-page-data/games-in-cart.json"));
response.send(require("./json/shopping-cart/success.json"));
});
// Добавляем поддержку разных ответов для /home
router.get("/home", (req, res) => {
if (stubs.home === "success") {
res.send(require("./json/home-page-data/success.json"));
} else if (stubs.home === "empty") {
res.send({ data: [] }); // Отправляем пустой массив
} else {
res.status(500).json({ success: false, message: "Server error" });
}
router.get("/home", (request, response) => {
response.send(require("./json/home-page-data/success.json"));
});
router.get("/all-games", (request, response) => {
response.send(require("./json/home-page-data/all-games.json"));
});
const stubs = {
home: "success",
};
// // Маршрут для обновления лайков
// router.post("/update-like", (request, response) => {
// const { username, likes } = request.body;
// // Эмулируем успешное обновление лайков
// console.log(`Лайки для пользователя ${username} обновлены до ${likes}`);
// response.status(200).json({
// success: true,
// message: `Лайки для пользователя ${username} обновлены до ${likes}`,
// });
// });
const fs = require("fs").promises;
const path = require("path");
// Path to JSON file
const commentsFilePath = path.join(__dirname, "./json/gamepage/success.json");
// Read JSON file
async function readComments() {
const data = await fs.readFile(commentsFilePath, "utf-8");
const parsedData = JSON.parse(data);
console.log("Прочитанные данные:", parsedData); // Логируем полученные данные
return parsedData;
}
// Write to JSON file
async function writeComments(data) {
await fs.writeFile(commentsFilePath, JSON.stringify(data, null, 2), "utf-8");
}
// Update likes route
router.post("/update-like", async (req, res) => {
const { username, likes } = req.body;
if (!username || likes === undefined) {
return res.status(400).json({ success: false, message: "Invalid input" });
}
try {
const data = await readComments();
const comment = data.data.comments.find((c) => c.username === username);
if (comment) {
comment.likes = likes;
await writeComments(data); // Сохраняем обновленные данные в файл
// Возвращаем актуализированные данные
res.status(200).json({
success: true,
message: "Likes updated successfully",
data: data.data, // Возвращаем актуализированные данные
});
} else {
res.status(404).json({ success: false, message: "Comment not found" });
}
} catch (error) {
console.error("Error updating likes:", error);
res.status(500).json({ success: false, message: "Server error" });
}
});
// Путь к JSON-файлу с корзиной
const cartFilePath = path.join(
__dirname,
"./json/home-page-data/games-in-cart.json"
);
// Функция для чтения JSON-файла
async function readCart() {
const data = await fs.readFile(cartFilePath, "utf-8");
return JSON.parse(data);
}
// Функция для записи в JSON-файл
async function writeCart(data) {
await fs.writeFile(cartFilePath, JSON.stringify(data, null, 2), "utf-8");
}
// Маршрут для добавления/удаления товара в корзине
router.post("/add-to-cart", async (req, res) => {
const { id, action } = req.body;
// Проверка наличия id и action
if (id === undefined || action === undefined) {
return res
.status(400)
.json({ success: false, message: "Invalid id or action" });
}
try {
const cartData = await readCart();
let ids = cartData.data.ids;
if (action === "add") {
// Если action "add", добавляем товар, если его нет в корзине
if (!ids?.includes(id)) {
ids.push(id);
}
} else if (action === "remove") {
// Если action "remove", удаляем товар, если он есть в корзине
if (ids?.includes(id)) {
ids = ids.filter((item) => item !== id);
}
} else {
// Если action невалиден
return res
.status(400)
.json({ success: false, message: "Invalid action" });
}
// Записываем обновленные данные обратно в файл
cartData.data.ids = ids;
await writeCart(cartData);
res.status(200).json({
success: true,
message: "Cart updated successfully",
data: cartData.data, // Возвращаем обновленные данные
});
} catch (error) {
console.error("Error updating cart:", error);
res.status(500).json({ success: false, message: "Server error" });
}
});
module.exports = router;
const createElement = (key, value, buttonTitle, basePath) => `
<label>
<input name="${key}" type="radio" ${
stubs[key] === value ? "checked" : ""
} onclick="fetch('${basePath}/admin/set/${key}/${value}')"/>
${buttonTitle || value}
</label>
`;
router.get("/admin/home", (request, response) => {
const basePath = request.baseUrl; // Получаем базовый путь маршрутизатора
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /home</legend>
${createElement("home", "success", "Отдать успешный ответ", basePath)}
${createElement("home", "empty", "Отдать пустой массив", basePath)}
${createElement("home", "error", "Отдать ошибку", basePath)}
</fieldset>
</div>
`);
});
router.get("/admin/game-page", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /game-page</legend>
${createElement(
"game-page",
"success",
"Отдать успешный ответ"
)}
${createElement("game-page", "empty", "Отдать пустой массив")}
${createElement("game-page", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/categories", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /categories</legend>
${createElement(
"categories",
"success",
"Отдать успешный ответ"
)}
${createElement("categories", "empty", "Отдать пустой массив")}
${createElement("categories", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/favourites", (request, response) => {
response.send(`
<div>
<fieldset>
<legend>Настройка данных для /favourites</legend>
${createElement(
"favourites",
"success",
"Отдать успешный ответ"
)}
${createElement("favourites", "empty", "Отдать пустой массив")}
${createElement("favourites", "error", "Отдать ошибку")}
</fieldset>
</div>
`);
});
router.get("/admin/set/:key/:value", (request, response) => {
const { key, value } = request.params;
stubs[key] = value;
response.send("Настройки обновлены!");
});

View File

@@ -0,0 +1,150 @@
{
"success": true,
"data": {
"games1": [
{
"id": 1,
"title": "How to Survive",
"price": 259,
"old_price": 500,
"image": "sales_game1",
"os": "windows"
},
{
"id": 2,
"title": "Red Solstice 2 Survivors",
"price": 561,
"image": "sales_game2",
"os": "windows"
},
{
"id": 3,
"title": "Sons Of The Forests",
"price": 820,
"old_price": 1100,
"image": "new_game2",
"os": "windows"
},
{
"id": 4,
"title": "The Witcher 3: Wild Hunt",
"price": 990,
"old_price": 1200,
"image": "leaders_game4",
"os": "windows"
},
{
"id": 5,
"title": "Atomic Heart",
"price": 1200,
"old_price": 2500,
"image": "leaders_game5",
"os": "windows"
},
{
"id": 6,
"title": "Crab Game",
"price": 600,
"old_price": 890,
"image": "leaders_game6",
"os": "windows"
}
],
"games2": [
{
"id": 1,
"title": "Alpha League",
"price": 299,
"image": "new_game1",
"os": "windows"
},
{
"id": 2,
"title": "Sons Of The Forests",
"price": 820,
"old_price": 1100,
"image": "new_game2",
"os": "windows"
},
{
"id": 3,
"title": "Pacific Drives",
"price": 1799,
"image": "new_game3",
"os": "windows"
},
{
"id": 4,
"title": "The Witcher 3: Wild Hunt",
"price": 990,
"old_price": 1200,
"image": "leaders_game4",
"os": "windows"
},
{
"id": 5,
"title": "Atomic Heart",
"price": 1200,
"old_price": 2500,
"image": "leaders_game5",
"os": "windows"
},
{
"id": 6,
"title": "Crab Game",
"price": 600,
"old_price": 890,
"image": "leaders_game6",
"os": "windows"
}
],
"games3": [
{
"id": 1,
"title": "Elden Ring",
"price": 3295,
"old_price": 3599,
"image": "leaders_game2",
"os": "windows"
},
{
"id": 2,
"title": "Counter-Strike 2",
"price": 479,
"image": "leaders_game1",
"os": "windows"
},
{
"id": 3,
"title": "PUBG: BATTLEGROUNDS",
"price": 199,
"image": "leaders_game3",
"os": "windows"
},
{
"id": 4,
"title": "The Witcher 3: Wild Hunt",
"price": 990,
"old_price": 1200,
"image": "leaders_game4",
"os": "windows"
},
{
"id": 5,
"title": "Atomic Heart",
"price": 1200,
"old_price": 2500,
"image": "leaders_game5",
"os": "windows"
},
{
"id": 6,
"title": "Crab Game",
"price": 600,
"old_price": 890,
"image": "leaders_game6",
"os": "windows"
}
]
}
}

View File

@@ -4,32 +4,21 @@
"comments": [
{
"username": ользователь1",
"text": "Текст комментария 1",
"likes": 13,
"rating": 8,
"date": "2025-03-01T10:00:00Z"
"text": "Текст комментария 1"
},
{
"username": ользователь2",
"text": "Текст комментария 2",
"likes": 10,
"rating": 7,
"date": "2025-01-01T10:00:00Z"
"text": "Текст комментария 2"
},
{
"username": ользователь3",
"text": "Текст комментария 3",
"likes": 4,
"rating": 3,
"date": "2025-02-01T10:00:00Z"
"text": "Текст комментария 3"
},
{
"username": ользователь4",
"text": "Текст комментария 4",
"likes": 18,
"rating": 2,
"date": "2025-12-01T10:00:00Z"
"text": "Текст комментария 4"
}
]
}
}
}

View File

@@ -3,183 +3,144 @@
"data": [
{
"id": 1,
"title": "Elden Ring",
"image": "game17",
"price": 3295,
"old_price": 3599,
"imgPath": "img_top_17",
"description": "Крупномасштабная RPG, действие которой происходит в обширном открытом мире c богатой мифологией и множеством опасных врагов.",
"name": "The Witcher 3: Wild Hunt",
"image": "game1",
"text": "$10",
"imgPath": "img_top_1",
"description": "Эпическая RPG с открытым миром, в которой Геральт из Ривии охотится на монстров и раскрывает политические заговоры.",
"category": "RPG"
},
{
"id": 2,
"title": "The Witcher 3: Wild Hunt",
"image": "game1",
"price": 990,
"old_price": 1200,
"imgPath": "img_top_1",
"description": "Эпическая RPG с открытым миром, в которой Геральт из Ривии охотится на монстров и раскрывает политические заговоры.",
"category": "RPG"
},
{
"id": 17,
"title": "Red Dead Redemption 2",
"name": "Red Dead Redemption 2",
"image": "game2",
"price": 980,
"old_price": 3800,
"text": "$10",
"imgPath": "img_top_2",
"description": "Приключенческая игра с открытым миром на Диком Западе, рассказывающая историю Артура Моргана.",
"category": "Adventures"
},
{
"id": 3,
"title": "Forza Horizon 5",
"name": "Forza Horizon 5",
"image": "game3",
"price": 1900,
"text": "$10",
"imgPath": "img_top_3",
"description": "Гоночная игра с огромным открытым миром, действие которой происходит в Мексике.",
"category": "Race"
},
{
"id": 4,
"title": "Atomic Heart",
"name": "Atomic Heart",
"image": "game4",
"price": 1200,
"old_price": 2500,
"text": "$10",
"imgPath": "img_top_4",
"description": "Экшен-шутер с элементами RPG, разворачивающийся в альтернативной Советской России.",
"category": "Shooters"
},
{
"id": 5,
"title": "Counter-Strike 2",
"name": "Counter-Strike 2",
"image": "game5",
"price": 479,
"text": "$10",
"imgPath": "img_top_5",
"description": "Популярный онлайн-шутер с соревновательным геймплеем и тактическими элементами.",
"category": "Shooters"
},
{
"id": 6,
"title": "Grand Theft Auto V",
"name": "Grand Theft Auto V",
"image": "game6",
"price": 700,
"text": "$10",
"imgPath": "img_top_6",
"description": "Игра с открытым миром, где можно погрузиться в криминальный мир Лос-Сантоса.",
"category": "Adventures"
},
{
"id": 7,
"title": "Assassins Creed IV: Black Flag",
"name": "Assassins Creed IV: Black Flag",
"image": "game7",
"price": 1100,
"text": "$10",
"imgPath": "img_top_7",
"description": "Приключенческая игра о пиратах и морских сражениях в эпоху золотого века пиратства.",
"category": "Adventures"
},
{
"id": 8,
"title": "Spider-Man",
"name": "Spider-Man",
"image": "game8",
"price": 3800,
"text": "$10",
"imgPath": "img_top_8",
"description": "Игра о супергерое Человеке-пауке с захватывающими битвами и паркуром по Нью-Йорку.",
"category": "Action"
},
{
"id": 9,
"title": "Assassins Creed Mirage",
"name": "Assassins Creed Mirage",
"image": "game9",
"price": 1600,
"text": "$10",
"imgPath": "img_top_9",
"description": "Приключенческая игра с упором на скрытность, вдохновленная классическими частями серии.",
"category": "Action"
},
{
"id": 10,
"title": "Assassins Creed Valhalla",
"name": "Assassins Creed Valhalla",
"image": "game10",
"price": 800,
"old_price": 2200,
"text": "$10",
"imgPath": "img_top_10",
"description": "RPG с открытым миром о викингах, включающая битвы, исследования и строительство поселений.",
"category": "RPG"
},
{
"id": 11,
"title": "ARK: Survival Evolved",
"name": "ARK: Survival Evolved",
"image": "game11",
"price": 790,
"text": "$10",
"imgPath": "img_top_11",
"description": "Выживание в открытом мире с динозаврами, строительством и многопользовательскими элементами.",
"category": "Simulators"
},
{
"id": 12,
"title": "FIFA 23",
"name": "FIFA 23",
"image": "game12",
"price": 3900,
"text": "$10",
"imgPath": "img_top_12",
"description": "Популярный футбольный симулятор с улучшенной графикой и реалистичным геймплеем.",
"category": "Sports"
},
{
"id": 13,
"title": "Dirt 5",
"name": "Dirt 5",
"image": "game13",
"price": 2300,
"text": "$10",
"imgPath": "img_top_13",
"description": "Аркадная гоночная игра с фокусом на ралли и внедорожных соревнованиях.",
"category": "Race"
},
{
"id": 14,
"title": "Cyberpunk 2077",
"name": "Cyberpunk 2077",
"image": "game14",
"price": 3400,
"text": "$10",
"imgPath": "img_top_14",
"description": "RPG в киберпанк-сеттинге с нелинейным сюжетом и детализированным открытым миром.",
"category": "RPG"
},
{
"id": 15,
"title": "Age of Empires IV",
"name": "Age of Empires IV",
"image": "game15",
"price": 3200,
"text": "$10",
"imgPath": "img_top_15",
"description": "Классическая стратегия в реальном времени с историческими кампаниями.",
"category": "Strategies"
},
{
"id": 16,
"title": "Civilization VI",
"name": "Civilization VI",
"image": "game16",
"price": 4200,
"text": "$10",
"imgPath": "img_top_16",
"description": "Глобальная пошаговая стратегия, в которой игроки строят и развивают цивилизации.",
"category": "Strategies"

View File

@@ -1,16 +0,0 @@
{
"success": true,
"data": {
"ids": [
3,
13,
1,
10,
4,
9,
15,
6,
7
]
}
}

View File

@@ -3,51 +3,43 @@
"data": {
"topSail": [
{
"id": 1,
"image": "game1",
"price": 1500,
"text": "$10",
"imgPath": "img_top_1"
},
{
"id": 2,
"image": "game2",
"price": 980,
"text": "$10",
"imgPath": "img_top_2"
},
{
"id": 3,
"image": "game3",
"price": 1900,
"text": "$10",
"imgPath": "img_top_3"
},
{
"id": 4,
"image": "game4",
"price": 1200,
"text": "$10",
"imgPath": "img_top_4"
},
{
"id": 5,
"image": "game5",
"price": 479,
"text": "$10",
"imgPath": "img_top_5"
},
{
"id": 6,
"image": "game6",
"price": 700,
"text": "$10",
"imgPath": "img_top_6"
},
{
"id": 7,
"image": "game7",
"price": 1100,
"text": "$10",
"imgPath": "img_top_7"
},
{
"id": 8,
"image": "game8",
"price": 3800,
"text": "$10",
"imgPath": "img_top_8"
}
],
@@ -105,26 +97,22 @@
{
"image": "news1",
"text": "Разработчики Delta Force: Hawk Ops представили крупномасштабный режим Havoc Warfare",
"imgPath": "img_news_1",
"link": "https://gamemag.ru/news/185583/delta-force-hawk-ops-gameplay-showcase-havoc-warfare"
"imgPath": "img_news_1"
},
{
"image": "news2",
"text": "Первый трейлер Assassins Creed Shadows — с темнокожим самураем в феодальной Японии",
"imgPath": "img_news_2",
"link": "https://stopgame.ru/newsdata/62686/pervyy_trailer_assassin_s_creed_shadows_s_temnokozhim_samuraem_v_feodalnoy_yaponii"
"imgPath": "img_news_2"
},
{
"image": "news3",
"text": "Призрак Цусимы» вышел на ПК — и уже ставит рекорды для Sony",
"imgPath": "img_news_3",
"link": "https://stopgame.ru/newsdata/62706/prizrak_cusimy_vyshel_na_pk_i_uzhe_stavit_rekordy_dlya_sony"
"imgPath": "img_news_3"
},
{
"image": "news4",
"text": "Авторы Skull and Bones расширяют планы на второй сезон",
"imgPath": "img_news_4",
"link": "https://stopgame.ru/newsdata/62711/avtory_skull_and_bones_rasshiryayut_plany_na_vtoroy_sezon"
"text": "Авторы Skull and Bones расширяют планы на второй сезо",
"imgPath": "img_news_4"
}
]
}

View File

@@ -1,3 +0,0 @@
exports.KAZAN_EXPLORE_RESULTS_MODEL_NAME = 'KAZAN_EXPLORE_RESULTS'
exports.TOKEN_KEY = "kazan-explore_top_secret_key_hbfhqf9jq9prg"

View File

@@ -1,32 +1,18 @@
const router = require('express').Router();
const { expressjwt } = require('express-jwt')
const axios = require('axios');
const jwt = require('jsonwebtoken')
const { ResultsModel } = require('./model/results')
const { TOKEN_KEY } = require('./const')
// First page
router.get('/getInfoAboutKazan', (request, response) => {
const lang = request.query.lang || 'ru';
const lang = request.query.lang || 'ru'; // Получаем язык из параметров запроса
try {
const data = require('./json/first/info-about-kazan/success.json');
const translatedData = data[lang] || data['ru'];
response.send(translatedData);
const data = require('./json/first/info-about-kazan/success.json'); // Загружаем весь JSON
const translatedData = data[lang] || data['ru']; // Выбираем перевод по языку или дефолтный
response.send(translatedData); // Отправляем перевод клиенту
} catch (error) {
response.status(500).send({ message: 'Internal server error' });
response.status(500).send({ message: 'Internal server error' }); // Ошибка в случае проблем с JSON
}
});
router.get('/getServices', (request, response) => {
const lang = request.query.lang || 'ru';
try {
const data = require(`./json/first/services/${lang}/success.json`);
response.send(data);
} catch (error) {
response.status(404).send({ message: 'Language not found' });
}
})
router.get('/getNews', (request, response) => {
const lang = request.query.lang || 'ru';
try {
@@ -39,13 +25,13 @@ router.get('/getNews', (request, response) => {
// Sport page
router.get('/getFirstText', (request, response) => {
const lang = request.query.lang || 'ru';
const lang = request.query.lang || 'ru'; // Получаем язык из параметров
try {
const data = require('./json/sport/first-text/success.json');
const translatedData = data[lang] || data['ru'];
const data = require('./json/sport/first-text/success.json'); // Загружаем JSON
const translatedData = data[lang] || data['ru']; // Берём перевод или дефолтный
response.send(translatedData);
} catch (error) {
response.status(404).send({ message: 'Language not found' });
response.status(404).send({ message: 'Language not found' }); // Обработка ошибки
}
});
@@ -70,16 +56,6 @@ router.get('/getSportData', (request, response) => {
}
})
router.get('/getSportQuiz', (request, response) => {
const lang = request.query.lang || 'ru';
try {
const data = require(`./json/sport/quiz/${lang}/success.json`);
response.send(data);
} catch (error) {
response.status(404).send({ message: 'Language not found' });
}
})
// Places page
router.get('/getPlacesData', (request, response) => {
const lang = request.query.lang || 'ru';
@@ -222,89 +198,4 @@ router.post('/registration', async (request, response) => {
}
});
router.post('/signin', async (req, res) => {
const { user } = req.body
if (!user || !user.token) {
return res.status(404).json({error : "No user found"});
}
const valRes = await axios.get('https://antd-table-v2-backend.onrender.com/api/auth/check',
{
headers: {
'authorization': `Bearer ${user.token}`
}
}
)
if (valRes.status !== 200) {
return res.status(401).json({error : "User authorization error"});
}
const accessToken = jwt.sign({
...JSON.parse(JSON.stringify(user._id)),
}, TOKEN_KEY, {
expiresIn: '12h'
})
user.token = accessToken;
res.json(user)
})
router.use(
expressjwt({
secret: TOKEN_KEY,
algorithms: ['HS256'],
getToken: function fromHeaderOrQuerystring(req) {
if (req.headers.authorization && req.headers.authorization.split(" ")[0] === "Bearer")
return req.headers.authorization.split(" ")[1];
else if (req.query && req.query.token)
return req.query.token;
return null;
}
})
)
router.get('/getQuizResults/:userId', async (request, response) => {
const { userId } = request.params;
try {
const results = await ResultsModel.findOne({ userId: userId }).exec();
if (!results)
return response.status(404).send({ message: 'Quiz results not found' });
response.send(results.items);
} catch (error) {
response.status(500).send({ message: 'An error occurred while fetching quiz results' });
}
});
router.post('/addQuizResult', async (request, response) => {
const { userId, quizId, result } = request.body;
if (!userId || !quizId || !result)
return response.status(400).send({ message: 'Invalid input data' });
try {
let userResults = await ResultsModel.findOne({ userId: userId }).exec();
if (!userResults) {
userResults = new ResultsModel({ userId, items: [] });
}
const itemToOverride = userResults.items.find(item => item.quizId === quizId)
if (!itemToOverride) {
userResults.items.push({ quizId, result });
}
else {
itemToOverride.result = result;
}
await userResults.save();
response.status(200).send({ message: 'Quiz result added successfully' });
} catch (error) {
response.status(500).send({ message: 'An error occurred while adding quiz result' });
}
});
module.exports = router;

View File

@@ -1,102 +0,0 @@
{
"banks": [
{
"name": "Sberbank of Russia",
"description": "One of the largest and most popular banks in Russia. There are many branches and ATMs in Kazan. Sberbank offers a wide range of services, including loans, deposits, insurance, business services, and online banking."
},
{
"name": "VTB",
"description": "The second largest bank in Russia, with many offices and ATMs in Kazan. VTB offers various financial products for individuals and businesses, including loans, deposits, investment solutions, and cards."
},
{
"name": "Tinkoff Bank",
"description": "Although Tinkoff does not have traditional offices in Kazan, its products and services are available in the city through online banking and remote services. Tinkoff offers favorable terms for credit cards, deposits, and services for small and medium-sized businesses."
},
{
"name": "Alfa-Bank",
"description": "One of the largest private banks in Russia, with offices in Kazan. Alfa-Bank offers standard banking services such as loans, deposits, cards, as well as investment and insurance products."
},
{
"name": "Rosselkhozbank",
"description": "Rosselkhozbank is also present in Kazan, specializing in servicing the agro-industrial complex, but also provides services for individuals and businesses, including loans, deposits, and cards."
},
{
"name": "RBC Bank",
"description": "A Russian bank with several offices and ATMs in Kazan. It offers loans, cards, deposits, and business services."
},
{
"name": "Bank Saint Petersburg",
"description": "A local bank that also provides services in Kazan. It offers a wide range of banking products for individuals and businesses."
}
],
"hospitals": [
{
"name": "Kazan City Clinical Hospital No. 1",
"description": "One of the largest multidisciplinary hospitals in Kazan, offering services in surgery, traumatology, neurology, cardiology, and other medical fields. Modern technologies and highly qualified staff."
},
{
"name": "Republican Clinical Hospital",
"description": "The main medical organization of the Republic of Tatarstan, providing a wide range of services for adults and children, including emergency care, high-tech surgeries, and diagnostics."
},
{
"name": "City Hospital No. 7",
"description": "A hospital specializing in providing medical care in therapeutic, surgical, and resuscitation medicine. The hospital employs experienced specialists and uses modern treatment methods."
},
{
"name": "Kazan Children's Clinical Hospital",
"description": "A specialized medical facility for children, providing services for the treatment of diseases related to pediatrics, surgery, cardiology, and other fields for children of all ages."
},
{
"name": "Kazan Oncology Dispensary",
"description": "A medical institution specializing in the treatment of oncological diseases. It uses the latest methods of cancer diagnosis and treatment, including chemotherapy, radiotherapy, and surgical interventions."
},
{
"name": "City Hospital No. 18",
"description": "A multidisciplinary medical institution offering treatment in various medical fields, including traumatology, neurology, and cardiology. The hospital has a rehabilitation department for patients recovering from serious diseases."
},
{
"name": "Republican Hospital for War Veterans",
"description": "A medical institution providing specialized care for World War II veterans, disabled individuals, and elderly people. It also offers a wide range of services for citizens with chronic diseases."
}
],
"pharmacies": [
{
"name": "Apteka 36.6",
"description": "A pharmacy chain with a wide range of medications, vitamins, cosmetics, and health products. Loyalty programs and online orders for customer convenience."
},
{
"name": "Rigla",
"description": "One of the largest pharmacy chains in Russia. It offers a wide range of medicines, medical products, and cosmetics. It also provides the option to order online."
},
{
"name": "Zdorovaya Semya",
"description": "A pharmacy chain focused on the sale of medicines and health products, including medical equipment. Often runs promotions and discounts on popular items."
},
{
"name": "A5 Pharmacy Chain",
"description": "Pharmacies offering a wide range of products, including medicines, vitamins, cosmetics, and children's products. Convenient delivery and online order services."
},
{
"name": "Samson-Pharma Pharmacy",
"description": "A pharmacy chain offering customers all necessary medicines and health products. Pharmacies offer various discount and bonus programs for regular customers."
},
{
"name": "Tsvetnoy Pharmacy Chain",
"description": "Pharmacies known for their convenient locations and high-quality service. They sell medicines, vitamins, self-care products, and medical equipment."
},
{
"name": "Doctor Stoletev Pharmacy",
"description": "A pharmacy chain focused on selling pharmaceutical products, medical goods, and cosmetics. Convenient service and promotions for customers."
}
],
"airports": [
{
"name": "Kazan International Airport",
"description": "The main airport of the city of Kazan, serving international and domestic flights. The airport is equipped with modern terminals, comfortable waiting areas, shops, and restaurants. It is one of the largest in the Volga region and an important transport hub for Tatarstan."
},
{
"name": "Kazan-2 (when it was operational)",
"description": "Previously used for domestic flights and military needs. It is no longer fully operational as all passenger flights have been redirected to Kazan International Airport. The airport building is closed for commercial air traffic."
}
]
}

View File

@@ -1,102 +0,0 @@
{
"banks": [
{
"name": "Сбербанк России",
"description": "Один из крупнейших и самых популярных банков в России. В Казани есть множество отделений и банкоматов. Сбербанк предлагает широкий спектр услуг, включая кредиты, депозиты, страхование, обслуживание бизнеса и онлайн-банкинг."
},
{
"name": "ВТБ",
"description": "Второй по величине банк в России, с большим количеством офисов и банкоматов в Казани. ВТБ предлагает различные финансовые продукты для физических и юридических лиц, включая кредиты, вклады, инвестиционные решения и карты."
},
{
"name": "Тинькофф Банк",
"description": "Несмотря на то что у Тинькофф нет традиционных офисов в Казани, его продукты и услуги доступны в городе через онлайн-банкинг и удаленное обслуживание. Тинькофф предлагает выгодные условия по кредитным картам, вклады, а также услуги для малого и среднего бизнеса."
},
{
"name": "Альфа-Банк",
"description": "Один из крупных частных банков в России, с офисами в Казани. Альфа-Банк предлагает стандартные банковские услуги, такие как кредиты, депозиты, карты, а также инвестиционные и страховые продукты."
},
{
"name": "Россельхозбанк",
"description": "В Казани также присутствует Россельхозбанк, специализирующийся на обслуживании агропромышленного комплекса, но также предоставляет услуги для физических и юридических лиц, включая кредиты, депозиты и карты."
},
{
"name": "РБК Банк",
"description": "Российский банк с рядом офисов и банкоматов в Казани. Предлагает кредиты, карты, депозиты, а также обслуживание для бизнеса."
},
{
"name": "Банк Санкт-Петербург",
"description": "Местный банк, который также предоставляет услуги в Казани. Предлагает широкий выбор банковских продуктов для частных лиц и бизнеса."
}
],
"hospitals": [
{
"name": "Казанская городская клиническая больница №1",
"description": "Одна из крупнейших многопрофильных больниц Казани, предлагающая услуги в области хирургии, травматологии, неврологии, кардиологии и других медицинских направлений. Современные технологии и высококвалифицированный персонал."
},
{
"name": "Республиканская клиническая больница",
"description": "Основная медицинская организация Республики Татарстан, предоставляющая широкий спектр услуг для взрослых и детей, включая экстренную помощь, высокотехнологичные операции и диагностику."
},
{
"name": "Городская больница №7",
"description": "Больница, специализирующаяся на оказании медицинской помощи в области терапевтической, хирургической и реанимационной медицины. В больнице работают опытные специалисты, используемые современные методы лечения."
},
{
"name": "Казанская детская клиническая больница",
"description": "Профильное медицинское учреждение для детей, которое предоставляет услуги по лечению заболеваний, связанных с педиатрией, хирургией, кардиологией и другими направлениями для детей всех возрастов."
},
{
"name": "Казанский онкологический диспансер",
"description": "Медицинское учреждение, специализирующееся на лечении онкологических заболеваний. Использует новейшие методы диагностики и лечения рака, включая химиотерапию, радиотерапию и операционные вмешательства."
},
{
"name": "Городская больница №18",
"description": "Многопрофильное медицинское учреждение, предлагающее лечение в различных областях медицины, включая травматологию, неврологию и кардиологию. В больнице есть отделение для реабилитации пациентов после тяжелых заболеваний."
},
{
"name": "Республиканская больница для ветеранов войн",
"description": "Медицинское учреждение, оказывающее специализированную помощь ветеранам Великой Отечественной войны, инвалидам и пожилым людям. Также предлагает широкий спектр услуг для граждан с хроническими заболеваниями."
}
],
"pharmacies": [
{
"name": "Аптека 36,6",
"description": "Сеть аптек с большим ассортиментом лекарственных средств, витаминов, косметики и товаров для здоровья. Программы лояльности и онлайн-заказы для удобства клиентов."
},
{
"name": "Ригла",
"description": "Одна из крупнейших аптечных сетей в России. Предлагает широкий выбор лекарств, медицинских товаров и косметики. Также предоставляет возможность заказа через интернет."
},
{
"name": "Здоровая семья",
"description": "Аптечная сеть, ориентированная на продажу лекарств и товаров для здоровья, включая медицинскую технику. Часто проводятся акции и скидки на популярные товары."
},
{
"name": "Аптечная сеть 'А5'",
"description": "Аптеки, предлагающие широкий ассортимент товаров, включая лекарства, витамины, косметику и товары для детей. Удобные услуги доставки и онлайн-заказов."
},
{
"name": "Аптека 'Самсон-Фарма'",
"description": "Сеть аптек, предоставляющая клиентам все необходимые лекарства и товары для здоровья. Аптеки предлагают различные программы скидок и бонусов для постоянных клиентов."
},
{
"name": "Аптечная сеть 'Цветной'",
"description": "Аптеки, известные своими удобными местоположениями и качественным обслуживанием. В продаже лекарства, витамины, товары для ухода за собой и медтехника."
},
{
"name": "Аптека 'Доктор Столетов'",
"description": "Сеть аптек, ориентированная на продажу фармацевтической продукции, медицинских товаров и косметики. Удобный сервис и акции для клиентов."
}
],
"airports": [
{
"name": "Международный аэропорт Казань",
"description": "Главный аэропорт города Казани, обслуживающий международные и внутренние рейсы. Аэропорт оснащен современными терминалами, удобными зонами ожидания, магазинами и ресторанами. Он является одним из крупнейших в Поволжье и важным транспортным узлом для Татарстана."
},
{
"name": "Казань-2 (когда был действующим)",
"description": "Ранее используемый аэропорт для внутренних рейсов и военных нужд. В настоящее время не функционирует в полном объеме, поскольку все пассажирские рейсы перенаправлены в Международный аэропорт Казань. Здание аэропорта закрыто для коммерческих авиаперевозок."
}
]
}

View File

@@ -1,102 +0,0 @@
{
"banks": [
{
"name": "Россия Сбербанкы",
"description": "Россиядәге иң зур һәм популяр банкларның берсе. Казан шәһәрендә күпсанлы бүлекләр һәм банкоматлар бар. Сбербанк киң спектрлы хезмәтләр тәкъдим итә, шул исәптән кредитлар, депозиты, иминиятләштерү, бизнеска хезмәт күрсәтү һәм онлайн-банкчылык."
},
{
"name": "ВТБ",
"description": "Россиядә икенче зурлыктагы банк, Казан шәһәрендә күп санлы офислар һәм банкоматлар белән. ВТБ физик һәм юридик затлар өчен төрле финанс продуктларын тәкъдим итә, шул исәптән кредитлар, депозитлар, инвестицион чишелешләр һәм карталар."
},
{
"name": "Тинькофф Банк",
"description": "Тинькофф Казан шәһәрендә традицион офисларга ия булмаса да, аның продуктлары һәм хезмәтләре шәһәрдә онлайн-банкчылык һәм ерак хезмәт күрсәтү аша тәкъдим ителә. Тинькофф кредит карталары, депозитлар, шулай ук кечкенә һәм урта бизнес өчен хезмәтләр тәкъдим итә."
},
{
"name": "Альфа-Банк",
"description": "Россиядәге зур шәхси банкларның берсе, Казанда офислары белән. Альфа-Банк стандарт банк хезмәтләрен тәкъдим итә, шул исәптән кредитлар, депозитлар, карталар, шулай ук инвестицион һәм иминият продуктлары."
},
{
"name": "Россельхозбанк",
"description": "Казан шәһәрендә Россельхозбанк та бар, ул агропромышленность өлкәсендә хезмәт күрсәтүгә махсуслашкан, ләкин шулай ук физик һәм юридик затлар өчен хезмәтләр тәкъдим итә, шул исәптән кредитлар, депозитлар һәм карталар."
},
{
"name": "РБК Банк",
"description": "Казан шәһәрендә офислары һәм банкоматлары булган Россия банкы. Кредитлар, карталар, депозитлар һәм бизнеска хезмәт күрсәтү тәкъдим итә."
},
{
"name": "Санкт-Петербург Банкы",
"description": "Казан шәһәрендә дә хезмәт күрсәткән җирле банк. Ул шәхси затлар һәм бизнес өчен киң банк продуктлары сайлау тәкъдим итә."
}
],
"hospitals": [
{
"name": "Казан шәһәр клиник хастаханәсе №1",
"description": "Казанның иң зур күппрофильле хастаханәләренең берсе, хирургия, травматология, неврология, кардиология һәм башка медицина юнәлешләре буенча хезмәтләр тәкъдим итә. Замана технологияләре һәм югары квалификацияле персонал."
},
{
"name": "Республиканың клиник хастаханәсе",
"description": "Татарстан Республикасы өчен төп медицина оешмасы, зур спектрдагы хезмәтләрне тәкъдим итә, шул исәптән ашыгыч ярдәм, югары технологияле операцияләр һәм диагностика."
},
{
"name": "Шәһәр хастаханәсе №7",
"description": "Терапевтик, хирургик һәм реанимация медицинасы өлкәсендә медицина ярдәме күрсәтүгә махсуслашкан хастаханә. Хастаханәдә тәҗрибәле белгечләр эшли, заманча дәвалау ысуллары кулланыла."
},
{
"name": "Казан балалар клиник хастаханәсе",
"description": "Балалар өчен профильле медицина учреждениесе, педиатрия, хирургия, кардиология һәм башка юнәлешләр буенча хезмәтләр тәкъдим итә."
},
{
"name": "Казан онкология диспансеры",
"description": "Онкологик авыруларны дәвалауга махсуслашкан медицина учреждениесе. Рак диагнозын һәм дәвалауны үткәрүдә заманча ысуллар кулланыла, шул исәптән химиотерапия, радиотерапия һәм операцияләр."
},
{
"name": "Шәһәр хастаханәсе №18",
"description": "Күппрофильле медицина учреждениесе, төрле медицина өлкәләрендә дәвалау тәкъдим итә, шул исәптән травматология, неврология һәм кардиология. Хастаханәдә авыр авырулардан соң реабилитация бүлекләре бар."
},
{
"name": "Ветераннар өчен республика хастаханәсе",
"description": "Бөек Ватан сугышы ветераннарына, инвалидларга һәм картларга махсус медицина ярдәме күрсәтүче учреждение. Шулай ук хроник авырулары булган гражданнар өчен хезмәтләр тәкъдим итә."
}
],
"pharmacies": [
{
"name": "Аптека 36,6",
"description": "Дәреслекләр, витаминнар, косметика һәм сәламәтлек товарларының киң ассортименты булган аптека челтәре. Лояльлек программалары һәм онлайн-заказлар клиентлар өчен уңайлы."
},
{
"name": "Ригла",
"description": "Россиядәге иң зур аптекалар челтәрләренең берсе. Дәреслекләр, медицина товарлары һәм косметика тәкъдим итә. Шулай ук интернет аша заказ бирү мөмкинлеге бар."
},
{
"name": "Здоровая семья",
"description": "Дәреслекләр һәм сәламәтлек товарлары, шул исәптән медицина техникасы сатуга юнәлдерелгән аптека челтәре. Популяр товарларга акцияләр һәм ташламалар еш үткәрелә."
},
{
"name": "Аптечная сеть 'А5'",
"description": "Дәреслекләр, витаминнар, косметика һәм балалар товарларының киң ассортименты булган аптека челтәре. Уңайлы җибәрү һәм онлайн-заказлар хезмәтләре."
},
{
"name": "Аптека 'Самсон-Фарма'",
"description": "Дәреслекләр һәм сәламәтлек товарлары тәкъдим итә торган аптека челтәре. Аптекалар даими клиентлар өчен скидкалар һәм бонуслар тәкъдим итә."
},
{
"name": "Аптечная сеть 'Цветной'",
"description": "Уңайлы урнашкан һәм сыйфатлы хезмәт күрсәтү белән танылган аптекалар. Дәреслекләр, витаминнар, үз-үзеңне карау товарлары һәм медицина техникасы сатыла."
},
{
"name": "Аптека 'Доктор Столетов'",
"description": "Фармацевтик продукция, медицина товарлары һәм косметика сату белән шөгыльләнгән аптека челтәре. Уңайлы хезмәт һәм клиентлар өчен акцияләр."
}
],
"airports": [
{
"name": "Казан Халыкара Аэропорты",
"description": "Казанның төп аэропорты, халыкара һәм эчке рейсларны башкаручы. Аэропорт заманча терминаллар, уңайлы көтү зоналары, кибетләр һәм рестораннар белән җиһазландырылган. Ул Поволжье төбәгендә иң зур аэропортларның берсе һәм Татарстан өчен мөһим транспорт узелы."
},
{
"name": "Казан-2 (эшләгән вакытта)",
"description": "Элекке эчке рейслар һәм хәрби кирәклекләр өчен кулланылган аэропорт. Хәзерге вакытта тулы көченә эшләми, чөнки барлык пассажир рейслары Казан Халыкара Аэропортына күчерелгән. Аэропорт бинасы коммерция авиаперевозкалары өчен ябык."
}
]
}

View File

@@ -1,908 +0,0 @@
{
"basketball": {
"intro_text": "Let's see how well you know UNICS!",
"intro_image": "intro_basket",
"questions": [
{
"question": "In which year was the UNICS basketball club founded?",
"options": [
"1988",
"1991",
"1995",
"2000"
],
"correct_answer": "1991",
"image_url": "b_one"
},
{
"question": "What does \"UNICS\" stand for?",
"options": [
"University, Innovations, Sports",
"University, Culture, Sports",
"University Kazan Sports",
"Unique Sports"
],
"correct_answer": "University, Culture, Sports",
"image_url": "b_two"
},
{
"question": "What are the main colors of the uniform?",
"options": [
"Red and white",
"Green and white",
"Blue and yellow",
"Black and gold"
],
"correct_answer": "Green and white",
"image_url": "b_three"
},
{
"question": "What is the name of UNICS's home arena?",
"options": [
"Tatneft Arena",
"Central Stadium",
"Basket-Hall",
"Kazan Arena"
],
"correct_answer": "Basket-Hall",
"image_url": "b_four"
},
{
"question": "In which year did UNICS win the EuroCup?",
"options": [
"2009",
"2011",
"2013",
"2015"
],
"correct_answer": "2011",
"image_url": "b_five"
},
{
"question": "Which famous American player, who became the Euroleague top scorer, did UNICS sign?",
"options": [
"Michael Jordan",
"Keith Langford",
"LeBron James",
"Curtis Jerrells"
],
"correct_answer": "Keith Langford",
"image_url": "b_six"
},
{
"question": "How many seats does UNICS's home arena, Basket-Hall, hold?",
"options": [
"3000",
"5000",
"7000",
"10000"
],
"correct_answer": "7000",
"image_url": "b_seven"
},
{
"question": "Who was the head coach of UNICS when the club won the EuroCup?",
"options": [
"Dimitris Itoudis",
"Evgeny Pashutin",
"Zoran Lukic",
"Andrei Kirilenko"
],
"correct_answer": "Evgeny Pashutin",
"image_url": "b_eight"
},
{
"question": "Who is UNICS's traditional biggest rival in the VTB United League?",
"options": [
"CSKA Moscow",
"Lokomotiv-Kuban",
"Zenit Saint Petersburg",
"Nizhny Novgorod"
],
"correct_answer": "CSKA Moscow",
"image_url": "b_nine"
},
{
"question": "Which trophy has UNICS not won yet?",
"options": [
"VTB United League",
"Russian Cup",
"Euroleague",
"EuroCup"
],
"correct_answer": "Euroleague",
"image_url": "b_ten"
},
{
"question": "Who is the head coach of UNICS in the 2024/2025 season?",
"options": [
"Alexander Volkov",
"Velimir Perasovic",
"Andrey Maltsov",
"Dimitris Priftis"
],
"correct_answer": "Velimir Perasovic",
"image_url": "b_eleven"
},
{
"question": "Which team was UNICS's opponent in the EuroCup 2011 final?",
"options": [
"Real Madrid",
"Caja Laboral",
"Lokomotiv-Kuban",
"Crvena Zvezda"
],
"correct_answer": "Caja Laboral",
"image_url": "b_twelve"
},
{
"question": "Since which year has UNICS participated in the Euroleague?",
"options": [
"2005",
"2008",
"2010",
"2011"
],
"correct_answer": "2011",
"image_url": "b_thirteen"
},
{
"question": "Which title is considered the greatest achievement of UNICS on the international stage?",
"options": [
"Euroleague Champion",
"EuroCup Champion",
"VTB United League Winner",
"Russian Cup Winner"
],
"correct_answer": "EuroCup Champion",
"image_url": "b_fourteen"
},
{
"question": "Which UNICS star has twice been the top scorer of the EuroCup?",
"options": [
"Keith Langford",
"Alexey Shved",
"Nando De Colo",
"Artem Parakhovsky"
],
"correct_answer": "Keith Langford",
"image_url": "b_fifteen"
},
{
"question": "How many times has UNICS won the Russian Cup?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "3",
"image_url": "b_sixteen"
},
{
"question": "What place did UNICS take in the VTB United League in the 2020/2021 season?",
"options": [
"1st",
"2nd",
"3rd",
"4th"
],
"correct_answer": "2nd",
"image_url": "b_seventeen"
},
{
"question": "In which year did UNICS win the VTB United League for the first time?",
"options": [
"2009",
"2011",
"2015",
"2023"
],
"correct_answer": "2023",
"image_url": "b_eighteen"
},
{
"question": "And now the most important question. How many of White's teeth did Labeyrie knock out in the VTB United League final? 😁",
"options": [
"1",
"2",
"3",
"4",
"It's not funny anymore."
],
"correct_answer": "4",
"image_url": "b_nineteen"
}
]
},
"hockey": {
"intro_text": "Let's find out how well you know Ak Bars!",
"intro_image": "intro_hockey",
"questions": [
{
"question": "In which year was the Ak Bars hockey club founded?",
"options": [
"1946",
"1956",
"1960",
"1990"
],
"correct_answer": "1956",
"image_url": "h_one"
},
{
"question": "What does the name \"Ak Bars\" mean?",
"options": [
"White snow leopard",
"Strong snow leopard",
"Fierce snow leopard",
"Ice snow leopard"
],
"correct_answer": "White snow leopard",
"image_url": "h_two"
},
{
"question": "What colors are traditionally used in Ak Bars' uniform?",
"options": [
"Red, green, and white",
"Blue, white, and black",
"Yellow, black, and white",
"Red, black, and white"
],
"correct_answer": "Red, green, and white",
"image_url": "h_three"
},
{
"question": "Which arena does Ak Bars play at?",
"options": [
"Basket-Hall",
"Tatneft Arena",
"Central Stadium",
"Kazan Arena"
],
"correct_answer": "Tatneft Arena",
"image_url": "h_four"
},
{
"question": "How many times has Ak Bars become the KHL champion?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "3",
"image_url": "h_five"
},
{
"question": "In which season did Ak Bars win the Gagarin Cup for the first time?",
"options": [
"2008/2009",
"2009/2010",
"2010/2011",
"2011/2012"
],
"correct_answer": "2008/2009",
"image_url": "h_six"
},
{
"question": "Which Ak Bars player is one of the club's top scorers of all time?",
"options": [
"Alexander Radulov",
"Danis Zaripov",
"Alexey Morozov",
"Evgeni Malkin"
],
"correct_answer": "Danis Zaripov",
"image_url": "h_seven"
},
{
"question": "Who is Ak Bars' rival in the Green Derby?",
"options": [
"Salavat Yulaev",
"Metallurg Magnitogorsk",
"Avangard Omsk",
"CSKA Moscow"
],
"correct_answer": "Salavat Yulaev",
"image_url": "h_eight"
},
{
"question": "What is the mascot of the Ak Bars hockey club?",
"options": [
"White tiger",
"Lion",
"Snow leopard",
"Bear"
],
"correct_answer": "Snow leopard",
"image_url": "h_nine"
},
{
"question": "Who was the head coach of Ak Bars in the 2023/2024 season?",
"options": [
"Dmitry Kvartalnov",
"Zinetula Bilyaletdinov",
"Andrei Skabelka",
"Valery Bragin"
],
"correct_answer": "Zinetula Bilyaletdinov",
"image_url": "h_ten"
},
{
"question": "How many times did Ak Bars win the Russian Championship before the KHL was created?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "2",
"image_url": "h_eleven"
},
{
"question": "Which of the following players did not play for Ak Bars?",
"options": [
"Ilya Nikulin",
"Sergey Mozyaikin",
"Alexey Tereshchenko",
"Nikita Zaitsev"
],
"correct_answer": "Sergey Mozyaikin",
"image_url": "h_twelve"
},
{
"question": "Which team was Ak Bars' opponent in the 2018 Gagarin Cup final?",
"options": [
"Avangard Omsk",
"Salavat Yulaev",
"CSKA Moscow",
"SKA St. Petersburg"
],
"correct_answer": "CSKA Moscow",
"image_url": "h_thirteen"
},
{
"question": "Who holds the record for most games played for Ak Bars?",
"options": [
"Danis Zaripov",
"Ilya Nikulin",
"Alexey Morozov",
"Viktor Tikhonov"
],
"correct_answer": "Ilya Nikulin",
"image_url": "h_fourteen"
},
{
"question": "Which coach led Ak Bars to their first KHL championship?",
"options": [
"Zinetula Bilyaletdinov",
"Vladimir Krikunov",
"Dmitry Kvartalnov",
"Alexander Medvedev"
],
"correct_answer": "Zinetula Bilyaletdinov",
"image_url": "h_fifteen"
},
{
"question": "What nickname do Ak Bars fans give to their team?",
"options": [
"White Bars",
"Green Machines",
"Kazan Bars",
"Green Army"
],
"correct_answer": "Green Army",
"image_url": "h_sixteen"
}
]
},
"football": {
"intro_text": "Let's find out how well you know Rubin!",
"intro_image": "intro_football",
"questions": [
{
"question": "In which year was Rubin football club founded?",
"options": [
"1936",
"1958",
"1964",
"1972"
],
"correct_answer": "1958",
"image_url": "f_one"
},
{
"question": "What colors are traditionally used in Rubin's kit?",
"options": [
"Red and white",
"Green and white",
"Red and green",
"Yellow and blue"
],
"correct_answer": "Red and green",
"image_url": "f_two"
},
{
"question": "What is the home stadium of FC Rubin?",
"options": [
"Kazan Arena",
"Central Stadium",
"Tatneft Arena",
"Ak Bars Arena"
],
"correct_answer": "Ak Bars Arena",
"image_url": "f_three"
},
{
"question": "In which year did Rubin first become the Russian champion?",
"options": [
"2006",
"2008",
"2009",
"2010"
],
"correct_answer": "2008",
"image_url": "f_four"
},
{
"question": "How many times has FC Rubin become the Russian champion?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "2",
"image_url": "f_five"
},
{
"question": "Who was the head coach of Rubin during their first championship in 2008?",
"options": [
"Guus Hiddink",
"Kurban Berdyev",
"Yuri Semin",
"Valery Karpin"
],
"correct_answer": "Kurban Berdyev",
"image_url": "f_six"
},
{
"question": "What was the score when Rubin defeated Barcelona in their famous Champions League win in 2009?",
"options": [
"2:1",
"1:0",
"3:0",
"3:1"
],
"correct_answer": "2:1",
"image_url": "f_seven"
},
{
"question": "Which player became Rubin's top scorer in the 2009/2010 season?",
"options": [
"Alexander Bukharov",
"Salomon Rondon",
"Gökdeniz Karadeniz",
"Sergey Semak"
],
"correct_answer": "Alexander Bukharov",
"image_url": "f_eight"
},
{
"question": "What position did Rubin finish in their debut season in the UEFA Champions League group stage?",
"options": [
"1st",
"2nd",
"3rd",
"4th"
],
"correct_answer": "3rd",
"image_url": "f_nine"
},
{
"question": "Who became Rubin's captain in the 2023/2024 season?",
"options": [
"Ilzat Akhmetov",
"Oleg Danchenko",
"Andres Pankov",
"Dmitry Tarasov"
],
"correct_answer": "Ilzat Akhmetov",
"image_url": "f_ten"
},
{
"question": "Which Russian coach made his debut as Rubin's head coach after Kurban Berdyev's departure?",
"options": [
"Sergey Semak",
"Roman Sharonov",
"Viktor Goncharenko",
"Alexander Borodyuk"
],
"correct_answer": "Roman Sharonov",
"image_url": "f_eleven"
},
{
"question": "In which tournament did Rubin defeat Atletico Madrid in 2012?",
"options": [
"Europa League",
"UEFA Super Cup",
"Intertoto Cup",
"Russian Cup"
],
"correct_answer": "Europa League",
"image_url": "f_twelve"
},
{
"question": "What is Rubin's greatest achievement in European competitions?",
"options": [
"Europa League Semi-final",
"Champions League Quarter-final",
"Europa League Quarter-final",
"Champions League Group Stage"
],
"correct_answer": "Europa League Quarter-final",
"image_url": "f_thirteen"
},
{
"question": "Which Rubin player scored a magnificent goal from the center of the field against Barcelona?",
"options": [
"Gökdeniz Karadeniz",
"Roman Eremenko",
"Alexander Ryazantsev",
"Alexander Bukharov"
],
"correct_answer": "Alexander Ryazantsev",
"image_url": "f_fourteen"
},
{
"question": "Who became Rubin's top scorer in the club's history?",
"options": [
"Alexander Bukharov",
"Salomon Rondon",
"Gökdeniz Karadeniz",
"Sergey Semak"
],
"correct_answer": "Gökdeniz Karadeniz",
"image_url": "f_fifteen"
},
{
"question": "Which club became Rubin's main rival in the 2010s?",
"options": [
"Zenit",
"Spartak",
"Lokomotiv",
"CSKA"
],
"correct_answer": "Zenit",
"image_url": "f_sixteen"
}
]
},
"volleyball": {
"intro_text": "Let's find out how well you know volleyball teams Zenit-Kazan and Dynamo-Ak Bars!",
"intro_image": "intro_volleyball",
"questions": [
{
"question": "In which year was the volleyball club Zenit-Kazan founded?",
"options": [
"2000",
"2003",
"2007",
"2010"
],
"correct_answer": "2000",
"image_url": "v_one"
},
{
"question": "How many times has Zenit-Kazan won the Champions League?",
"options": [
"4",
"5",
"6",
"7"
],
"correct_answer": "6",
"image_url": "v_two"
},
{
"question": "Who is the most decorated player in the history of Zenit-Kazan?",
"options": [
"Maxim Mikhaylov",
"Wilfredo Leon",
"Alexey Verbov",
"Matthew Anderson"
],
"correct_answer": "Maxim Mikhaylov",
"image_url": "v_three"
},
{
"question": "What is the home arena of Zenit-Kazan?",
"options": [
"Tatneft Arena",
"Basket-Hall",
"Saint Petersburg Volleyball Center",
"Kazan Arena"
],
"correct_answer": "Saint Petersburg Volleyball Center",
"image_url": "v_four"
},
{
"question": "Who was the head coach of Zenit-Kazan during their dominance in the 2010s?",
"options": [
"Alexey Verbov",
"Vladimir Alekno",
"Dante Amaral",
"Mikhail Likhachev"
],
"correct_answer": "Vladimir Alekno",
"image_url": "v_five"
},
{
"question": "What colors are used in Zenit-Kazan's uniform?",
"options": [
"Blue and white",
"Blue and silver",
"Green and white",
"Red and blue"
],
"correct_answer": "Blue and white",
"image_url": "v_six"
},
{
"question": "In which year did Zenit-Kazan win their first Russian championship?",
"options": [
"2005",
"2007",
"2009",
"2011"
],
"correct_answer": "2007",
"image_url": "v_seven"
},
{
"question": "When was the women's team Dynamo-Ak Bars founded?",
"options": [
"2002",
"2007",
"2012",
"2015"
],
"correct_answer": "2002",
"image_url": "v_eight"
},
{
"question": "What is the most significant achievement of Dynamo-Ak Bars?",
"options": [
"Victory in the Champions League",
"Victory in the Russian Championship",
"Victory in the Russian Cup",
"Victory in the CEV Cup"
],
"correct_answer": "Victory in the Russian Championship",
"image_url": "v_nine"
},
{
"question": "In which year did the women's team Dynamo-Ak Bars win their first Russian Cup?",
"options": [
"2008",
"2010",
"2012",
"2014"
],
"correct_answer": "2010",
"image_url": "v_ten"
},
{
"question": "How many times has the women's team Dynamo-Ak Bars won the Russian Championship?",
"options": [
"5",
"2",
"7",
"4"
],
"correct_answer": "7",
"image_url": "v_eleven"
},
{
"question": "Which of these volleyball stars played for Zenit-Kazan?",
"options": [
"Wilfredo Leon",
"Matey Kazijski",
"Giba",
"Trevor Clevery"
],
"correct_answer": "Wilfredo Leon",
"image_url": "v_twelve"
}
]
},
"water-polo": {
"intro_text": "Let's find out how well you know 'Sintez'!",
"intro_image": "intro_water_polo",
"questions": [
{
"question": "In which year was the water polo club Sintez founded?",
"options": [
"1974",
"1980",
"1985",
"1991"
],
"correct_answer": "1974",
"image_url": "w_one"
},
{
"question": "How many times has Sintez won the Russian water polo championship?",
"options": [
"5",
"8",
"10",
"12"
],
"correct_answer": "10",
"image_url": "w_two"
},
{
"question": "In which year did Sintez win the European Water Polo League Cup (LEN Trophy)?",
"options": [
"2005",
"2007",
"2010",
"2012"
],
"correct_answer": "2007",
"image_url": "w_three"
},
{
"question": "How many players are there from one team in the water polo game at the same time?",
"options": [
"5",
"6",
"7",
"8"
],
"correct_answer": "7",
"image_url": "w_four"
},
{
"question": "What is the standard length of a water polo pool?",
"options": [
"20 meters",
"25 meters",
"30 meters",
"50 meters"
],
"correct_answer": "30 meters",
"image_url": "w_five"
},
{
"question": "Which team is Sintez's main rival in Russia?",
"options": [
"Spartak Volgograd",
"Dynamo Moscow",
"Lokomotiv Krasnodar",
"Zenit St. Petersburg"
],
"correct_answer": "Spartak Volgograd",
"image_url": "w_six"
}
]
},
"history": {
"intro_text": "Let's see how well you know UNICS!",
"intro_image": "culture",
"questions": [
{
"question": "When was Kazan founded?",
"options": [
"1005",
"1156",
"1230",
"1323"
],
"correct_answer": "1005",
"image_url": "culture"
},
{
"question": "What is the main river flowing through Kazan?",
"options": [
"Volga",
"Kazanka",
"Kama",
"Izh"
],
"correct_answer": "Kazanka",
"image_url": "culture"
},
{
"question": "Who was the first khan of Kazan?",
"options": [
"Ulugh Muhammad",
"Akhmat",
"Shah Ali",
"Mamuka"
],
"correct_answer": "Ulugh Muhammad",
"image_url": "culture"
},
{
"question": "What is the name of Kazan's main sports complex where the 2013 Universiade was held?",
"options": [
"Kazan Arena",
"Tatneft Arena",
"Bugulma Arena",
"Ak Bars Sports Palace"
],
"correct_answer": "Kazan Arena",
"image_url": "culture"
},
{
"question": "Which mosque in Kazan is considered one of the largest in Russia?",
"options": [
"Kul Sharif Mosque",
"Mari El Mosque",
"Aisha Mosque",
"Imam Muhammad Mosque"
],
"correct_answer": "Kul Sharif Mosque",
"image_url": "culture"
},
{
"question": "What is the name of the square where the Kazan Kremlin and Kul Sharif Mosque are located?",
"options": [
"Vakhitov Square",
"Freedom Square",
"Kremlin Square",
"Revolution Square"
],
"correct_answer": "Kremlin Square",
"image_url": "culture"
},
{
"question": "What symbol of Kazan is depicted on the city's coat of arms?",
"options": [
"Dragon",
"Tiger",
"Lion",
"Eagle"
],
"correct_answer": "Dragon",
"image_url": "culture"
},
{
"question": "Who was the architect of the Kazan Kremlin?",
"options": [
"Ivan Zarudny",
"Fyodor Benjamin",
"Andrey Ushakov",
"Yury Dashevsky"
],
"correct_answer": "Andrey Ushakov",
"image_url": "culture"
},
{
"question": "Which of these universities is located in Kazan?",
"options": [
"Moscow State University",
"Kazan Federal University",
"St. Petersburg Polytechnic University",
"Novosibirsk State University"
],
"correct_answer": "Kazan Federal University",
"image_url": "culture"
},
{
"question": "What is the name of the largest street in Kazan, which is also the center of the city's nightlife?",
"options": [
"Kremlin Street",
"Bauman Street",
"Pushkin Street",
"Kayum Nasyri Street"
],
"correct_answer": "Bauman Street",
"image_url": "culture"
}
]
}
}

View File

@@ -1,908 +0,0 @@
{
"basketball": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете UNICS!",
"intro_image": "intro_basket",
"questions": [
{
"question": "В каком году был основан баскетбольный клуб UNICS?",
"options": [
"1988",
"1991",
"1995",
"2000"
],
"correct_answer": "1991",
"image_url": "b_one"
},
{
"question": "Как расшифровывается \"UNICS\"?",
"options": [
"Университет, Инновации, Спорт",
"Университет, Культура, Спорт",
"Университетский Казанский Спорт",
"Уникальный Спорт"
],
"correct_answer": "Университет, Культура, Спорт",
"image_url": "b_two"
},
{
"question": "Какие цвета являются основными в форме?",
"options": [
"Красный и белый",
"Зелёный и белый",
"Синий и жёлтый",
"Чёрный и золотой"
],
"correct_answer": "Зелёный и белый",
"image_url": "b_three"
},
{
"question": "Как называется домашняя арена БК UNICS?",
"options": [
"Татнефть Арена",
"Центральный стадион",
"Баскет-холл",
"Казань Арена"
],
"correct_answer": "Баскет-холл",
"image_url": "b_four"
},
{
"question": "В каком году UNICS выиграл Кубок Европы (EuroCup)?",
"options": [
"2009",
"2011",
"2013",
"2015"
],
"correct_answer": "2011",
"image_url": "b_five"
},
{
"question": "Какого известного американского игрока, ставшего лучшим бомбардиром Евролиги, подписывал UNICS?",
"options": [
"Майкл Джордан",
"Кит Лэнгфорд",
"Леброн Джеймс",
"Куртис Джерреллс"
],
"correct_answer": "Кит Лэнгфорд",
"image_url": "b_six"
},
{
"question": "Сколько мест вмещает домашняя арена UNICS, Баскет-холл?",
"options": [
"3000",
"5000",
"7000",
"10000"
],
"correct_answer": "7000",
"image_url": "b_seven"
},
{
"question": "Кто был главным тренером UNICS, когда клуб выиграл Еврокубок?",
"options": [
"Димитрис Итудис",
"Евгений Пашутин",
"Зоран Лукич",
"Андрей Кириленко"
],
"correct_answer": "Евгений Пашутин",
"image_url": "b_eight"
},
{
"question": "С кем у УНИКСа традиционно самое принципиальное противостояние в Единой Лиге ВТБ?",
"options": [
"ЦСКА Москва",
"Локомотив-Кубань",
"Зенит Санкт-Петербург",
"Нижний Новгород"
],
"correct_answer": "ЦСКА Москва",
"image_url": "b_nine"
},
{
"question": "Какой трофей UNICS ещё не выиграл?",
"options": [
"Единая лига ВТБ",
"Кубок России",
"Евролига",
"Еврокубок"
],
"correct_answer": "Евролига",
"image_url": "b_ten"
},
{
"question": "Кто является главным тренером UNICS в сезоне 2024/2025?",
"options": [
"Александр Волков",
"Велимир Перасович",
"Андрей Мальцев",
"Димитрис Прифтис"
],
"correct_answer": "Велимир Перасович",
"image_url": "b_eleven"
},
{
"question": "Какая команда была соперником UNICS в финале Кубка Европы 2011 года?",
"options": [
"Реал Мадрид",
"Каха Лабораль",
"Локомотив-Кубань",
"Црвена Звезда"
],
"correct_answer": "Каха Лабораль",
"image_url": "b_twelve"
},
{
"question": "С какого года UNICS участвует в Евролиге?",
"options": [
"2005",
"2008",
"2010",
"2011"
],
"correct_answer": "2011",
"image_url": "b_thirteen"
},
{
"question": "Какой титул считается главным достижением UNICS на международной арене?",
"options": [
"Чемпион Евролиги",
"Чемпион Кубка Европы",
"Победа в Единой лиге ВТБ",
"Победа в Кубке России"
],
"correct_answer": "Чемпион Кубка Европы",
"image_url": "b_fourteen"
},
{
"question": "Какая звезда UNICS дважды становилась лучшим бомбардиром Еврокубка?",
"options": [
"Кит Лэнгфорд",
"Алексей Швед",
"Нандо Де Коло",
"Артём Параховский"
],
"correct_answer": "Кит Лэнгфорд",
"image_url": "b_fifteen"
},
{
"question": "Сколько раз UNICS выигрывал Кубок России?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "3",
"image_url": "b_sixteen"
},
{
"question": "Какое место занял UNICS в Единой лиге ВТБ в сезоне 2020/2021?",
"options": [
"1-е",
"2-е",
"3-е",
"4-е"
],
"correct_answer": "2-е",
"image_url": "b_seventeen"
},
{
"question": "В каком году УНИКС впервые выиграл Единую Лигу ВТБ?",
"options": [
"2009",
"2011",
"2015",
"2023"
],
"correct_answer": "2023",
"image_url": "b_eighteen"
},
{
"question": "А сейчас самый главный вопрос. Сколько зубов выбил Лабери Уайту в финале ЕЛВТБ в 2023 году😁?",
"options": [
"1",
"2",
"3",
"4",
"уже не смешно вообще-то."
],
"correct_answer": "4",
"image_url": "b_nineteen"
}
]
},
"hockey": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете Ак Барс!",
"intro_image": "intro_hockey",
"questions": [
{
"question": "В каком году был основан хоккейный клуб Ак Барс?",
"options": [
"1946",
"1956",
"1960",
"1990"
],
"correct_answer": "1956",
"image_url": "h_one"
},
{
"question": "Что означает название \"Ак Барс\"?",
"options": [
"Белый барс",
"Сильный барс",
"Свирепый барс",
"Ледяной барс"
],
"correct_answer": "Белый барс",
"image_url": "h_two"
},
{
"question": "Какие цвета традиционно используются в форме Ак Барса?",
"options": [
"Красный, зелёный и белый",
"Синий, белый и чёрный",
"Жёлтый, чёрный и белый",
"Красный, чёрный и белый"
],
"correct_answer": "Красный, зелёный и белый",
"image_url": "h_three"
},
{
"question": "На какой арене выступает Ак Барс?",
"options": [
"Баскет-холл",
"Татнефть Арена",
"Центральный стадион",
"Казань Арена"
],
"correct_answer": "Татнефть Арена",
"image_url": "h_four"
},
{
"question": "Сколько раз Ак Барс становился чемпионом КХЛ?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "3",
"image_url": "h_five"
},
{
"question": "В каком сезоне Ак Барс впервые выиграл Кубок Гагарина?",
"options": [
"2008/2009",
"2009/2010",
"2010/2011",
"2011/2012"
],
"correct_answer": "2008/2009",
"image_url": "h_six"
},
{
"question": "Какой игрок Ак Барса является одним из лучших бомбардиров клуба за всю историю?",
"options": [
"Александр Радулов",
"Данис Зарипов",
"Алексей Морозов",
"Евгений Малкин"
],
"correct_answer": "Данис Зарипов",
"image_url": "h_seven"
},
{
"question": "Кто является соперником Ак Барса в Зеленом дерби дерби?",
"options": [
"Салават Юлаев",
"Металлург Магнитогорск",
"Авангард Омск",
"ЦСКА Москва"
],
"correct_answer": "Салават Юлаев",
"image_url": "h_eight"
},
{
"question": "Какой талисман представлен у хоккейного клуба Ак Барс?",
"options": [
"Белый тигр",
"Лев",
"Барс",
"Медведь"
],
"correct_answer": "Барс",
"image_url": "h_nine"
},
{
"question": "Кто был главным тренером Ак Барса в сезоне 2023/2024?",
"options": [
"Дмитрий Квартальнов",
"Зинэтула Билялетдинов",
"Андрей Скабелка",
"Валерий Брагин"
],
"correct_answer": "Зинэтула Билялетдинов",
"image_url": "h_ten"
},
{
"question": "Сколько раз Ак Барс выигрывал чемпионат России до создания КХЛ?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "2",
"image_url": "h_eleven"
},
{
"question": "Кто из следующих игроков не играл за Ак Барс?",
"options": [
"Илья Никулин",
"Сергей Мозякин",
"Алексей Терещенко",
"Никита Зайцев"
],
"correct_answer": "Сергей Мозякин",
"image_url": "h_twelve"
},
{
"question": "Какая команда стала соперником Ак Барса в финале Кубка Гагарина 2018 года?",
"options": [
"Авангард Омск",
"Салават Юлаев",
"ЦСКА Москва",
"СКА Санкт-Петербург"
],
"correct_answer": "ЦСКА Москва",
"image_url": "h_thirteen"
},
{
"question": "Кто является рекордсменом Ак Барса по числу игр в составе команды?",
"options": [
"Данис Зарипов",
"Илья Никулин",
"Алексей Морозов",
"Виктор Тихонов"
],
"correct_answer": "Илья Никулин",
"image_url": "h_fourteen"
},
{
"question": "Какой тренер привёл Ак Барс к их первому чемпионству в КХЛ?",
"options": [
"Зинэтула Билялетдинов",
"Владимир Крикунов",
"Дмитрий Квартальнов",
"Александр Медведев"
],
"correct_answer": "Зинэтула Билялетдинов",
"image_url": "h_fifteen"
},
{
"question": "Какое прозвище имеет команда Ак Барс среди своих фанатов?",
"options": [
"Белые Барсы",
"Зелёные Машины",
"Казанские Барсы",
"Зелёная Армия"
],
"correct_answer": "Зелёная Армия",
"image_url": "h_sixteen"
}
]
},
"football": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете Рубин!",
"intro_image": "intro_football",
"questions": [
{
"question": "В каком году был основан футбольный клуб Рубин?",
"options": [
"1936",
"1958",
"1964",
"1972"
],
"correct_answer": "1958",
"image_url": "f_one"
},
{
"question": "Какие цвета традиционно используются в форме Рубина?",
"options": [
"Красный и белый",
"Зелёный и белый",
"Красный и зелёный",
"Жёлтый и синий"
],
"correct_answer": "Красный и зелёный",
"image_url": "f_two"
},
{
"question": "Как называется домашний стадион ФК Рубин?",
"options": [
"Казань Арена",
"Центральный стадион",
"Татнефть Арена",
"Ак Барс Арена"
],
"correct_answer": "Ак Барс Арена",
"image_url": "f_three"
},
{
"question": "В каком году Рубин впервые стал чемпионом России?",
"options": [
"2006",
"2008",
"2009",
"2010"
],
"correct_answer": "2008",
"image_url": "f_four"
},
{
"question": "Сколько раз ФК Рубин становился чемпионом России?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "2",
"image_url": "f_five"
},
{
"question": "Кто был главным тренером Рубина во время их первого чемпионства в 2008 году?",
"options": [
"Гус Хиддинк",
"Курбан Бердыев",
"Юрий Сёмин",
"Валерий Карпин"
],
"correct_answer": "Курбан Бердыев",
"image_url": "f_six"
},
{
"question": "С каким счетом Рубин обыграл Барселону в их знаменитой победе в Лиге чемпионов в 2009 году?",
"options": [
"2:1",
"1:0",
"3:0",
"3:1"
],
"correct_answer": "2:1",
"image_url": "f_seven"
},
{
"question": "Какой игрок стал лучшим бомбардиром Рубина в сезоне 2009/2010?",
"options": [
"Александр Бухаров",
"Саломон Рондон",
"Гёкдениз Карадениз",
"Сергей Семак"
],
"correct_answer": "Александр Бухаров",
"image_url": "f_eight"
},
{
"question": "Какое место в группе занял Рубин в своём дебютном сезоне в Лиге чемпионов УЕФА?",
"options": [
"1-е",
"2-е",
"3-е",
"4-е"
],
"correct_answer": "3-е",
"image_url": "f_nine"
},
{
"question": "Кто стал капитаном Рубина в сезоне 2023/2024?",
"options": [
"Ильзат Ахметов",
"Олег Данченко",
"Андрес Панков",
"Дмитрий Тарасов"
],
"correct_answer": "Ильзат Ахметов",
"image_url": "f_ten"
},
{
"question": "Какой российский тренер дебютировал за Рубин в роли главного тренера после ухода Курбана Бердыева?",
"options": [
"Сергей Семак",
"Роман Шаронов",
"Виктор Гончаренко",
"Александр Бородюк"
],
"correct_answer": "Роман Шаронов",
"image_url": "f_eleven"
},
{
"question": "На каком турнире Рубин выиграл Атлетико Мадрид в 2012 году?",
"options": [
"Лига Европы",
"Суперкубок УЕФА",
"Кубок Интертото",
"Кубок России"
],
"correct_answer": "Лига Европы",
"image_url": "f_twelve"
},
{
"question": "Какое самое крупное достижение Рубина в Еврокубках?",
"options": [
"Полуфинал Лиги Европы",
"Четвертьфинал Лиги чемпионов",
"Четвертьфинал Лиги Европы",
"Групповой этап Лиги чемпионов"
],
"correct_answer": "Четвертьфинал Лиги Европы",
"image_url": "f_thirteen"
},
{
"question": "Какой игрок Рубина отличился своим великолепным голом с центра поля против Барселоны?",
"options": [
"Гёкдениз Карадениз",
"Роман Еременко",
"Александр Рязанцев",
"Александр Бухаров"
],
"correct_answer": "Александр Рязанцев",
"image_url": "f_fourteen"
},
{
"question": "Кто стал лучшим бомбардиром Рубина за всю историю клуба?",
"options": [
"Александр Бухаров",
"Саломон Рондон",
"Гёкдениз Карадениз",
"Сергей Семак"
],
"correct_answer": "Гёкдениз Карадениз",
"image_url": "f_fifteen"
},
{
"question": "Какой клуб стал принципиальным соперником Рубина в 2010-х годах?",
"options": [
"Зенит",
"Спартак",
"Локомотив",
"ЦСКА"
],
"correct_answer": "Зенит",
"image_url": "f_sixteen"
}
]
},
"volleyball": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете волейбольный Зенит-Казань и Динамо-Ак Барс!",
"intro_image": "intro_volleyball",
"questions": [
{
"question": "В каком году был основан волейбольный клуб Зенит-Казань?",
"options": [
"2000",
"2003",
"2007",
"2010"
],
"correct_answer": "2000",
"image_url": "v_one"
},
{
"question": "Сколько раз Зенит-Казань выигрывал Лигу чемпионов?",
"options": [
"4",
"5",
"6",
"7"
],
"correct_answer": "6",
"image_url": "v_two"
},
{
"question": "Кто является самым титулованным игроком в истории Зенита-Казань?",
"options": [
"Максим Михайлов",
"Вильфредо Леон",
"Алексей Вербов",
"Мэттью Андерсон"
],
"correct_answer": "Максим Михайлов",
"image_url": "v_three"
},
{
"question": "Как называется домашняя арена Зенита-Казань?",
"options": [
"Татнефть Арена",
"Баскет-Холл",
"Центр волейбола Санкт-Петербург",
"Казань Арена"
],
"correct_answer": "Центр волейбола Санкт-Петербург",
"image_url": "v_four"
},
{
"question": "Кто был главным тренером Зенита-Казань в период их доминирования в 2010-х годах?",
"options": [
"Алексей Вербов",
"Владимир Алекно",
"Данте Амарал",
"Михаил Лихачев"
],
"correct_answer": "Владимир Алекно",
"image_url": "v_five"
},
{
"question": "Какие цвета используются в форме Зенита-Казань?",
"options": [
"Синий и белый",
"Синий и серебристый",
"Зелёный и белый",
"Красный и синий"
],
"correct_answer": "Синий и белый",
"image_url": "v_six"
},
{
"question": "В каком году Зенит-Казань выиграл свой первый чемпионат России?",
"options": [
"2005",
"2007",
"2009",
"2011"
],
"correct_answer": "2007",
"image_url": "v_seven"
},
{
"question": "Когда была основана женская команда Динамо-Ак Барс?",
"options": [
"2002",
"2007",
"2012",
"2015"
],
"correct_answer": "2002",
"image_url": "v_eight"
},
{
"question": "Какое достижение является наиболее значимым для Динамо-Ак Барс?",
"options": [
"Победа в Лиге чемпионов",
"Победа в чемпионате России",
"Победа в Кубке России",
"Победа в Кубке Европейской конфедерации волейбола"
],
"correct_answer": "Победа в чемпионате России",
"image_url": "v_nine"
},
{
"question": "В каком году женская команда Динамо-Ак Барс выиграла свой первый Кубок России?",
"options": [
"2008",
"2010",
"2012",
"2014"
],
"correct_answer": "2010",
"image_url": "v_ten"
},
{
"question": "Сколько раз женская команда Динамо-Ак Барс выигрывала чемпионат России?",
"options": [
"5",
"2",
"7",
"4"
],
"correct_answer": "7",
"image_url": "v_eleven"
},
{
"question": "Какая из этих звезд мирового волейбола играла за Зенит-Казань?",
"options": [
"Вильфредо Леон",
"Матей Казийский",
"Жиба",
"Тревор Клеверли"
],
"correct_answer": "Вильфредо Леон",
"image_url": "v_twelve"
}
]
},
"water-polo": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете \"Синтез\"!",
"intro_image": "intro_water_polo",
"questions": [
{
"question": "В каком году был основан воднопольный клуб Синтез?",
"options": [
"1974",
"1980",
"1985",
"1991"
],
"correct_answer": "1974",
"image_url": "w_one"
},
{
"question": "Сколько раз Синтез выигрывал чемпионат России по водному поло?",
"options": [
"5",
"8",
"10",
"12"
],
"correct_answer": "10",
"image_url": "w_two"
},
{
"question": "В каком году Синтез выиграл Кубок Европейской водной лиги (LEN Trophy)?",
"options": [
"2005",
"2007",
"2010",
"2012"
],
"correct_answer": "2007",
"image_url": "w_three"
},
{
"question": "Сколько игроков одновременно находится в игре от одной команды в водном поло?",
"options": [
"5",
"6",
"7",
"8"
],
"correct_answer": "7",
"image_url": "w_four"
},
{
"question": "Какая длина стандартного бассейна для водного поло?",
"options": [
"20 метров",
"25 метров",
"30 метров",
"50 метров"
],
"correct_answer": "30 метров",
"image_url": "w_five"
},
{
"question": "Какая команда является главным соперником Синтеза в России?",
"options": [
"Спартак Волгоград",
"Динамо Москва",
"Локомотив Краснодар",
"Зенит Санкт-Петербург"
],
"correct_answer": "Спартак Волгоград",
"image_url": "w_six"
}
]
},
"history": {
"intro_text": "Давайте узнаем, насколько вы хорошо знаете историю Казани!",
"intro_image": "culture",
"questions": [
{
"question": "Когда Казань была основана?",
"options": [
"1000 год",
"1156 год",
"1230 год",
"1323 год"
],
"correct_answer": "1000 год",
"image_url": "culture"
},
{
"question": "Как называется главная река, протекающая через Казань?",
"options": [
"Волга",
"Казанка",
"Кама",
"Иж"
],
"correct_answer": "Казанка",
"image_url": "culture"
},
{
"question": "Кто был первым казанским ханом?",
"options": [
"Улу-Мухаммед",
"Ахмат",
"Шах-Али",
"Мамука"
],
"correct_answer": "Улу-Мухаммед",
"image_url": "culture"
},
{
"question": "Как называется главный спортивный комплекс Казани, где проводились Универсиада 2013 года?",
"options": [
"Казан Арена",
"Татнефть Арена",
"Бугульминская арена",
"Дворец спорта \"Ак Барс\""
],
"correct_answer": "Казан Арена",
"image_url": "culture"
},
{
"question": "Какая мечеть в Казани считается одной из самых больших в России?",
"options": [
"Мечеть Кул Шариф",
"Мечеть Марий Эл",
"Мечеть Айша",
"Мечеть имама Мухаммада"
],
"correct_answer": "Мечеть Кул Шариф",
"image_url": "culture"
},
{
"question": "Как называется площадь, на которой расположены Казанский Кремль и мечеть Кул Шариф?",
"options": [
"Площадь Вахитова",
"Площадь Свободы",
"Площадь Кремля",
"Площадь Революции"
],
"correct_answer": "Площадь Кремля",
"image_url": "culture"
},
{
"question": "Какой символ Казани изображён на гербе города?",
"options": [
"Дракон",
"Тигр",
"Лев",
"Орел"
],
"correct_answer": "Дракон",
"image_url": "culture"
},
{
"question": "Какой архитектор спроектировал Казанский Кремль?",
"options": [
"Иван Зарудный",
"Фёдор Бенжамин",
"Андрей Ушаков",
"Юрий Дашевский"
],
"correct_answer": "Андрей Ушаков",
"image_url": "culture"
},
{
"question": "Какой из этих вузов находится в Казани?",
"options": [
"Московский государственный университет",
"Казанский федеральный университет",
"Санкт-Петербургский политехнический университет",
"Новосибирский государственный университет"
],
"correct_answer": "Казанский федеральный университет",
"image_url": "culture"
},
{
"question": "Как называется крупнейшая в Казани улица, которая также является центром ночной жизни города?",
"options": [
"Кремлевская улица",
"Баумана улица",
"Пушкина улица",
"Каюма Насыри улица"
],
"correct_answer": "Баумана улица",
"image_url": "culture"
}
]
}
}

View File

@@ -1,908 +0,0 @@
{
"basketball": {
"intro_text": "UNICSны никадәр яхшы беләсең икән, тикшерик!",
"intro_image": "intro_basket",
"questions": [
{
"question": "UNICS баскетбол клубы кайсы елда оешты?",
"options": [
"1988",
"1991",
"1995",
"2000"
],
"correct_answer": "1991",
"image_url": "b_one"
},
{
"question": "\"UNICS\" аббревиатурасы нәрсәне аңлата?",
"options": [
"Университет, Инновацияләр, Спорт",
"Университет, Мәдәният, Спорт",
"Университет Казан Спорты",
"Уникаль Спорт"
],
"correct_answer": "Университет, Мәдәният, Спорт",
"image_url": "b_two"
},
{
"question": "Уен формасының төп төсләре нинди?",
"options": [
"Кызыл һәм ак",
"Яшел һәм ак",
"Күк һәм сары",
"Кара һәм алтын"
],
"correct_answer": "Яшел һәм ак",
"image_url": "b_three"
},
{
"question": "UNICSның ту home арена исемен ничек атыйлар?",
"options": [
"Татнефть Арена",
"Үзәк стадион",
"Баскет-холл",
"Казан Арена"
],
"correct_answer": "Баскет-холл",
"image_url": "b_four"
},
{
"question": "UNICS кайсы елда Еврокубокны яулады?",
"options": [
"2009",
"2011",
"2013",
"2015"
],
"correct_answer": "2011",
"image_url": "b_five"
},
{
"question": "Евролиганың иң яхшы бомбардиры булган нинди танылган Америка баскетболчысы UNICSка килде?",
"options": [
"Майкл Джордан",
"Кит Лэнгфорд",
"Леброн Джеймс",
"Куртис Джерреллс"
],
"correct_answer": "Кит Лэнгфорд",
"image_url": "b_six"
},
{
"question": "UNICSның ту home арена, Баскет-холл, күпме урынга ия?",
"options": [
"3000",
"5000",
"7000",
"10000"
],
"correct_answer": "7000",
"image_url": "b_seven"
},
{
"question": "UNICS Еврокубокны яулаганда аның баш тренеры кем иде?",
"options": [
"Димитрис Итудис",
"Евгений Пашутин",
"Зоран Лукич",
"Андрей Кириленко"
],
"correct_answer": "Евгений Пашутин",
"image_url": "b_eight"
},
{
"question": "Единой Лига ВТБда UNICSның традицион иң зур көндәше кем?",
"options": [
"ЦСКА Мәскәү",
"Локомотив-Кубань",
"Зенит Санкт-Петербург",
"Нижний Новгород"
],
"correct_answer": "ЦСКА Мәскәү",
"image_url": "b_nine"
},
{
"question": "UNICS кайсы трофейны әле алмаган?",
"options": [
"Единой лига ВТБ",
"Россия Кубогы",
"Евролига",
"Еврокубок"
],
"correct_answer": "Евролига",
"image_url": "b_ten"
},
{
"question": "2024/2025 сезонында UNICSның баш тренеры кем?",
"options": [
"Александр Волков",
"Велимир Перасович",
"Андрей Мальцев",
"Димитрис Прифтис"
],
"correct_answer": "Велимир Перасович",
"image_url": "b_eleven"
},
{
"question": "UNICS 2011 елда Еврокубок финалында кемгә каршы уен үтте?",
"options": [
"Реал Мадрид",
"Каха Лабораль",
"Локомотив-Кубань",
"Црвена Звезда"
],
"correct_answer": "Каха Лабораль",
"image_url": "b_twelve"
},
{
"question": "UNICS кайсы елдан башлап Евролигада катнаша?",
"options": [
"2005",
"2008",
"2010",
"2011"
],
"correct_answer": "2011",
"image_url": "b_thirteen"
},
{
"question": "UNICSның халыкара аренада төп казанышы нинди титул белән билгеләнә?",
"options": [
"Евролига чемпионы",
"Еврокубок чемпионы",
"Единой лига ВТБ җиңүчесе",
"Россия Кубогы җиңүчесе"
],
"correct_answer": "Еврокубок чемпионы",
"image_url": "b_fourteen"
},
{
"question": "UNICSның кайсы йолдызы ике тапкыр Еврокубокның иң яхшы бомбардиры булды?",
"options": [
"Кит Лэнгфорд",
"Алексей Швед",
"Нандо Де Коло",
"Артём Параховский"
],
"correct_answer": "Кит Лэнгфорд",
"image_url": "b_fifteen"
},
{
"question": "UNICS Россия Кубогын ничә тапкыр яулаган?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "3",
"image_url": "b_sixteen"
},
{
"question": "2020/2021 сезонында UNICS Единой Лига ВТБда кайсы урынны алды?",
"options": [
"1нче",
"2нче",
"3нче",
"4нче"
],
"correct_answer": "2нче",
"image_url": "b_seventeen"
},
{
"question": "UNICS кайсы елда беренче тапкыр Единой Лигу ВТБны яулады?",
"options": [
"2009",
"2011",
"2015",
"2023"
],
"correct_answer": "2023",
"image_url": "b_eighteen"
},
{
"question": "Ә хәзер иң мөһим сорау. Лабери 2023 eлда Уайттың ничә тешен төшерде?😁",
"options": [
"1",
"2",
"3",
"4",
"Шаярту бөттө инде."
],
"correct_answer": "4",
"image_url": "b_nineteen"
}
]
},
"hockey": {
"intro_text": "Ак Барсны никадәр яхшы беләсең икән, тикшерик!",
"intro_image": "intro_hockey",
"questions": [
{
"question": "Ак Барс хоккей клубы кайсы елда оешты?",
"options": [
"1946",
"1956",
"1960",
"1990"
],
"correct_answer": "1956",
"image_url": "h_one"
},
{
"question": "\"Ак Барс\" исеме нәрсәне аңлата?",
"options": [
"Ак барс",
"Көчле барс",
"Яман барс",
"Мөслим барс"
],
"correct_answer": "Ак барс",
"image_url": "h_two"
},
{
"question": "Ак Барсның форма төсләре нинди?",
"options": [
"Кызыл, яшел һәм ак",
"Күк, ак һәм кара",
"Сары, кара һәм ак",
"Кызыл, кара һәм ак"
],
"correct_answer": "Кызыл, яшел һәм ак",
"image_url": "h_three"
},
{
"question": "Ак Барс кайсы аренада чыгыш ясый?",
"options": [
"Баскет-холл",
"Татнефть Арена",
"Үзәк стадион",
"Казан Арена"
],
"correct_answer": "Татнефть Арена",
"image_url": "h_four"
},
{
"question": "Ак Барс КХЛ чемпионы булган ничә тапкыр?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "3",
"image_url": "h_five"
},
{
"question": "Ак Барс беренче тапкыр Гагарин Кубогын кайсы сезонда яулады?",
"options": [
"2008/2009",
"2009/2010",
"2010/2011",
"2011/2012"
],
"correct_answer": "2008/2009",
"image_url": "h_six"
},
{
"question": "Ак Барсның иң яхшы бомбардирларының берсе булган кайсы уенчы?",
"options": [
"Александр Радулов",
"Данис Зарипов",
"Алексей Морозов",
"Евгений Малкин"
],
"correct_answer": "Данис Зарипов",
"image_url": "h_seven"
},
{
"question": "Ак Барсның төп көндәше кемдер 'Яшел дерби'да?",
"options": [
"Салават Юлаев",
"Металлург Магнитогорск",
"Авангард Омск",
"ЦСКА Мәскәү"
],
"correct_answer": "Салават Юлаев",
"image_url": "h_eight"
},
{
"question": "Ак Барсның талисманы нәрсә?",
"options": [
"Ак тигр",
"Арыслан",
"Барс",
"Аю"
],
"correct_answer": "Барс",
"image_url": "h_nine"
},
{
"question": "2023/2024 сезонында Ак Барсның баш тренеры кем булган?",
"options": [
"Дмитрий Квартальнов",
"Зинэтула Билялетдинов",
"Андрей Скабелка",
"Валерий Брагин"
],
"correct_answer": "Зинэтула Билялетдинов",
"image_url": "h_ten"
},
{
"question": "КХЛ оештырылганчы Ак Барс Русия чемпионатын ничә тапкыр яулады?",
"options": [
"2",
"3",
"4",
"5"
],
"correct_answer": "2",
"image_url": "h_eleven"
},
{
"question": "Бу уенчыларның кайсысы Ак Барс өчен уйнамаган?",
"options": [
"Илья Никулин",
"Сергей Мозякин",
"Алексей Терещенко",
"Никита Зайцев"
],
"correct_answer": "Сергей Мозякин",
"image_url": "h_twelve"
},
{
"question": "2018 елда Гагарин Кубогының финалында Ак Барсның көндәше кем булды?",
"options": [
"Авангард Омск",
"Салават Юлаев",
"ЦСКА Мәскәү",
"СКА Санкт-Петербург"
],
"correct_answer": "ЦСКА Мәскәү",
"image_url": "h_thirteen"
},
{
"question": "Ак Барс өчен иң күп уеннар уздырган уенчы кем?",
"options": [
"Данис Зарипов",
"Илья Никулин",
"Алексей Морозов",
"Виктор Тихонов"
],
"correct_answer": "Илья Никулин",
"image_url": "h_fourteen"
},
{
"question": "Ак Барсны КХЛ чемпионлыгына беренче тапкыр кем тренерлыкка китерде?",
"options": [
"Зинэтула Билялетдинов",
"Владимир Крикунов",
"Дмитрий Квартальнов",
"Александр Медведев"
],
"correct_answer": "Зинэтула Билялетдинов",
"image_url": "h_fifteen"
},
{
"question": "Ак Барсның фанатлары командасына нинди кушамат бирәләр?",
"options": [
"Ак Барслар",
"Яшел Машиналар",
"Казан Барслары",
"Яшел Армия"
],
"correct_answer": "Яшел Армия",
"image_url": "h_sixteen"
}
]
},
"football": {
"intro_text": "Рубин турында ни дәрәҗәдә беләсең, тикшерик!",
"intro_image": "intro_football",
"questions": [
{
"question": "Рубин футбол клубы кайсы елда оешты?",
"options": [
"1936",
"1958",
"1964",
"1972"
],
"correct_answer": "1958",
"image_url": "f_one"
},
{
"question": "Рубинның форма төсләре нинди?",
"options": [
"Кызыл һәм ак",
"Яшел һәм ак",
"Кызыл һәм яшел",
"Сары һәм зәңгәр"
],
"correct_answer": "Кызыл һәм яшел",
"image_url": "f_two"
},
{
"question": "ФК Рубинның туган стадионы нәрсә?",
"options": [
"Казан Арена",
"Үзәк стадион",
"Татнефть Арена",
"Ак Барс Арена"
],
"correct_answer": "Ак Барс Арена",
"image_url": "f_three"
},
{
"question": "Рубин беренче тапкыр Русия чемпионы булган ел кайсы?",
"options": [
"2006",
"2008",
"2009",
"2010"
],
"correct_answer": "2008",
"image_url": "f_four"
},
{
"question": "ФК Рубин Русия чемпионы булган ничә тапкыр?",
"options": [
"1",
"2",
"3",
"4"
],
"correct_answer": "2",
"image_url": "f_five"
},
{
"question": "Рубинның 2008 елда беренче чемпионлыгында төп тренеры кем булган?",
"options": [
"Гус Хиддинк",
"Курбан Бердыев",
"Юрий Семин",
"Валерий Карпин"
],
"correct_answer": "Курбан Бердыев",
"image_url": "f_six"
},
{
"question": "Рубин 2009 елда Лига Чемпионнарында Барселонаны нинди нәтиҗә белән җиңгән?",
"options": [
"2:1",
"1:0",
"3:0",
"3:1"
],
"correct_answer": "2:1",
"image_url": "f_seven"
},
{
"question": "2009/2010 сезонында Рубинның иң күп гол керткән уенчысы кем булган?",
"options": [
"Александр Бухаров",
"Саломон Рондон",
"Гёкдениз Карадениз",
"Сергей Семак"
],
"correct_answer": "Александр Бухаров",
"image_url": "f_eight"
},
{
"question": "Рубинның Лига Чемпионнарында дебют сезонында төркемдә нинди урында булды?",
"options": [
"1нче",
"2нче",
"3нче",
"4нче"
],
"correct_answer": "3нче",
"image_url": "f_nine"
},
{
"question": "2023/2024 сезонында Рубинның капитаны кем?",
"options": [
"Ильзат Ахметов",
"Олег Данченко",
"Андрес Панков",
"Дмитрий Тарасов"
],
"correct_answer": "Ильзат Ахметов",
"image_url": "f_ten"
},
{
"question": "Курбан Бердыев киткәннән соң Рубинның баш тренеры булып кем эшли башлады?",
"options": [
"Сергей Семак",
"Роман Шаронов",
"Виктор Гончаренко",
"Александр Бородюк"
],
"correct_answer": "Роман Шаронов",
"image_url": "f_eleven"
},
{
"question": "2012 елда Рубин Атлетико Мадридны кайсы турнирыда җиңгән?",
"options": [
"Евро Лига",
"Суперкубок УЕФА",
"Кубок Интертото",
"Кубок Русия"
],
"correct_answer": "Евро Лига",
"image_url": "f_twelve"
},
{
"question": "Рубинның Еврокубокларда иң зур җиңүе нинди?",
"options": [
"Евро Лигадагы яртыфинал",
"Лига Чемпионнарында 4нче финал",
"Евро Лигадагы 4нче финал",
"Лига Чемпионнарында төркем этабы"
],
"correct_answer": "Евро Лигадагы 4нче финал",
"image_url": "f_thirteen"
},
{
"question": "Рубинның Барселонаны җиңгән вакытта үзенең кырга утылган голы белән дан алган уенчысы кем?",
"options": [
"Гёкдениз Карадениз",
"Роман Еременко",
"Александр Рязанцев",
"Александр Бухаров"
],
"correct_answer": "Александр Рязанцев",
"image_url": "f_fourteen"
},
{
"question": "Рубинның клуб тарихында иң күп гол керткән уенчысы кем?",
"options": [
"Александр Бухаров",
"Саломон Рондон",
"Гёкдениз Карадениз",
"Сергей Семак"
],
"correct_answer": "Гёкдениз Карадениз",
"image_url": "f_fifteen"
},
{
"question": "2010нчы елларда Рубинның төп көндәше булган клуб?",
"options": [
"Зенит",
"Спартак",
"Локомотив",
"ЦСКА"
],
"correct_answer": "Зенит",
"image_url": "f_sixteen"
}
]
},
"volleyball": {
"intro_text": "Волейбол командасы Зенит-Казан һәм Динамо-Ак Барс турында ни дәрәҗәдә беләсең?",
"intro_image": "intro_volleyball",
"questions": [
{
"question": "Зенит-Казан волейбол клубы кайсы елда оешты?",
"options": [
"2000",
"2003",
"2007",
"2010"
],
"correct_answer": "2000",
"image_url": "v_one"
},
{
"question": "Зенит-Казан Лигасында ничә тапкыр җиңгән?",
"options": [
"4",
"5",
"6",
"7"
],
"correct_answer": "6",
"image_url": "v_two"
},
{
"question": "Зенит-Казанның тарихында иң күп медаль җыйган уенчы кем?",
"options": [
"Максим Михайлов",
"Вильфредо Леон",
"Алексей Вербов",
"Мэттью Андерсон"
],
"correct_answer": "Максим Михайлов",
"image_url": "v_three"
},
{
"question": "Зенит-Казанның туган стадионы нинди?",
"options": [
"Татнефть Арена",
"Баскет-Холл",
"Санкт-Петербург Волейбол Центры",
"Казан Арена"
],
"correct_answer": "Санкт-Петербург Волейбол Центры",
"image_url": "v_four"
},
{
"question": "Зенит-Казанның 2010нчы еллардагы доминирование дәверендә төп тренер кем булды?",
"options": [
"Алексей Вербов",
"Владимир Алекно",
"Данте Амарал",
"Михаил Лихачев"
],
"correct_answer": "Владимир Алекно",
"image_url": "v_five"
},
{
"question": "Зенит-Казанның форма төсләре нинди?",
"options": [
"Күк һәм ак",
"Күк һәм көмеш",
"Яшел һәм ак",
"Кызыл һәм күк"
],
"correct_answer": "Күк һәм ак",
"image_url": "v_six"
},
{
"question": "Зенит-Казан беренче тапкыр Россия чемпионатын кайсы елда отты?",
"options": [
"2005",
"2007",
"2009",
"2011"
],
"correct_answer": "2007",
"image_url": "v_seven"
},
{
"question": "Динамо-Ак Барс хатын-кыз командасы кайсы елда оешты?",
"options": [
"2002",
"2007",
"2012",
"2015"
],
"correct_answer": "2002",
"image_url": "v_eight"
},
{
"question": "Динамо-Ак Барсның иң зур җиңүе нинди?",
"options": [
"Лига Чемпионнарында җиңү",
"Россия Чемпионатында җиңү",
"Россия Кубогында җиңү",
"Европа Конфедерациясе Кубогында җиңү"
],
"correct_answer": "Россия Чемпионатында җиңү",
"image_url": "v_nine"
},
{
"question": "Динамо-Ак Барсның хатын-кыз командасы беренче тапкыр Россия Кубогын кайсы елда отты?",
"options": [
"2008",
"2010",
"2012",
"2014"
],
"correct_answer": "2010",
"image_url": "v_ten"
},
{
"question": "Динамо-Ак Барсның хатын-кыз командасы Россия Чемпионатын ничә тапкыр отты?",
"options": [
"5",
"2",
"7",
"4"
],
"correct_answer": "7",
"image_url": "v_eleven"
},
{
"question": "Шушы зур волейбол йолдызларының кайсысы Зенит-Казан командасында уйнаган?",
"options": [
"Вильфредо Леон",
"Матей Казийский",
"Жиба",
"Тревор Клеверли"
],
"correct_answer": "Вильфредо Леон",
"image_url": "v_twelve"
}
]
},
"water-polo": {
"intro_text": "Синтез турында нинди белемегез барлыгын тикшерик!",
"intro_image": "intro_water_polo",
"questions": [
{
"question": "Синтез воднопольный клубы кайсы елда оешты?",
"options": [
"1974",
"1980",
"1985",
"1991"
],
"correct_answer": "1974",
"image_url": "w_one"
},
{
"question": "Синтез Россия воднопольный чемпионатын ничә тапкыр отты?",
"options": [
"5",
"8",
"10",
"12"
],
"correct_answer": "10",
"image_url": "w_two"
},
{
"question": "Синтез кайсы елда Европаның водное поло лигасы Кубогын (LEN Trophy) отты?",
"options": [
"2005",
"2007",
"2010",
"2012"
],
"correct_answer": "2007",
"image_url": "w_three"
},
{
"question": "Водное поло уенында бер командадан берьюлы ничә уенчы катнаша?",
"options": [
"5",
"6",
"7",
"8"
],
"correct_answer": "7",
"image_url": "w_four"
},
{
"question": "Водное поло өчен стандарт бассейнның озынлыгы нинди?",
"options": [
"20 метр",
"25 метр",
"30 метр",
"50 метр"
],
"correct_answer": "30 метр",
"image_url": "w_five"
},
{
"question": "Синтезның Россиядәге төп көндәше нинди команда?",
"options": [
"Спартак Волгоград",
"Динамо Мәскәү",
"Локомотив Краснодар",
"Зенит Санкт-Петербург"
],
"correct_answer": "Спартак Волгоград",
"image_url": "w_six"
}
]
},
"history": {
"intro_text": "Әйдәгез, Казан тарихын белүегезне ачыклыйк!",
"intro_image": "culture",
"questions": [
{
"question": "Казан кайчан нигезләнгән?",
"options": [
"1000 ел",
"1156 ел",
"1230 ел",
"1323 ел"
],
"correct_answer": "1000 ел",
"image_url": "culture"
},
{
"question": "Казан аша агучы төп елга ничек атала?",
"options": [
"Идел",
"Казанка",
"Кама",
"Иж"
],
"correct_answer": "Казанка",
"image_url": "culture"
},
{
"question": "Казанның беренче ханы кем булган?",
"options": [
"Олуг Мөхәммәт",
"Әхмәт",
"Шаһ-Әли",
"Мамука"
],
"correct_answer": "Олуг Мөхәммәт",
"image_url": "culture"
},
{
"question": "2013 елда Универсиада узган Казанның төп спорт комплексы ничек атала?",
"options": [
"Казан Арена",
"Татнефть Арена",
"Бөгелмә Арена",
"Ак Барс спорт сарае"
],
"correct_answer": "Казан Арена",
"image_url": "culture"
},
{
"question": "Казандагы иң зур мәчетләрнең берсе Россиядәге кайсысы?",
"options": [
"Кол Шәриф мәчете",
"Марий Эл мәчете",
"Айша мәчете",
"Имам Мөхәммәт мәчете"
],
"correct_answer": "Кол Шәриф мәчете",
"image_url": "culture"
},
{
"question": "Казан Кремле һәм Кол Шәриф мәчете урнашкан мәйдан ничек атала?",
"options": [
"Вахитов мәйданы",
"Ирек мәйданы",
"Кремль мәйданы",
"Революция мәйданы"
],
"correct_answer": "Кремль мәйданы",
"image_url": "culture"
},
{
"question": "Казан гербында кайсы символ сурәтләнгән?",
"options": [
"Аждаһа",
"Юлбарыс",
"Арслан",
"Бүре"
],
"correct_answer": "Аждаһа",
"image_url": "culture"
},
{
"question": "Казан Кремленең архитекторын атагыз.",
"options": [
"Иван Зарудный",
"Фёдор Бенжамин",
"Андрей Ушаков",
"Юрий Дашевский"
],
"correct_answer": "Андрей Ушаков",
"image_url": "culture"
},
{
"question": "Бу югары уку йортларының кайсысы Казанда урнашкан?",
"options": [
"Мәскәү дәүләт университеты",
"Казан федераль университеты",
"Санкт-Петербург политехник университеты",
"Новосибирск дәүләт университеты"
],
"correct_answer": "Казан федераль университеты",
"image_url": "culture"
},
{
"question": "Казандагы иң зур урам ничек атала? Ул шулай ук шәһәрнең төнге тормыш үзәге булып тора.",
"options": [
"Кремль урамы",
"Бауман урамы",
"Пушкин урамы",
"Каюм Насыйри урамы"
],
"correct_answer": "Бауман урамы",
"image_url": "culture"
}
]
}
}

View File

@@ -2,7 +2,6 @@
{
"id": "1",
"type": "Basketball",
"sport": "basketball",
"title": "UNICS",
"text": "UNICS Basketball Club is a Russian men's basketball team founded in 1991. It represents Kazan, the capital of the Republic of Tatarstan. The club competes in the Professional Basketball League. 'UNICS' stands for 'University — Culture — Sport.'",
"logo": "unics_logo",
@@ -24,7 +23,6 @@
{
"id": "3",
"type": "Hockey",
"sport": "hockey",
"title": "Ak Bars",
"text": "Ak Bars Hockey Club from Kazan is one of the most renowned and successful professional hockey teams in Russia. Founded in 1956, it is a member of the Kontinental Hockey League (KHL), the most prestigious league in Russia and one of the strongest in the world.",
"logo": "akbars_logo",
@@ -46,7 +44,6 @@
{
"id": "5",
"type": "Football",
"sport": "football",
"title": "Rubin",
"text": "Rubin Kazan (tat. Рубин Казан футбол төркеме) is a Russian football club from Kazan. One of the leading Russian football clubs in the second half of the 2000s. Its history dates back to 1936, but the official founding date is 1958.",
"logo": "rubin_logo",
@@ -58,7 +55,6 @@
{
"id": "6",
"type": "Volleyball",
"sport": "volleyball",
"title": "Zenit-Kazan",
"text": "Zenit-Kazan is a Russian men's volleyball team founded in 2000. Initially named 'Dynamo' until 2004, it was later known as 'Dynamo-Tattransgaz' until June 2008. The team is a 4-time Russian champion, 3-time Russian Cup winner, and a Champions League winner for the 2007/08 and 2011/12 seasons. Its main colors are white and blue.",
"logo": "zenit_logo",
@@ -80,7 +76,6 @@
{
"id": "8",
"type": "Water Polo",
"sport": "water-polo",
"title": "Sintez",
"text": "The development of water polo in Kazan was boosted by the construction of the first 50-meter swimming pool, 'Orgsintez,' in 1973. Based on this pool, the Sintez senior team was formed, earning the right to play in the first group of the RSFSR championship.",
"logo": "waterpolo_logo",

View File

@@ -2,7 +2,6 @@
{
"id": "1",
"type": "Баскетбол",
"sport": "basketball",
"title": "УНИКС ",
"text": "Баскетбольный клуб УНИКС — российский мужской баскетбольный клуб. Основан в 1991 году. Представляет город Казань, столицу Республики Татарстан. Выступает в Профессиональной баскетбольной лиге. «УНИКС» расшифровывается как «Университет — Культура — Спорт».",
"logo": "unics_logo",
@@ -24,7 +23,6 @@
{
"id": "3",
"type": "Хоккей",
"sport": "hockey",
"title": "Ак Барс ",
"text": "Хоккейный клуб «Ак Барс» из Казани — это один из наиболее известных и успешных профессиональных хоккейных клубов в России. Он был основан в 1956 году и является членом Континентальной Хоккейной Лиги (КХЛ), самой престижной лиги в России и одной из сильнейших в мире.",
"logo": "akbars_logo",
@@ -46,7 +44,6 @@
{
"id": "5",
"type": "Футбол",
"sport": "football",
"title": "Рубин ",
"text": "«Рубин» (тат. Рубин Казан футбол төркеме) — российский футбольный клуб из Казани. Один из ведущих российских футбольных клубов второй половины 2000-х годов. Ведёт свою историю с 1936 года, но официальной датой основания считается 1958 год.",
"logo": "rubin_logo",
@@ -58,7 +55,6 @@
{
"id": "6",
"type": "Волейбол",
"sport": "volleyball",
"title": "Зенит-Казань ",
"text": "«Зенит» (Казань) — российский мужской волейбольный клуб. Основан в 2000 году, до 2004 года назывался «Динамо», с 2005 по июнь 2008 года — «Динамо-Таттрансгаз». 4-кратный чемпион России, 3-кратный обладатель Кубка России, победитель Лиги чемпионов сезонов 2007/08 и 2011/12 годов. Основные цвета: бело-синие.",
"logo": "zenit_logo",
@@ -80,7 +76,6 @@
{
"id": "8",
"type": "Водное поло",
"sport": "water-polo",
"title": "Синтез ",
"text": "Толчком развития водного поло в Казани стало строительство первого в Республике 50-ти метрового плавательного бассейна «Оргсинтез», закончившееся в 1973 году. Именно на базе бассейна «Оргсинтез» и была создана взрослая команда «Синтез», добившаяся права играть в первой группе чемпионата РСФСР.",
"logo": "waterpolo_logo",

View File

@@ -2,7 +2,6 @@
{
"id": "1",
"type": "Баскетбол",
"sport": "basketball",
"title": "УНИКС",
"text": "Баскетбол клубы УНИКС — Россиянең ир-ат баскетбол командасы, 1991 елда оешкан. Ул Татарстан башкаласы Казанны тәкъдим итә. Клуб Профессиональ баскетбол лигасында катнаша. 'УНИКС' 'Университет — Мәдәният — Спорт'ны аңлата.",
"logo": "unics_logo",
@@ -24,7 +23,6 @@
{
"id": "3",
"type": "Хоккей",
"sport": "hockey",
"title": "Ак Барс",
"text": "Казанның Ак Барс хоккей клубы — Россиядәге иң танылган һәм уңышлы профессиональ хоккей клубларының берсе. Ул 1956 елда нигез салынган һәм Россиянең иң абруйлы лигасы — Континенталь Хоккей Лигасы (КХЛ) әгъзасы булып тора.",
"logo": "akbars_logo",
@@ -46,7 +44,6 @@
{
"id": "5",
"type": "Футбол",
"sport": "football",
"title": "Рубин",
"text": "Рубин Казан (тат. Рубин Казан футбол төркеме) — Казаннан Россия футбол клубы. 2000-нче елларның икенче яртысында Россиянең әйдәп баручы футбол клубларының берсе. Аның тарихы 1936 елдан башлана, әмма рәсми төзелү датасы дип 1958 ел санала.",
"logo": "rubin_logo",
@@ -58,7 +55,6 @@
{
"id": "6",
"type": "Волейбол",
"sport": "volleyball",
"title": "Зенит-Казан",
"text": "Зенит-Казан — Россиянең ир-ат волейбол командасы. Ул 2000 елда оешкан. Башта 'Динамо' дип аталган, 2004 елга кадәр, аннары 2005 елдан 2008 елның июненә кадәр 'Динамо-Таттрансгаз' дип аталган. Команда 4 тапкыр Россия чемпионы, 3 тапкыр Россия Кубогы җиңүчесе һәм 2007/08 һәм 2011/12 еллар сезоннарының Чемпионнар Лигасы җиңүчесе. Төп төсләре: ак һәм зәңгәр.",
"logo": "zenit_logo",
@@ -80,7 +76,6 @@
{
"id": "8",
"type": "Су полосы",
"sport": "water-polo",
"title": "Синтез",
"text": "Казанда су полосын үстерүгә этәргеч 1973 елда беренче 50 метрлы 'Оргсинтез' бассейны төзелеше булды. Нәкъ шул бассейн базасында өлкәннәр командасы 'Синтез' төзелде, ул РСФСР чемпионатының беренче төркемендә уйнау хокукын яулады.",
"logo": "waterpolo_logo",

View File

@@ -1,450 +0,0 @@
[
{
"id": "1",
"from": "River Port",
"to": "Derbyshki Residential Area",
"route_length": "18.03 km",
"operating_mode_weekdays": "Weekdays: Start of service — 5:24. Last trip departure A — 22:32. Last trip departure B — 21:27. Interval: morning — 6.9 min, evening — 7.3 min.",
"operating_mode_weekend": "Weekends: Start of service — 5:30. Last trip departure A — 22:44. Last trip departure B — 21:55. Interval — 7.5 min."
},
{
"id": "2",
"from": "Ametyevo Metro Station",
"to": "Privokzalnaya Street",
"route_length": "20.9 km",
"operating_mode_weekdays": "Weekdays: Start of service — 5:40. Last trip departure A — 21:54. Last trip departure B — 21:01. Interval — 8.5 min.",
"operating_mode_weekend": "Weekends: Start of service — 5:40. Last trip departure A — 21:34. Last trip departure B — 20:32. Interval — 9 min."
},
{
"id": "4",
"from": "Ferma-2 Residential Area",
"to": "Novaya Sosnovka Residential Area",
"route_length": "24.59 km",
"operating_mode_weekdays": "Weekdays: Start of service — 5:30. Last trip departure A — 21:02. Last trip departure B — 20:50. Interval — 14.8 min.",
"operating_mode_weekend": "Weekends: Start of service — 5:30. Last trip departure A — 21:07. Last trip departure B — 21:20. Interval — 22 min."
},
{
"id": "5",
"from": "TSUM",
"to": "Khalitova Street",
"route_length": "27.42 km",
"operating_mode_weekdays": "Weekdays: Start of service — 5:43. Last trip departure A — 22:05. Last trip departure B — 20:45. Interval: morning — 6.7 min, evening — 7 min.",
"operating_mode_weekend": "Weekends: Start of service — 5:31. Last trip departure A — 22:15. Last trip departure B — 21:03. Interval — 7.6 min."
},
{
"id": "6",
"from": "River Port",
"to": "Northern Residential Area",
"route_length": "20.9 km",
"operating_mode_weekdays": "Weekdays: Start of service — 5:00. Last trip departure A — 22:24. Last trip departure B — 21:32. Interval — 7.8 min.",
"operating_mode_weekend": "Weekends: Start of service — 5:45. Last trip departure A — 22:07. Last trip departure B — 21:20. Interval — 8.4 min."
},
{
"id": "9",
"from": "Derbyski Residential Area",
"to": "Aki Residential Area",
"route_length": "4.52 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last departure A — 19:10. Last departure B — 19:40.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 19:10. Last departure B — 19:40."
},
{
"id": "10",
"from": "Serova Street",
"to": "Academician Gubkin Street (Ring)",
"route_length": "15.3 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:20. Last departure A — 22:12. Last departure B — 23:04. Interval 5.7 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 21:30. Last departure B — 22:30. Interval 7.1 min."
},
{
"id": "10a",
"from": "Serova Street",
"to": "Academician Gubkin Street (Ring)",
"route_length": "14.82 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:19. Last departure A — 21:52. Last departure B — 22:23. Interval 5.9 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 21:55. Last departure B — 22:25. Interval 7.2 min."
},
{
"id": "11",
"from": "Derbyski Residential Area",
"to": "Malye Derbyski Residential Area",
"route_length": "10.56 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last departure A — 18:45. Last departure B — 19:10.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 18:45. Last departure B — 19:10."
},
{
"id": "15",
"from": "Health Combine",
"to": "Adoratsky Street",
"route_length": "10.38 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Last departure A — 22:51. Last departure B — 22:22. Interval 6 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 22:50. Last departure B — 22:30. Interval 7 min."
},
{
"id": "18",
"from": "Severopolusnaya Street",
"to": "Duslyk Alley",
"route_length": "31.41 km",
"operating_mode_weekdays": "Weekdays: start of movement — 4:50. Last departure A — 22:01. Last departure B — 20:20. Interval 8.5 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:40. Last departure A — 21:58. Last departure B — 20:14. Interval 10.2 min."
},
{
"id": "22",
"from": "Mozhayskogo Street",
"to": "Ferma-2 Residential Area",
"route_length": "24.45 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Last departure A — 21:38. Last departure B — 20:34. Interval: morning 7.1 min, evening 7.4 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:40. Last departure A — 21:19. Last departure B — 20:01. Interval 10.1 min."
},
{
"id": "23",
"from": "R.Yakhina Street",
"to": "Mirny Residential Area",
"route_length": "13.58 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:20. Last departure A — 22:11. Last departure B — 21:40. Interval: morning 12.9 min, evening 15.1 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:20. Last departure A — 21:05. Last departure B — 21:13. Interval 15 min."
},
{
"id": "25",
"from": "Technical Street",
"to": "Derbyski Residential Area",
"route_length": "20.66 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Last departure A — 19:30. Last departure B — 18:30. Interval 13.2 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:10. Last departure A — 18:10. Last departure B — 17:50. Interval 20 min."
},
{
"id": "28",
"from": "CPC and Park",
"to": "Korolenko Street (Ring)",
"route_length": "8.81 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last departure A — 21:45. Last departure B — 22:02. Interval 7.6 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 21:38. Last departure B — 22:01. Interval 15 min."
},
{
"id": "28a",
"from": "Chekhov Market",
"to": "Korolenko Street (Ring)",
"route_length": "10.74 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last departure A — 21:30. Last departure B — 22:01. Interval 8 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 21:30. Last departure B — 22:01. Interval 15 min."
},
{
"id": "29",
"from": "Construction Institute",
"to": "Gudovantseva Street",
"route_length": "17.23 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Last departure A — 22:30. Last departure B — 21:40. Interval 7.2 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last departure A — 22:30. Last departure B — 21:40. Interval 9.2 min."
},
{
"id": "30",
"from": "Pereulok Duslyk",
"to": "Lagernaya Station",
"route_length": "27.05 km",
"operating_mode_weekdays": "Weekdays: start of movement — 4:38. Last A trip departure — 21:24. Last B trip departure — 22:40. Interval: morning 6.6 min, evening 6.8 min.",
"operating_mode_weekend": "Weekend: start of movement — 5:01. Last A trip departure — 20:56. Last B trip departure — 22:18. Interval 8.1 min."
},
{
"id": "31",
"from": "IKEA Store",
"to": "Old Pobeditovo Residential Area",
"route_length": "29.48 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:15. Last A trip departure — 22:10. Last B trip departure — 21:46. Interval: morning 6.6 min, evening 6.8 min.",
"operating_mode_weekend": "Weekend: start of movement — 5:30. Last A trip departure — 22:10. Last B trip departure — 21:51. Interval 8 min."
},
{
"id": "33",
"from": "Leninskaya St.",
"to": "Ferma-2 Residential Area",
"route_length": "99999",
"operating_mode_weekdays": "88888",
"operating_mode_weekend": "99999"
},
{
"id": "34",
"from": "Ferma-2 Residential Area",
"to": "Said-Galeev Cultural Center",
"route_length": "25.39 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:25. Last A trip departure — 18:51. Last B trip departure — 19:59. Interval 14.8 min.",
"operating_mode_weekend": "Weekend: start of movement — 6:00. Last A trip departure — 17:44. Last B trip departure — 18:48. Interval 17.5 min."
},
{
"id": "35",
"from": "G.Tukay Square Metro Station",
"to": "Gavrilova St. (ring road)",
"route_length": "14.33 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last A trip departure — 22:26. Last B trip departure — 21:35. Interval 7 min.",
"operating_mode_weekend": "Weekend: start of movement — 6:00. Last A trip departure — 22:15. Last B trip departure — 21:21. Interval 9.4 min."
},
{
"id": "35a",
"from": "G.Tukay Square Metro Station",
"to": "Car Market (ring road)",
"route_length": "14.06 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last A trip departure — 22:37. Last B trip departure — 21:45. Interval morning 6.4 min, evening 6.9 min.",
"operating_mode_weekend": "Weekend: start of movement — 6:00. Last A trip departure — 22:20. Last B trip departure — 21:30. Interval 9 min."
},
{
"id": "36",
"from": "Clothing Market",
"to": "Osinovo",
"route_length": "29.9 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:14. Last A trip departure — 21:42. Last B trip departure — 20:28. Interval: morning 6.8 min, evening 7.1 min.",
"operating_mode_weekend": "Weekend: start of movement — 5:30. Last A trip departure — 21:41. Last B trip departure — 20:21. Interval 11 min."
},
{
"id": "36a",
"from": "Zalesny Residential Area",
"to": "Novonikolaevsky",
"route_length": "14.98 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last A trip departure — 19:00. Last B trip departure — 19:15.",
"operating_mode_weekend": "Weekend: start of movement — 6:00. Last A trip departure — 19:00. Last B trip departure — 19:15."
},
{
"id": "37",
"from": "Ferma-2 Residential Area",
"to": "Sukhaya Reka Residential Area",
"route_length": "30.26 km",
"operating_mode_weekdays": "Weekdays: start of movement — 4:58. Last A trip departure — 21:47. Last B trip departure — 21:32. Interval 10 min.",
"operating_mode_weekend": "Weekend: start of movement — 5:30. Last A trip departure — 21:48. Last B trip departure — 20:54. Interval 13.7 min."
},
{
"id": "40",
"from": "Gavrilova St.",
"to": "Ak Bars Cafe",
"route_length": "19.5 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last A trip departure — 20:00. Last B trip departure — 20:55.",
"operating_mode_weekend": "Weekend: start of movement — 6:00. Last A trip departure — 20:00. Last B trip departure — 20:00."
},
{
"id": "42",
"from": "Lenin Cultural Center",
"to": "Borisoglebsk Residential Area",
"route_length": "8.56 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00. Last A trip departure — 19:10. Last B trip departure — 19:40.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last A trip departure — 19:10. Last B trip departure — 19:40."
},
{
"id": "43",
"from": "Khimicheskaya St.",
"to": "Tekhnicheskaya St.",
"route_length": "27.37 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:00. Last A trip departure — 21:45. Last B trip departure — 21:45. Interval — 7.9 minutes.",
"operating_mode_weekend": "Weekends: start of movement — 5:12. Last A trip departure — 20:32. Last B trip departure — 22:02. Interval — 11.3 minutes."
},
{
"id": "45",
"from": "Ferma-2 Residential Area",
"to": "Su Anasy",
"route_length": "39.28 km",
"operating_mode_weekdays": "Weekdays: start of movement — 4:57. Last A trip departure — 22:01. Last B trip departure — 22:17. Interval in the morning — 7.1 minutes, in the evening — 7.9 minutes.",
"operating_mode_weekend": "Weekends: start of movement — 5:00. Last A trip departure — 21:46. Last B trip departure — 22:00. Interval — 7.6 minutes."
},
{
"id": "46",
"from": "Dubrava Eco-Park",
"to": "Sanatorium",
"route_length": "41.42 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:20. Last A trip departure — 22:19. Last B trip departure — 21:46. Interval — 7.1 minutes.",
"operating_mode_weekend": "Weekends: start of movement — 5:45. Last A trip departure — 22:01. Last B trip departure — 22:01. Interval — 8.1 minutes."
},
{
"id": "47",
"from": "Batyrshina St.",
"to": "Universiade Village",
"route_length": "20.67 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Last A trip departure — 21:48. Last B trip departure — 22:51. Interval — 6.6 minutes.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Last A trip departure — 21:18. Last B trip departure — 22:20. Interval — 8.2 minutes."
},
{
"id": "49",
"from": "Clothing Market",
"to": "Lagernaya Station",
"route_length": "21.52 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Departure of the last trip A — 20:48. Departure of the last trip B — 19:48. Morning interval — 8.3 min, evening interval — 8.9 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Departure of the last trip A — 20:43. Departure of the last trip B — 19:50. Interval — 11.4 min."
},
{
"id": "53",
"from": "River Port",
"to": "Leningradskaya Street",
"route_length": "16.73 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Departure of the last trip A — 22:10. Departure of the last trip B — 21:14. Interval — 9.1 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:20. Departure of the last trip A — 21:41. Departure of the last trip B — 20:47. Interval — 12.4 min."
},
{
"id": "54",
"from": "River Port",
"to": "Gavrilov Street",
"route_length": "13.14 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:30. Departure of the last trip A — 22:26. Departure of the last trip B — 21:41. Interval — 5.6 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Departure of the last trip A — 22:20. Departure of the last trip B — 21:35. Interval — 9.1 min."
},
{
"id": "55",
"from": "39th Quarter",
"to": "Residential Complex «Forest Town»",
"route_length": "22.22 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:35. Departure of the last trip A — 22:02. Departure of the last trip B — 22:22. Interval — 8.7 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00. Departure of the last trip A — 22:02. Departure of the last trip B — 22:02. Interval — 15 min."
},
{
"id": "56",
"from": "R.Yakhin Street",
"to": "Petrovsky Residential Area",
"route_length": "17.10 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:40. Departure of the last trip A — 21:30. Departure of the last trip B — 20:46.",
"operating_mode_weekend": "Weekends: start of movement — 5:50. Departure of the last trip A — 18:11. Departure of the last trip B — 19:05."
},
{
"id": "60",
"from": "Khimicheskaya St.",
"to": "Residential Area Derbeshki",
"route_length": "26.62 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:15 AM. Last trip A departs at 10:15 PM. Last trip B departs at 9:08 PM. Morning interval: 7.7 min, evening interval: 8.5 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:15 AM. Last trip A departs at 9:55 PM. Last trip B departs at 8:43 PM. Interval: 8.2 min."
},
{
"id": "62",
"from": "DVVS",
"to": "Residential Complex 'Salavat Kupere'",
"route_length": "29.33 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:00 AM. Last trip A departs at 9:00 PM. Last trip B departs at 9:19 PM. Interval: 12.5 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:30 AM. Last trip A departs at 9:06 PM. Last trip B departs at 8:06 PM. Interval: 12 min."
},
{
"id": "63",
"from": "Komissar Gabishhev St.",
"to": "Residential Area Levchenko",
"route_length": "30.90 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:45 AM. Last trip A departs at 9:20 PM. Last trip B departs at 10:41 PM. Interval: 8.6 min.",
"operating_mode_weekend": "Weekends: start of movement — 6:00 AM. Last trip A departs at 8:56 PM. Last trip B departs at 10:26 PM. Interval: 11.5 min."
},
{
"id": "68",
"from": "Railway Station",
"to": "Iman St.",
"route_length": "20.87 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:00 AM. Last trip A departs at 9:47 PM. Last trip B departs at 8:46 PM. Interval: 10 min.",
"operating_mode_weekend": "Weekends: start of movement — 5:00 AM. Last trip A departs at 9:14 PM. Last trip B departs at 8:15 PM. Interval: 12.5 min."
},
{
"id": "70",
"from": "Chekhov Market",
"to": "Residential Complex 'Vesna'",
"route_length": "11.09 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:50 AM. Last trip A departs at 9:30 PM. Last trip B departs at 9:00 PM.",
"operating_mode_weekend": "Weekends: start of movement — 5:50 AM. Last trip A departs at 9:21 PM. Last trip B departs at 8:39 PM."
},
{
"id": "71",
"from": "City Clinical Hospital No. 5",
"to": "Residential Area Konstantinovka",
"route_length": "17.29 km",
"operating_mode_weekdays": "Weekdays: start of movement — 6:00 AM. Last trip A departs at 9:35 PM. Last trip B departs at 8:53 PM.",
"operating_mode_weekend": "Weekends: start of movement — 6:00 AM. Last trip A departs at 9:30 PM. Last trip B departs at 8:45 PM."
},
{
"id": "72",
"from": "Vakhitov Square",
"to": "Prophylactic Center",
"route_length": "27.33 km",
"operating_mode_weekdays": "Weekdays: start of movement — 5:40 AM. Last trip A departs at 10:25 PM. Last trip B departs at 9:59 PM.",
"operating_mode_weekend": "Weekends: start of movement — 6:10 AM. Last trip A departs at 8:45 PM. Last trip B departs at 8:14 PM."
},
{
"id": "74",
"from": "DVVS",
"to": "10th Microdistrict",
"route_length": "22.39 km",
"operating_mode_weekdays": "Weekdays: start time — 6:00 AM. Last departure A — 9:47 PM. Last departure B — 8:45 PM.",
"operating_mode_weekend": "Weekends: start time — 6:00 AM. Last departure A — 9:40 PM. Last departure B — 8:38 PM."
},
{
"id": "75",
"from": "Health Complex",
"to": "Universal-2",
"route_length": "11.60 km",
"operating_mode_weekdays": "Weekdays: start time — 5:40 AM. Last departure A — 10:25 PM. Last departure B — 9:59 PM.",
"operating_mode_weekend": "Weekends: start time — 6:10 AM. Last departure A — 8:45 PM. Last departure B — 8:14 PM."
},
{
"id": "77",
"from": "Duslyk Lane",
"to": "Oil Depot",
"route_length": "28.10 km",
"operating_mode_weekdays": "Weekdays: start time — 4:45 AM. Last departure A — 7:23 PM. Last departure B — 8:20 PM.",
"operating_mode_weekend": "Weekends: start time — 5:00 AM. Last departure A — 7:03 PM. Last departure B — 8:22 PM."
},
{
"id": "78",
"from": "Volgogradskaya Street",
"to": "Krutushka Residential Area",
"route_length": "18.74 km",
"operating_mode_weekdays": "Weekdays: start time — 6:00 AM. Last departure A — 7:35 PM. Last departure B — 8:20 PM.",
"operating_mode_weekend": "Weekends: start time — 6:00 AM. Last departure A — 7:35 PM. Last departure B — 8:20 PM."
},
{
"id": "84",
"from": "Khalitov Street",
"to": "Chebaksa Residential Area",
"route_length": "15.32 km",
"operating_mode_weekdays": "Weekdays: start time — 6:15 AM. Last departure A — 5:10 PM. Last departure B — 6:00 PM.",
"operating_mode_weekend": "Weekends: start time — 6:15 AM. Last departure A — 5:10 PM. Last departure B — 6:00 PM."
},
{
"id": "84a",
"from": "Khalitov Street",
"to": "Chebaksa Residential Area",
"route_length": "17.48 km",
"operating_mode_weekdays": "Weekdays: start time — 6:15 AM. Last departure A — 5:10 PM. Last departure B — 6:00 PM.",
"operating_mode_weekend": "Weekends: start time — 6:15 AM. Last departure A — 5:10 PM. Last departure B — 6:00 PM."
},
{
"id": "88",
"from": "Derbyshki Residential Area",
"to": "Kulseitovo Residential Area",
"route_length": "9.69 km",
"operating_mode_weekdays": "Weekdays: start time — 5:30 AM. Last departure A — 10:05 PM. Last departure B — 8:57 PM. Morning interval 7.4 min, evening interval 7.7 min.",
"operating_mode_weekend": "Weekends: start time — 5:30 AM. Last departure A — 9:30 PM. Last departure B — 8:00 PM. Interval 9.4 min."
},
{
"id": "89",
"from": "Akademika Sakharova Street",
"to": "Gudovantseva Street",
"route_length": "27.98 km",
"operating_mode_weekdays": "Weekdays: start of operation — 5:18. Last departure for Route A — 22:13. Last departure for Route B — 21:04. Interval — 7.3 min.",
"operating_mode_weekend": "Weekends: start of operation — 5:30. Last departure for Route A — 22:11. Last departure for Route B — 20:40. Interval — 9.1 min."
},
{
"id": "90",
"from": "«G.Tukay Square» Metro Station",
"to": "Kuyuki",
"route_length": "22.02 km",
"operating_mode_weekdays": "Weekdays: start of operation — 5:30. Last departure for Route A — 21:10. Last departure for Route B — 20:13. Interval — 13.8 min.",
"operating_mode_weekend": "Weekends: start of operation — 5:45. Last departure for Route A — 21:10. Last departure for Route B — 20:04. Interval — 19 min."
},
{
"id": "91",
"from": "TSUM",
"to": "Vysokaya Gora (ARZ)",
"route_length": "26.71 km",
"operating_mode_weekdays": "Weekdays: start of operation — 5:30. Last departure for Route A — 22:05. Last departure for Route B — 20:57. Interval — morning: 7.4 min, evening: 7.7 min.",
"operating_mode_weekend": "Weekends: start of operation — 5:30. Last departure for Route A — 21:30. Last departure for Route B — 20:00. Interval — 9.4 min."
},
{
"id": "93",
"from": "Lenin Cultural Center",
"to": "Bernovyye Kovaly Village",
"route_length": "21.20 km",
"operating_mode_weekdays": "Weekdays: start of operation — 6:00. Last departure for Route A — 18:10. Last departure for Route B — 18:55.",
"operating_mode_weekend": "Weekends: start of operation — 6:00. Last departure for Route A — 16:30. Last departure for Route B — 17:15."
},
{
"id": "94",
"from": "Dubravnaya",
"to": "Privolny Residential Area",
"route_length": "6.65 km",
"operating_mode_weekdays": "Weekdays: start of operation — 6:00. Last departure for Route A — 19:30. Last departure for Route B — 20:00.",
"operating_mode_weekend": "Weekends: start of operation — 6:00. Last departure for Route A — 19:30. Last departure for Route B — 20:00."
}
]

View File

@@ -1,450 +0,0 @@
[
{
"id": "1",
"from": "Речной порт",
"to": "жилой массив Дербышки",
"route_length": "18,03 км",
"operating_mode_weekdays": "Будни: начало движения — 5:24. Отправление в последний рейс А — 22:32. Отправление в последний рейс Б — 21:27. Интервал движения: утро — 6,9 мин, вечер — 7,3 мин.",
"operating_mode_weekend": " В выходные: начало движения — 5:30. Отправление в последний рейс А — 22:44. Отправление в последний рейс Б — 21:55. Интервал движения 7,5 мин."
},
{
"id": "2",
"from": "Станция метро «Аметьево»",
"to": "ул.Привокзальная",
"route_length": "20,9 км",
"operating_mode_weekdays": "Будни: начало движения — 5:40. Отправление в последний рейс А — 21:54. Отправление в последний рейс Б — 21:01. Интервал движения 8,5 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:40. Отправление в последний рейс А — 21:34. Отправление в последний рейс Б — 20:32. Интервал движения 9 мин."
},
{
"id": "4",
"from": "Жилой массив Ферма-2",
"to": "жилой массив Новая Сосновка",
"route_length": "24,59 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 21:02. Отправление в последний рейс Б — 20:50. Интервал движения 14,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:07. Отправление в последний рейс Б — 21:20. Интервал движения 22 мин."
},
{
"id": "5",
"from": "ЦУМ",
"to": "ул.Халитова",
"route_length": "27,42 км",
"operating_mode_weekdays": "Будни: начало движения — 5:43. Отправление в последний рейс А — 22:05. Отправление в последний рейс Б — 20:45. Интервал движения: утро 6,7 мин, вечер 7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:31. Отправление в последний рейс А — 22:15. Отправление в последний рейс Б — 21:03. Интервал движения 7,6 мин."
},
{
"id": "6",
"from": "Речной порт",
"to": "жилой массив Северный",
"route_length": "20,9 км",
"operating_mode_weekdays": "Будни: начало движения — 5:00. Отправление в последний рейс А — 22:24. Отправление в последний рейс Б — 21:32. Интервал движения 7,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:45. Отправление в последний рейс А — 22:07. Отправление в последний рейс Б — 21:20. Интервал движения 8,4 мин."
},
{
"id": "9",
"from": "Жилой массив Дербышки",
"to": "жилой массив Аки",
"route_length": "4,52 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 19:10. Отправление в последний рейс Б — 19:40.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 19:10. Отправление в последний рейс Б — 19:40."
},
{
"id": "10",
"from": "Ул.Серова",
"to": "ул.Академика Губкина (кольцевой)",
"route_length": "15,3 км",
"operating_mode_weekdays": "Будни: начало движения — 5:20. Отправление в последний рейс А — 22:12. Отправление в последний рейс Б — 23:04. Интервал движения 5,7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 22:30. Интервал движения 7,1 мин."
},
{
"id": "10a",
"from": "Ул.Серова",
"to": "ул.Академика Губкина (кольцевой)",
"route_length": "14,82 км",
"operating_mode_weekdays": "Будни: начало движения — 5:19. Отправление в последний рейс А — 21:52. Отправление в последний рейс Б — 22:23. Интервал движения 5,9 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:55. Отправление в последний рейс Б — 22:25. Интервал движения 7,2 мин."
},
{
"id": "11",
"from": "Жилой массив Дербышки",
"to": "жилой массив Малые Дербышки",
"route_length": "10,56 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 18:45. Отправление в последний рейс Б — 19:10.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 18:45. Отправление в последний рейс Б — 19:10."
},
{
"id": "15",
"from": "Комбинат «Здоровье»",
"to": "ул.Адоратского",
"route_length": "10,38 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:51. Отправление в последний рейс Б — 22:22. Интервал движения 6 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:50. Отправление в последний рейс Б — 22:30. Интервал движения 7 мин.99"
},
{
"id": "18",
"from": "Ул.Северополюсная",
"to": "переулок Дуслык",
"route_length": "31,41 км",
"operating_mode_weekdays": "Будни: начало движения — 4:50. Отправление в последний рейс А — 22:01. Отправление в последний рейс Б — 20:20. Интервал движения 8,5 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:40. Отправление в последний рейс А — 21:58. Отправление в последний рейс Б — 20:14. Интервал движения 10,2 мин."
},
{
"id": "22",
"from": "Ул.Можайского",
"to": "жилой массив Ферма-2",
"route_length": "24,45 км",
"operating_mode_weekdays": "5:30. Отправление в последний рейс А — 21:38. Отправление в последний рейс Б — 20:34. Интервал движения: утро 7,1 мин, вечер 7,4 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:40. Отправление в последний рейс А — 21:19. Отправление в последний рейс Б — 20:01. Интервал движения 10,1 мин."
},
{
"id": "23",
"from": "Ул.Р.Яхина",
"to": "жилой массив Мирный",
"route_length": "13,58 км",
"operating_mode_weekdays": "Будни: начало движения — 5:20. Отправление в последний рейс А — 22:11. Отправление в последний рейс Б — 21:40. Интервал движения: утро 12,9 мин, вечер 15,1 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:20. Отправление в последний рейс А — 21:05. Отправление в последний рейс Б — 21:13. Интервал движения 15 мин."
},
{
"id": "25",
"from": "Ул.Техническая",
"to": "жилой массив Дербышки",
"route_length": "20,66 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 19:30. Отправление в последний рейс Б — 18:30. Интервал движения 13,2 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:10. Отправление в последний рейс А — 18:10. Отправление в последний рейс Б — 17:50. Интервал движения 20 мин."
},
{
"id": "28",
"from": "ЦПКиО",
"to": "ул.Короленко (кольцевой)",
"route_length": "8,81 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 21:45. Отправление в последний рейс Б — 22:02. Интервал движения 7,6 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:38. Отправление в последний рейс Б — 22:01. Интервал движения 15 мин."
},
{
"id": "28a",
"from": "Чеховский рынок",
"to": "ул.Короленко (кольцевой)",
"route_length": "10,74 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 22:01. Интервал движения 8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 22:01. Интервал движения 15 мин."
},
{
"id": "29",
"from": "Строительный институт",
"to": "ул.Гудованцева",
"route_length": "17,23 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:30. Отправление в последний рейс Б — 21:40. Интервал движения 7,2 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:30. Отправление в последний рейс Б — 21:40. Интервал движения 9,2 мин."
},
{
"id": "30",
"from": "Переулок Дуслык",
"to": "станция Лагерная",
"route_length": "27,05 км",
"operating_mode_weekdays": "Будни: начало движения — 4:38. Отправление в последний рейс А — 21:24. Отправление в последний рейс Б — 22:40. Интервал движения: утро 6,6 мин, вечер 6,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:01. Отправление в последний рейс А — 20:56. Отправление в последний рейс Б — 22:18. Интервал движения 8,1 мин."
},
{
"id": "31",
"from": "Магазин «IKEA»",
"to": "жилой массив Старое Победилово",
"route_length": "29,48 км",
"operating_mode_weekdays": "Будни: начало движения — 5:15. Отправление в последний рейс А — 22:10. Отправление в последний рейс Б — 21:46. Интервал движения: утро 6,6 мин, вечер 6,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 22:10. Отправление в последний рейс Б — 21:51. Интервал движения 8 мин."
},
{
"id": "33",
"from": "Ул.Ленинградская",
"to": "жилой массив Ферма-2",
"route_length": "99999",
"operating_mode_weekdays": "88888",
"operating_mode_weekend": "99999"
},
{
"id": "34",
"from": "Жилой массив Ферма-2",
"to": "ДК им.Саид-Галеева",
"route_length": "25,39 км",
"operating_mode_weekdays": "Будни: начало движения — 5:25. Отправление в последний рейс А — 18:51. Отправление в последний рейс Б — 19:59. Интервал движения 14,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 17:44. Отправление в последний рейс Б — 18:48. Интервал движения 17,5 мин."
},
{
"id": "35",
"from": "Станция метро «Площадь Г.Тукая»",
"to": "ул.Гаврилова (кольцевой)",
"route_length": "14,33 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 22:26. Отправление в последний рейс Б — 21:35. Интервал движения 7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:15. Отправление в последний рейс Б — 21:21. Интервал движения 9,4 мин."
},
{
"id": "35a",
"from": "Станция метро «Площадь Г.Тукая»",
"to": "Авторынок (кольцевой)",
"route_length": "14,06 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 22:37. Отправление в последний рейс Б — 21:45. Интервал движения утро 6,4 мин, вечер 6,9 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:20. Отправление в последний рейс Б — 21:30. Интервал движения 9 мин."
},
{
"id": "36",
"from": "Вещевой рынок",
"to": "Осиново",
"route_length": "29,9 км",
"operating_mode_weekdays": "Будни: начало движения — 5:14. Отправление в последний рейс А — 21:42. Отправление в последний рейс Б — 20:28. Интервал движения: утро 6,8 мин, вечер 7,1 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:41. Отправление в последний рейс Б — 20:21. Интервал движения 11 мин."
},
{
"id": "36a",
"from": "Жилой массив Залесный",
"to": "Новониколаевский",
"route_length": "14,98 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 19:00. Отправление в последний рейс Б — 19:15.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 19:00. Отправление в последний рейс Б — 19:15."
},
{
"id": "37",
"from": "Жилой массив Ферма-2",
"to": "жилой массив Сухая Река",
"route_length": "30,26 км",
"operating_mode_weekdays": "Будни: начало движения — 4:58. Отправление в последний рейс А — 21:47. Отправление в последний рейс Б — 21:32. Интервал движения 10 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:48. Отправление в последний рейс Б — 20:54. Интервал движения 13,7 мин."
},
{
"id": "40",
"from": "Ул.Гаврилова",
"to": "кафе «Ак Барс»",
"route_length": "19,5 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 20:00. Отправление в последний рейс Б — 20:55.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 20:00. Отправление в последний рейс Б — 20:00."
},
{
"id": "42",
"from": "КДК им.Ленина",
"to": "жилой массив Борисоглебское",
"route_length": "8,56 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 19:10. Отправление в последний рейс Б -19:40.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 19:10. Отправление в последний рейс Б 19:40."
},
{
"id": "43",
"from": "Ул. Химическая",
"to": "ул.Техническая",
"route_length": "27,37 км",
"operating_mode_weekdays": "Будни: начало движения — 5:00. Отправление в последний рейс А — 21:45. Отправление в последний рейс Б — 21:45. Интервал движения 7,9 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:12. Отправление в последний рейс А — 20:32. Отправление в последний рейс Б — 22:02. Интервал движения 11,3 мин."
},
{
"id": "45",
"from": "Жилой массив Ферма-2",
"to": "Су Анасы",
"route_length": "39,28 км",
"operating_mode_weekdays": "Будни: начало движения — 4:57. Отправление в последний рейс А — 22:01. Отправление в последний рейс Б — 22:17. Интервал движения утро 7,1 мин, вечер 7,9 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:00. Отправление в последний рейс А — 21:46. Отправление в последний рейс Б — 22:00. Интервал движения 7,6 мин."
},
{
"id": "46",
"from": "Жилой комплекс «Экопарк „Дубрава“»",
"to": "Профилакторий",
"route_length": "41,42 км",
"operating_mode_weekdays": "Будни: начало движения — 5:20. Отправление в последний рейс А — 22:19. Отправление в последний рейс Б — 21:46. Интервал движения 7,1 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:45. Отправление в последний рейс А — 22:01. Отправление в последний рейс Б — 22:01. Интервал движения 8,1 мин."
},
{
"id": "47",
"from": "Ул.Батыршина",
"to": "Деревня Универсиады",
"route_length": "20,67 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 21:48. Отправление в последний рейс Б — 22:51. Интервал движения 6,6 мин",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:18. Отправление в последний рейс Б — 22:20. Интервал движения 8,2 мин."
},
{
"id": "49",
"from": "Вещевой рынок",
"to": "станция Лагерная",
"route_length": "21,52 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 20:48. Отправление в последний рейс Б — 19:48. Интервал движения утро 8,3 мин, вечер 8,9 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 20:43. Отправление в последний рейс Б — 19:50. Интервал движения 11,4 мин."
},
{
"id": "53",
"from": "Речной порт",
"to": "ул.Ленинградская",
"route_length": "16,73 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:10. Отправление в последний рейс Б — 21:14. Интервал движения 9,1 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:20. Отправление в последний рейс А — 21:41. Отправление в последний рейс Б — 20:47. Интервал движения 12,4 мин."
},
{
"id": "54",
"from": "Речной порт",
"to": "ул.Гаврилова",
"route_length": "13,14 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:26. Отправление в последний рейс Б — 21:41. Интервал движения 5,6 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:20. Отправление в последний рейс Б — 21:35. Интервал движения 9,1 мин."
},
{
"id": "55",
"from": "39-й квартал",
"to": "жилой комплекс «Лесной городок»",
"route_length": "22,22 км",
"operating_mode_weekdays": "Будни: начало движения — 5:35. Отправление в последний рейс А — 22:02. Отправление в последний рейс Б — 22:22. Интервал движения 8,7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 22:02. Отправление в последний рейс Б — 22:02. Интервал движения 15 мин."
},
{
"id": "56",
"from": "Ул.Р.Яхина",
"to": "жилой массив Петровский",
"route_length": "17,10 км",
"operating_mode_weekdays": "Будни: начало движения — 5:40. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 20:46.",
"operating_mode_weekend": "В выходные: начало движения — 5:50. Отправление в последний рейс А — 18:11. Отправление в последний рейс Б — 19:05."
},
{
"id": "60",
"from": "Ул. Химическая",
"to": "жилой массив Дербышки",
"route_length": "26,62 км",
"operating_mode_weekdays": "Будни: начало движения — 5:15. Отправление в последний рейс А — 22:15. Отправление в последний рейс Б — 21:08. Интервал движения утро 7,7 мин, вечер 8,5 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:15. Отправление в последний рейс А — 21:55. Отправление в последний рейс Б — 20:43. Интервал движения 8,2 мин."
},
{
"id": "62",
"from": "ДВВС",
"to": "жилой комплекс «Салават Купере»",
"route_length": "29,33 км",
"operating_mode_weekdays": "Будни: начало движения — 5:00. Отправление в последний рейс А — 21:00. Отправление в последний рейс Б — 21:19. Интервал движения 12,5 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:06. Отправление в последний рейс Б — 20:06. Интервал движения 12 мин."
},
{
"id": "63",
"from": "Ул.Комиссара Габишева",
"to": "жилой массив Левченко",
"route_length": "30,90 км",
"operating_mode_weekdays": "Будни: начало движения — 5:45. Отправление в последний рейс А — 21:20. Отправление в последний рейс Б — 22:41. Интервал движения 8,6 мин.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 20:56. Отправление в последний рейс Б — 22:26. Интервал движения 11,5 мин."
},
{
"id": "68",
"from": "Ж/д вокзал",
"to": "ул.Иман",
"route_length": "20,87 км",
"operating_mode_weekdays": "Будни: начало движения — 5:00. Отправление в последний рейс А — 21:47. Отправление в последний рейс Б — 20:46. Интервал движения 10 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:00. Отправление в последний рейс А — 21:14. Отправление в последний рейс Б — 20:15. Интервал движения 12,5 мин."
},
{
"id": "70",
"from": "Чеховский рынок",
"to": "жилой комплекс «Весна»",
"route_length": "11,09 км",
"operating_mode_weekdays": "Будни: начало движения — 5:50. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 21:00.",
"operating_mode_weekend": "В выходные: начало движения — 5:50. Отправление в последний рейс А — 21:21. Отправление в последний рейс Б — 20:39."
},
{
"id": "71",
"from": "Городская клиническая больница №5",
"to": "жилой массив Константиновка",
"route_length": "17,29 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 21:35. Отправление в последний рейс Б — 20:53.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 20:45."
},
{
"id": "72",
"from": "Площадь Вахитова",
"to": "Профилакторий",
"route_length": "27,33 км",
"operating_mode_weekdays": "Будни: начало движения — 5:40. Отправление в последний рейс А — 22:25. Отправление в последний рейс Б — 21:59.",
"operating_mode_weekend": "В выходные: начало движения — 6:10. Отправление в последний рейс А — 20:45. Отправление в последний рейс Б — 20:14."
},
{
"id": "74",
"from": "ДВВС",
"to": "10-й микрорайон",
"route_length": "22,39 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 21:47. Отправление в последний рейс Б — 20:45.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 21:40. Отправление в последний рейс Б — 20:38."
},
{
"id": "75",
"from": "Комбинат «Здоровье»",
"to": "Универсам-2",
"route_length": "11,60 км",
"operating_mode_weekdays": "Будни: начало движения — 5:40. Отправление в последний рейс А — 22:25. Отправление в последний рейс Б — 21:59.",
"operating_mode_weekend": "В выходные: начало движения — 6:10. Отправление в последний рейс А — 20:45. Отправление в последний рейс Б — 20:14."
},
{
"id": "77",
"from": "Переулок Дуслык",
"to": "Нефтебаза",
"route_length": "28,10 км",
"operating_mode_weekdays": "Будни: начало движения — 4:45. Отправление в последний рейс А — 19:23. Отправление в последний рейс Б — 20:20.",
"operating_mode_weekend": "В выходные: начало движения — 5:00. Отправление в последний рейс А — 19:03. Отправление в последний рейс Б — 20:22."
},
{
"id": "78",
"from": "Ул.Волгоградская",
"to": "жилой массив Крутушка",
"route_length": " 18,74 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 19:35. Отправление в последний рейс Б — 20:20.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 19:35. Отправление в последний рейс Б — 20:20."
},
{
"id": "84",
"from": "Ул. Халитова",
"to": "жилой массив Чебакса",
"route_length": "15,32 км",
"operating_mode_weekdays": "Будни: начало движения — 6:15. Отправление в последний рейс А — 17:10. Отправление в последний рейс Б — 18:00.",
"operating_mode_weekend": "В выходные: начало движения — 6:15. Отправление в последний рейс А — 17:10. Отправление в последний рейс Б — 18:00."
},
{
"id": "84а",
"from": "Ул. Халитова",
"to": "жилой массив Чебакса",
"route_length": "17,48 км",
"operating_mode_weekdays": "Будни: начало движения — 6:15. Отправление в последний рейс А — 17:10. Отправление в последний рейс Б — 18:00.",
"operating_mode_weekend": "В выходные: начало движения — 6:15. Отправление в последний рейс А — 17:10. Отправление в последний рейс Б — 18:00."
},
{
"id": "88",
"from": "Жилой массив Дербышки",
"to": "жилой массив Кульсеитово",
"route_length": "9,69 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:05. Отправление в последний рейс Б — 20:57. Интервал движения утро 7,4 мин, вечер 7,7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 20:00. Интервал движения 9,4 мин."
},
{
"id": "89",
"from": "Ул.Академика Сахарова",
"to": "ул.Гудованцева",
"route_length": "27,98 км",
"operating_mode_weekdays": "Будни: начало движения — 5:18. Отправление в последний рейс А — 22:13. Отправление в последний рейс Б — 21:04. Интервал движения 7,3 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 22:11. Отправление в последний рейс Б — 20:40. Интервал движения 9,1 мин."
},
{
"id": "90",
"from": "Станция метро «Площадь Г.Тукая»",
"to": "Куюки",
"route_length": "22,02 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 21:10. Отправление в последний рейс Б — 20:13. Интервал движения 13,8 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:45. Отправление в последний рейс А — 21:10. Отправление в последний рейс Б — 20:04. Интервал движения 19 мин."
},
{
"id": "91",
"from": "ЦУМ",
"to": "Высокая Гора (АРЗ)",
"route_length": "26,71 км",
"operating_mode_weekdays": "Будни: начало движения — 5:30. Отправление в последний рейс А — 22:05. Отправление в последний рейс Б — 20:57. Интервал движения утро 7,4 мин, вечер 7,7 мин.",
"operating_mode_weekend": "В выходные: начало движения — 5:30. Отправление в последний рейс А — 21:30. Отправление в последний рейс Б — 20:00. Интервал движения 9,4 мин."
},
{
"id": "93",
"from": "КДК им.Ленина",
"to": "н.п.Берновые Ковали",
"route_length": "21,20 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 18:10. Отправление в последний рейс Б — 18:55.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 16:30. Отправление в последний рейс Б — 17:15."
},
{
"id": "94",
"from": "Дубравная",
"to": "жилой массив Привольный",
"route_length": "6,65 км",
"operating_mode_weekdays": "Будни: начало движения — 6:00. Отправление в последний рейс А — 19:30. Отправление в последний рейс Б — 20:00.",
"operating_mode_weekend": "В выходные: начало движения — 6:00. Отправление в последний рейс А — 19:30. Отправление в последний рейс Б — 20:00."
}
]

View File

@@ -1,450 +0,0 @@
[
{
"id": "1",
"from": "Елга порты",
"to": "Дербышки торак массивы",
"route_length": "18,03 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:24. Соңгы рейс А кузгала — 22:32. Соңгы рейс В кузгала — 21:27. Интервал: иртән — 6,9 мин, кич — 7,3 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:30. Соңгы рейс А кузгала — 22:44. Соңгы рейс В кузгала — 21:55. Интервал — 7,5 мин."
},
{
"id": "2",
"from": "«Әмәт» метро станциясе",
"to": "Привокзальная урамы",
"route_length": "20,9 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:40. Соңгы рейс А кузгала — 21:54. Соңгы рейс В кузгала — 21:01. Интервал — 8,5 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:40. Соңгы рейс А кузгала — 21:34. Соңгы рейс В кузгала — 20:32. Интервал — 9 мин."
},
{
"id": "4",
"from": "Ферма-2 торак массивы",
"to": "Яңа Сосновка торак массивы",
"route_length": "24,59 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:30. Соңгы рейс А кузгала — 21:02. Соңгы рейс В кузгала — 20:50. Интервал — 14,8 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:30. Соңгы рейс А кузгала — 21:07. Соңгы рейс В кузгала — 21:20. Интервал — 22 мин."
},
{
"id": "5",
"from": "ЦУМ",
"to": "Халитова урамы",
"route_length": "27,42 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:43. Соңгы рейс А кузгала — 22:05. Соңгы рейс В кузгала — 20:45. Интервал: иртән — 6,7 мин, кич — 7 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:31. Соңгы рейс А кузгала — 22:15. Соңгы рейс В кузгала — 21:03. Интервал — 7,6 мин."
},
{
"id": "6",
"from": "Елга порты",
"to": "Төньяк торак массивы",
"route_length": "20,9 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:00. Соңгы рейс А кузгала — 22:24. Соңгы рейс В кузгала — 21:32. Интервал — 7,8 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:45. Соңгы рейс А кузгала — 22:07. Соңгы рейс В кузгала — 21:20. Интервал — 8,4 мин."
},
{
"id": "9",
"from": "Дербышки торак массивы",
"to": "Аки торак массивы",
"route_length": "4,52 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А китү — 19:10. Соңгы рейс Б китү — 19:40.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 19:10. Соңгы рейс Б китү — 19:40."
},
{
"id": "10",
"from": "Серова урамы",
"to": "Губкин академигы урамы (кольцевой)",
"route_length": "15,3 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:20. Соңгы рейс А китү — 22:12. Соңгы рейс Б китү — 23:04. Хәрәкәт интервалы 5,7 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:30. Соңгы рейс Б китү — 22:30. Хәрәкәт интервалы 7,1 минут."
},
{
"id": "10a",
"from": "Серова урамы",
"to": "Губкин академигы урамы (кольцевой)",
"route_length": "14,82 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:19. Соңгы рейс А китү — 21:52. Соңгы рейс Б китү — 22:23. Хәрәкәт интервалы 5,9 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:55. Соңгы рейс Б китү — 22:25. Хәрәкәт интервалы 7,2 минут."
},
{
"id": "11",
"from": "Дербышки торак массивы",
"to": "Малые Дербышки торак массивы",
"route_length": "10,56 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А китү — 18:45. Соңгы рейс Б китү — 19:10.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 18:45. Соңгы рейс Б китү — 19:10."
},
{
"id": "15",
"from": "«Здоровье» комбинаты",
"to": "Адоратский урамы",
"route_length": "10,38 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:30. Соңгы рейс А китү — 22:51. Соңгы рейс Б китү — 22:22. Хәрәкәт интервалы 6 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 22:50. Соңгы рейс Б китү — 22:30. Хәрәкәт интервалы 7 минут."
},
{
"id": "18",
"from": "Северополюсная урамы",
"to": "Дуслык урамы",
"route_length": "31,41 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 4:50. Соңгы рейс А китү — 22:01. Соңгы рейс Б китү — 20:20. Хәрәкәт интервалы 8,5 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:40. Соңгы рейс А китү — 21:58. Соңгы рейс Б китү — 20:14. Хәрәкәт интервалы 10,2 минут."
},
{
"id": "22",
"from": "Можайский урамы",
"to": "Ферма-2 торак массивы",
"route_length": "24,45 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:30. Соңгы рейс А китү — 21:38. Соңгы рейс Б китү — 20:34. Хәрәкәт интервалы: иртән 7,1 минут, кич 7,4 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:40. Соңгы рейс А китү — 21:19. Соңгы рейс Б китү — 20:01. Хәрәкәт интервалы 10,1 минут."
},
{
"id": "23",
"from": "Р.Яхина урамы",
"to": "Мирный торак массивы",
"route_length": "13,58 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:20. Соңгы рейс А китү — 22:11. Соңгы рейс Б китү — 21:40. Хәрәкәт интервалы: иртән 12,9 минут, кич 15,1 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:20. Соңгы рейс А китү — 21:05. Соңгы рейс Б китү — 21:13. Хәрәкәт интервалы 15 минут."
},
{
"id": "25",
"from": "Техническая урамы",
"to": "Дербышки торак массивы",
"route_length": "20,66 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:30. Соңгы рейс А китү — 19:30. Соңгы рейс Б китү — 18:30. Хәрәкәт интервалы 13,2 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:10. Соңгы рейс А китү — 18:10. Соңгы рейс Б китү — 17:50. Хәрәкәт интервалы 20 минут."
},
{
"id": "28",
"from": "ЦПКиО",
"to": "Короленко урамы (кольцевой)",
"route_length": "8,81 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:45. Соңгы рейс Б китү — 22:02. Хәрәкәт интервалы 7,6 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:38. Соңгы рейс Б китү — 22:01. Хәрәкәт интервалы 15 минут."
},
{
"id": "28a",
"from": "Чеховский базары",
"to": "Короленко урамы (кольцевой)",
"route_length": "10,74 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:30. Соңгы рейс Б китү — 22:01. Хәрәкәт интервалы 8 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 21:30. Соңгы рейс Б китү — 22:01. Хәрәкәт интервалы 15 минут."
},
{
"id": "29",
"from": "Строительный институт",
"to": "Гудованцева урамы",
"route_length": "17,23 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:30. Соңгы рейс А китү — 22:30. Соңгы рейс Б китү — 21:40. Хәрәкәт интервалы 7,2 минут.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы рейс А китү — 22:30. Соңгы рейс Б китү — 21:40. Хәрәкәт интервалы 9,2 минут."
},
{
"id": "30",
"from": "Переулок Дуслык",
"to": "станция Лагерная",
"route_length": "27,05 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 4:38. Соңгы рейс А — 21:24. Соңгы рейс Б — 22:40. Хәрәкәт интерваллары: иртә 6,6 мин, кич 6,8 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 5:01. Соңгы рейс А — 20:56. Соңгы рейс Б — 22:18. Хәрәкәт интерваллары 8,1 мин."
},
{
"id": "31",
"from": "Магазин «IKEA»",
"to": "жилой массив Старое Победилово",
"route_length": "29,48 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:15. Соңгы рейс А — 22:10. Соңгы рейс Б — 21:46. Хәрәкәт интерваллары: иртә 6,6 мин, кич 6,8 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 5:30. Соңгы рейс А — 22:10. Соңгы рейс Б — 21:51. Хәрәкәт интерваллары 8 мин."
},
{
"id": "33",
"from": "Ул.Ленинградская",
"to": "жилой массив Ферма-2",
"route_length": "99999",
"operating_mode_weekdays": "88888",
"operating_mode_weekend": "99999"
},
{
"id": "34",
"from": "Жилой массив Ферма-2",
"to": "ДК им.Саид-Галеева",
"route_length": "25,39 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:25. Соңгы рейс А — 18:51. Соңгы рейс Б — 19:59. Хәрәкәт интерваллары 14,8 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 17:44. Соңгы рейс Б — 18:48. Хәрәкәт интерваллары 17,5 мин."
},
{
"id": "35",
"from": "Станция метро «Площадь Г.Тукая»",
"to": "ул.Гаврилова (кольцевой)",
"route_length": "14,33 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 22:26. Соңгы рейс Б — 21:35. Хәрәкәт интерваллары 7 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 22:15. Соңгы рейс Б — 21:21. Хәрәкәт интерваллары 9,4 мин."
},
{
"id": "35a",
"from": "Станция метро «Площадь Г.Тукая»",
"to": "Авторынок (кольцевой)",
"route_length": "14,06 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 22:37. Соңгы рейс Б — 21:45. Хәрәкәт интерваллары иртә 6,4 мин, кич 6,9 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 22:20. Соңгы рейс Б — 21:30. Хәрәкәт интерваллары 9 мин."
},
{
"id": "36",
"from": "Вещевой рынок",
"to": "Осиново",
"route_length": "29,9 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 5:14. Соңгы рейс А — 21:42. Соңгы рейс Б — 20:28. Хәрәкәт интерваллары: иртә 6,8 мин, кич 7,1 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 5:30. Соңгы рейс А — 21:41. Соңгы рейс Б — 20:21. Хәрәкәт интерваллары 11 мин."
},
{
"id": "36a",
"from": "Жилой массив Залесный",
"to": "Новониколаевский",
"route_length": "14,98 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 19:00. Соңгы рейс Б — 19:15.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 19:00. Соңгы рейс Б — 19:15."
},
{
"id": "37",
"from": "Жилой массив Ферма-2",
"to": "жилой массив Сухая Река",
"route_length": "30,26 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 4:58. Соңгы рейс А — 21:47. Соңгы рейс Б — 21:32. Хәрәкәт интерваллары 10 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 5:30. Соңгы рейс А — 21:48. Соңгы рейс Б — 20:54. Хәрәкәт интерваллары 13,7 мин."
},
{
"id": "40",
"from": "Ул.Гаврилова",
"to": "кафе «Ак Барс»",
"route_length": "19,5 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 20:00. Соңгы рейс Б — 20:55.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башы — 6:00. Соңгы рейс А — 20:00. Соңгы рейс Б — 20:00."
},
{
"id": "42",
"from": "Ленин исемендәге мәдәният йорты",
"to": "Борисоглебское торак массивы",
"route_length": "8,56 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлану вакыты — 6:00. Соңгы А рейсы кузгалу вакыты — 19:10. Соңгы Б рейсы кузгалу вакыты — 19:40.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлану вакыты — 6:00. Соңгы А рейсы кузгалу вакыты — 19:10. Соңгы Б рейсы кузгалу вакыты — 19:40."
},
{
"id": "43",
"from": "Химик урамы",
"to": "Техник урамы",
"route_length": "27,37 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлану вакыты — 5:00. Соңгы А рейсы кузгалу вакыты — 21:45. Соңгы Б рейсы кузгалу вакыты — 21:45. Хәрәкәт интервалы — 7,9 минут.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлану вакыты — 5:12. Соңгы А рейсы кузгалу вакыты — 20:32. Соңгы Б рейсы кузгалу вакыты — 22:02. Хәрәкәт интервалы — 11,3 минут."
},
{
"id": "45",
"from": "Ферма-2 торак массивы",
"to": "Су Анасы",
"route_length": "39,28 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлану вакыты — 4:57. Соңгы А рейсы кузгалу вакыты — 22:01. Соңгы Б рейсы кузгалу вакыты — 22:17. Хәрәкәт интервалы иртән — 7,1 минут, кич — 7,9 минут.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлану вакыты — 5:00. Соңгы А рейсы кузгалу вакыты — 21:46. Соңгы Б рейсы кузгалу вакыты — 22:00. Хәрәкәт интервалы — 7,6 минут."
},
{
"id": "46",
"from": "«Дубрава» экопаркы",
"to": "Профилакторий",
"route_length": "41,42 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлану вакыты — 5:20. Соңгы А рейсы кузгалу вакыты — 22:19. Соңгы Б рейсы кузгалу вакыты — 21:46. Хәрәкәт интервалы — 7,1 минут.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлану вакыты — 5:45. Соңгы А рейсы кузгалу вакыты — 22:01. Соңгы Б рейсы кузгалу вакыты — 22:01. Хәрәкәт интервалы — 8,1 минут."
},
{
"id": "47",
"from": "Батыршин урамы",
"to": "Универсиада авылы",
"route_length": "20,67 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлану вакыты — 5:30. Соңгы А рейсы кузгалу вакыты — 21:48. Соңгы Б рейсы кузгалу вакыты — 22:51. Хәрәкәт интервалы — 6,6 минут.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлану вакыты — 6:00. Соңгы А рейсы кузгалу вакыты — 21:18. Соңгы Б рейсы кузгалу вакыты — 22:20. Хәрәкәт интервалы — 8,2 минут."
},
{
"id": "49",
"from": "Вещевой базар",
"to": "Лагерная станциясе",
"route_length": "21,52 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башлану — 5:30. Соңгы рейс А китү — 20:48. Соңгы рейс Б китү — 19:48. Иртәнге интервал — 8,3 мин, кичке интервал — 8,9 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башлану — 6:00. Соңгы рейс А китү — 20:43. Соңгы рейс Б китү — 19:50. Интервал — 11,4 мин."
},
{
"id": "53",
"from": "Елга порты",
"to": "Ленинград урамы",
"route_length": "16,73 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башлану — 5:30. Соңгы рейс А китү — 22:10. Соңгы рейс Б китү — 21:14. Интервал — 9,1 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башлану — 5:20. Соңгы рейс А китү — 21:41. Соңгы рейс Б китү — 20:47. Интервал — 12,4 мин."
},
{
"id": "54",
"from": "Елга порты",
"to": "Гаврилов урамы",
"route_length": "13,14 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башлану — 5:30. Соңгы рейс А китү — 22:26. Соңгы рейс Б китү — 21:41. Интервал — 5,6 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башлану — 6:00. Соңгы рейс А китү — 22:20. Соңгы рейс Б китү — 21:35. Интервал — 9,1 мин."
},
{
"id": "55",
"from": "39нчы квартал",
"to": "«Урман шәһәрчеге» торак комплексы",
"route_length": "22,22 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башлану — 5:35. Соңгы рейс А китү — 22:02. Соңгы рейс Б китү — 22:22. Интервал — 8,7 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башлану — 6:00. Соңгы рейс А китү — 22:02. Соңгы рейс Б китү — 22:02. Интервал — 15 мин."
},
{
"id": "56",
"from": "Р.Яхин урамы",
"to": "Петровский торак массивы",
"route_length": "17,10 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башлану — 5:40. Соңгы рейс А китү — 21:30. Соңгы рейс Б китү — 20:46.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башлану — 5:50. Соңгы рейс А китү — 18:11. Соңгы рейс Б китү — 19:05."
},
{
"id": "60",
"from": "Химия урамы",
"to": "Дербышки торак массивы",
"route_length": "26,62 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:15. Соңгы А рейсы кузгалу вакыты — 22:15. Соңгы Б рейсы кузгалу вакыты — 21:08. Иртән интервал 7,7 мин, кич интервал 8,5 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:15. Соңгы А рейсы кузгалу вакыты — 21:55. Соңгы Б рейсы кузгалу вакыты — 20:43. Интервал 8,2 мин."
},
{
"id": "62",
"from": "ДВВС",
"to": "Салават Купере торак комплексы",
"route_length": "29,33 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:00. Соңгы А рейсы кузгалу вакыты — 21:00. Соңгы Б рейсы кузгалу вакыты — 21:19. Интервал 12,5 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:30. Соңгы А рейсы кузгалу вакыты — 21:06. Соңгы Б рейсы кузгалу вакыты — 20:06. Интервал 12 мин."
},
{
"id": "63",
"from": "Комиссар Габишев урамы",
"to": "Левченко торак массивы",
"route_length": "30,90 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:45. Соңгы А рейсы кузгалу вакыты — 21:20. Соңгы Б рейсы кузгалу вакыты — 22:41. Интервал 8,6 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы А рейсы кузгалу вакыты — 20:56. Соңгы Б рейсы кузгалу вакыты — 22:26. Интервал 11,5 мин."
},
{
"id": "68",
"from": "Тимер юл вокзалы",
"to": "Иман урамы",
"route_length": "20,87 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:00. Соңгы А рейсы кузгалу вакыты — 21:47. Соңгы Б рейсы кузгалу вакыты — 20:46. Интервал 10 мин.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:00. Соңгы А рейсы кузгалу вакыты — 21:14. Соңгы Б рейсы кузгалу вакыты — 20:15. Интервал 12,5 мин."
},
{
"id": "70",
"from": "Чехов базары",
"to": "Весна торак комплексы",
"route_length": "11,09 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:50. Соңгы А рейсы кузгалу вакыты — 21:30. Соңгы Б рейсы кузгалу вакыты — 21:00.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 5:50. Соңгы А рейсы кузгалу вакыты — 21:21. Соңгы Б рейсы кузгалу вакыты — 20:39."
},
{
"id": "71",
"from": "5-нче шәһәр клиник хастаханәсе",
"to": "Константиновка торак массивы",
"route_length": "17,29 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 6:00. Соңгы А рейсы кузгалу вакыты — 21:35. Соңгы Б рейсы кузгалу вакыты — 20:53.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:00. Соңгы А рейсы кузгалу вакыты — 21:30. Соңгы Б рейсы кузгалу вакыты — 20:45."
},
{
"id": "72",
"from": "Вахитов мәйданы",
"to": "Профилакторий",
"route_length": "27,33 км",
"operating_mode_weekdays": "Эш көннәрендә: хәрәкәт башы — 5:40. Соңгы А рейсы кузгалу вакыты — 22:25. Соңгы Б рейсы кузгалу вакыты — 21:59.",
"operating_mode_weekend": "Ял көннәрендә: хәрәкәт башы — 6:10. Соңгы А рейсы кузгалу вакыты — 20:45. Соңгы Б рейсы кузгалу вакыты — 20:14."
},
{
"id": "74",
"from": "ДВВС",
"to": "10-нчы микрорайон",
"route_length": "22,39 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:00. Соңгы А рейсына китү — 21:47. Соңгы Б рейсына китү — 20:45.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:00. Соңгы А рейсына китү — 21:40. Соңгы Б рейсына китү — 20:38."
},
{
"id": "75",
"from": "«Сәламәтлек» комбинаты",
"to": "Универсам-2",
"route_length": "11,60 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 5:40. Соңгы А рейсына китү — 22:25. Соңгы Б рейсына китү — 21:59.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:10. Соңгы А рейсына китү — 20:45. Соңгы Б рейсына китү — 20:14."
},
{
"id": "77",
"from": "Дуслык тыкрыгы",
"to": "Нефтебаза",
"route_length": "28,10 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 4:45. Соңгы А рейсына китү — 19:23. Соңгы Б рейсына китү — 20:20.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 5:00. Соңгы А рейсына китү — 19:03. Соңгы Б рейсына китү — 20:22."
},
{
"id": "78",
"from": "Волгоград урамы",
"to": "Крутушка торак массивы",
"route_length": "18,74 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:00. Соңгы А рейсына китү — 19:35. Соңгы Б рейсына китү — 20:20.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:00. Соңгы А рейсына китү — 19:35. Соңгы Б рейсына китү — 20:20."
},
{
"id": "84",
"from": "Халитов урамы",
"to": "Чебакса торак массивы",
"route_length": "15,32 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:15. Соңгы А рейсына китү — 17:10. Соңгы Б рейсына китү — 18:00.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:15. Соңгы А рейсына китү — 17:10. Соңгы Б рейсына китү — 18:00."
},
{
"id": "84а",
"from": "Халитов урамы",
"to": "Чебакса торак массивы",
"route_length": "17,48 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:15. Соңгы А рейсына китү — 17:10. Соңгы Б рейсына китү — 18:00.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:15. Соңгы А рейсына китү — 17:10. Соңгы Б рейсына китү — 18:00."
},
{
"id": "88",
"from": "Дербышки торак массивы",
"to": "Кульсеитово торак массивы",
"route_length": "9,69 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 5:30. Соңгы А рейсына китү — 22:05. Соңгы Б рейсына китү — 20:57. Иртә белән интервал 7,4 мин, кич белән 7,7 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 5:30. Соңгы А рейсына китү — 21:30. Соңгы Б рейсына китү — 20:00. Интервал 9,4 мин."
},
{
"id": "89",
"from": "Академик Сахаров урамы",
"to": "Гудованцев урамы",
"route_length": "27,98 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 5:18. Соңгы рейс А китүе — 22:13. Соңгы рейс Б китүе — 21:04. Хәрәкәт интервалы 7,3 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 5:30. Соңгы рейс А китүе — 22:11. Соңгы рейс Б китүе — 20:40. Хәрәкәт интервалы 9,1 мин."
},
{
"id": "90",
"from": "«Г.Тукай мәйданы» метро станциясе",
"to": "Куюки",
"route_length": "22,02 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 5:30. Соңгы рейс А китүе — 21:10. Соңгы рейс Б китүе — 20:13. Хәрәкәт интервалы 13,8 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 5:45. Соңгы рейс А китүе — 21:10. Соңгы рейс Б китүе — 20:04. Хәрәкәт интервалы 19 мин."
},
{
"id": "91",
"from": "ЦУМ",
"to": "Югары таудагы (АРЗ)",
"route_length": "26,71 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 5:30. Соңгы рейс А китүе — 22:05. Соңгы рейс Б китүе — 20:57. Хәрәкәт интервалы иртән — 7,4 мин, кичен — 7,7 мин.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 5:30. Соңгы рейс А китүе — 21:30. Соңгы рейс Б китүе — 20:00. Хәрәкәт интервалы 9,4 мин."
},
{
"id": "93",
"from": "Ленин исемендәге мәдәни-күңел ачу үзәге",
"to": "Бернов Ковали авылы",
"route_length": "21,20 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:00. Соңгы рейс А китүе — 18:10. Соңгы рейс Б китүе — 18:55.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:00. Соңгы рейс А китүе — 16:30. Соңгы рейс Б китүе — 17:15."
},
{
"id": "94",
"from": "Дубравная",
"to": "Привольный торак массивы",
"route_length": "6,65 км",
"operating_mode_weekdays": "Эш көннәре: хәрәкәт башлануы — 6:00. Соңгы рейс А китүе — 19:30. Соңгы рейс Б китүе — 20:00.",
"operating_mode_weekend": "Ял көннәре: хәрәкәт башлануы — 6:00. Соңгы рейс А китүе — 19:30. Соңгы рейс Б китүе — 20:00."
}
]

View File

@@ -1,27 +0,0 @@
const { Schema, model } = require('mongoose')
const { KAZAN_EXPLORE_RESULTS_MODEL_NAME } = require('../const')
const schema = new Schema({
userId: { type: String },
items: [
{
quizId: { type: String },
result: { type: Number }
}
]
})
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform: function (doc, ret) {
delete ret._id
}
})
schema.virtual('id').get(function () {
return this._id.toHexString()
})
exports.ResultsModel = model(KAZAN_EXPLORE_RESULTS_MODEL_NAME, schema)

View File

@@ -1 +0,0 @@
GIGACHAT_API_KEY=NzgzNTkxMjMtNDQ0Ny00ODFhLTkwMjgtODYxZjUzYjI0ZWQxOjA5NDEwMzEwLTM5YjItNDUzOS1hYWYzLWE4ZDA1MDExNmQ4Nw==

View File

@@ -1,2 +0,0 @@
node_modules/
.env

View File

@@ -1,21 +0,0 @@
# back-new
非Python实现的后端Node.js + Express
## 启动方法
1. 安装依赖:
```bash
npm install
```
2. 启动服务:
```bash
npm start
```
默认端口:`3002`
## 支持接口
- POST `/api/auth/login` 用户登录
- POST `/api/auth/register` 用户注册
- GET `/gigachat/prompt?prompt=xxx` 生成图片(返回模拟图片链接)

View File

@@ -1,24 +0,0 @@
const express = require('express');
const cors = require('cors');
const featuresConfig = require('./features.config');
const imageRoutes = require('./features/image/image.routes');
const app = express();
app.use(cors());
app.use(express.json());
if (featuresConfig.auth) {
app.use('/api/auth', require('./features/auth/auth.routes'));
}
if (featuresConfig.user) {
app.use('/api/user', require('./features/user/user.routes'));
}
if (featuresConfig.image) {
app.use('/gigachat', imageRoutes);
}
app.get('/api/', (req, res) => {
res.json({ message: 'API root' });
});
module.exports = app;

View File

@@ -1,5 +0,0 @@
module.exports = {
auth: true,
user: true,
image: true, // 关闭为 false
};

View File

@@ -1,95 +0,0 @@
const usersDb = require('../../shared/usersDb');
const makeLinks = require('../../shared/hateoas');
exports.login = (req, res) => {
const { username, password, email } = req.body;
const user = usersDb.findUser(username, email, password);
if (user) {
res.json({
data: {
user: {
id: user.id,
username: user.username,
email: user.email,
firstName: user.firstName,
lastName: user.lastName
},
token: 'token-' + user.id,
message: 'Login successful'
},
_links: makeLinks('/api/auth', {
self: '/login',
profile: '/profile/',
logout: '/logout'
}),
_meta: {}
});
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
};
exports.register = (req, res) => {
const { username, password, email, firstName, lastName } = req.body;
if (usersDb.exists(username, email)) {
return res.status(409).json({ error: 'User already exists' });
}
const newUser = usersDb.addUser({ username, password, email, firstName, lastName });
res.json({
data: {
user: {
id: newUser.id,
username,
email,
firstName,
lastName
},
token: 'token-' + newUser.id,
message: 'Register successful'
},
_links: makeLinks('/api/auth', {
self: '/register',
login: '/login',
profile: '/profile/'
}),
_meta: {}
});
};
exports.profile = (req, res) => {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = auth.replace('Bearer ', '');
const id = parseInt(token.replace('token-', ''));
const user = usersDb.findById(id);
if (!user) {
return res.status(401).json({ error: 'Invalid token' });
}
res.json({
data: {
id: user.id,
username: user.username,
email: user.email,
firstName: user.firstName,
lastName: user.lastName
},
_links: makeLinks('/api/auth', {
self: '/profile/',
logout: '/logout'
}),
_meta: {}
});
};
exports.logout = (req, res) => {
res.json({
message: 'Logout successful',
_links: makeLinks('/api/auth', {
self: '/logout',
login: '/login'
}),
_meta: {}
});
};

View File

@@ -1,10 +0,0 @@
const express = require('express');
const router = express.Router();
const ctrl = require('./auth.controller');
router.post('/login', ctrl.login);
router.post('/register', ctrl.register);
router.get('/profile/', ctrl.profile);
router.post('/logout', ctrl.logout);
module.exports = router;

View File

@@ -1,81 +0,0 @@
const axios = require('axios');
const makeLinks = require('../../shared/hateoas');
const path = require('path');
const qs = require('qs');
const { v4: uuidv4 } = require('uuid');
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
exports.generate = async (req, res) => {
const { prompt } = req.query;
if (!prompt) {
return res.status(400).json({ error: 'Prompt parameter is required' });
}
try {
const apiKey = process.env.GIGACHAT_API_KEY;
const tokenResp = await axios.post(
'https://ngw.devices.sberbank.ru:9443/api/v2/oauth',
{
'scope':' GIGACHAT_API_PERS',
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'Authorization': `Basic ${apiKey}`,
'RqUID':'6f0b1291-c7f3-43c6-bb2e-9f3efb2dc98e'
},
}
);
const accessToken = tokenResp.data.access_token;
const chatResp = await axios.post(
'https://gigachat.devices.sberbank.ru/api/v1/chat/completions',
{
model: "GigaChat",
messages: [
{ role: "system", content: "Ты — Василий Кандинский" },
{ role: "user", content: prompt }
],
stream: false,
function_call: 'auto'
},
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'RqUID': uuidv4(),
}
}
);
const content = chatResp.data.choices[0].message.content;
const match = content.match(/<img src=\"(.*?)\"/);
if (!match) {
return res.status(500).json({ error: 'No image generated' });
}
const imageId = match[1];
const imageResp = await axios.get(
`https://gigachat.devices.sberbank.ru/api/v1/files/${imageId}/content`,
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'RqUID': uuidv4(),
},
responseType: 'arraybuffer'
}
);
res.set('Content-Type', 'image/jpeg');
res.set('X-HATEOAS', JSON.stringify(makeLinks('/gigachat', { self: '/prompt' })));
res.send(imageResp.data);
} catch (err) {
if (err.response) {
console.error('AI生成图片出错:');
console.error('status:', err.response.status);
console.error('headers:', err.response.headers);
console.error('data:', err.response.data);
console.error('config:', err.config);
} else {
console.error('AI生成图片出错:', err.message);
}
res.status(500).json({ error: err.message });
}
};

View File

@@ -1,7 +0,0 @@
const express = require('express');
const router = express.Router();
const ctrl = require('./image.controller');
router.get('/prompt', ctrl.generate);
module.exports = router;

View File

@@ -1,12 +0,0 @@
const usersDb = require('../../shared/usersDb');
const makeLinks = require('../../shared/hateoas');
exports.list = (req, res) => {
res.json({
data: usersDb.getAll(),
_links: makeLinks('/api/user', {
self: '/list',
}),
_meta: {}
});
};

View File

@@ -1,7 +0,0 @@
const express = require('express');
const router = express.Router();
const ctrl = require('./user.controller');
router.get('/list', ctrl.list);
module.exports = router;

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
{
"name": "back-new",
"version": "1.0.0",
"description": "非Python实现的后端兼容前端接口",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "jest"
},
"dependencies": {
"axios": "^1.10.0",
"cors": "^2.8.5",
"dotenv": "^17.0.0",
"express": "^4.21.2",
"qs": "^6.14.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"jest": "^30.0.3"
}
}

Some files were not shown because too many files have changed in this diff Show More