Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.env
.env.local
.env.*.local

node_modules/
package-lock.json

npm-debug.log*
yarn-debug.log*
yarn-error.log*

.DS_Store
.AppleDouble
.LSOverride
._*

Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/

*~
.fuse_hidden*

.vscode/

.vscode/.history

.idea/
*.swp
*.swo
*~
.project
.classpath
.settings/

*.log
logs/

tmp/
temp/
.cache/

coverage/
.nyc_output/
*.lcov

*.pid
*.seed
*.pid.lock

dist/
build/
out/

*.key
*.pem
*.crt
*.p12
*.pfx

*password*
*secret*
*token*
76 changes: 25 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,35 @@
# Безопасность веб-приложений. Лабораторка №2
# Лабораторная работа №2

## Схема сдачи
## О лабораторной работе

1. Получить задание
2. Сделать форк данного репозитория
3. Выполнить задание согласно полученному варианту
4. Сделать PR (pull request) в данный репозиторий
6. Исправить замечания после code review
7. Получить approve
8. Прийти на занятие и защитить работу
Это лабораторная работа №2 по дисциплине «Безопасность веб-приложений». По варианту 2 реализован веб-аналог «Прибывалки» для электричек: поиск станций, просмотр расписания по станции, выбор любимых станций, просмотр маршрутов проходящих между станций и через станции и адаптация интерфейса под мобильные устройства.

Что нужно проявить в работе:
- умение разработать завершенное целое веб-приложение, с клиентской и серверной частями (допустимы открытые АПИ)
- навыки верстки на html в объеме 200-300 тегов
- навыки применения css для лейаута и стилизации, желательно с адаптацией к мобилке
- использование jQuery или аналогичных JS-фреймворков
- динамическая подгрузка контента
- динамическое изменение DOM и CSSOM

Если у вас своя идея по заданию, то расскажите, обсудим и подкорректирую.

## Вариант 1. Расписания

Сделать аналог раздела https://ssau.ru/rasp?groupId=531030143

Какие нужны возможности:
- справочники групп, табличные данные по расписаниям добывать с настоящего сайта на серверной стороне приложения
- в клиентскую часть подгружать эти сведения динамически по JSON-API
- обеспечить возможность смотреть расписания в разрезе группы или препода
- обеспечить возможность выбора учебной недели (по умолчанию выбирается автоматически)

## Вариант 2. Аналог Прибывалки для электричек

Сделать веб-версию Прибывалки, только для электричек

Какие нужны возможности:
- находить желаемую ЖД-станцию поиском по названию и по карте
- отображать расписания всех проходящих поездов через выбранную станцию
- отображать расписания для поездов между двумя станциями
- работа через АПИ Яндекс.Расписаний https://yandex.ru/dev/rasp/doc/ru/ (доступ получите сами)
- хорошая работа в условиях экрана смартфона
- бонус: функция "любимых остановок"

## Вариант 3. Прогноз погоды

Сделать одностраничный сайт с картой, на которой можно выбрать населенный пункт и получить прогноз погоды на несколько дней по нему.

Какие нужны возможности:
- увидеть на карте точки с населенными пунктами. Координаты населенных пунктов взять из https://tochno.st/datasets/allsettlements - но все 150 тысяч не нужно, выберите 1 тысячу с самым большим населением.
- при нажатии на точку получить всплывающее окошко с графиками изменения температуры, осадков, силы ветра. API для прогнозов возьмите с https://projecteol.ru/ru/ с соблюдением правил.
- графики рисовать каким-нибудь приличным компонентом, например, https://www.chartjs.org/
- находить населенный пункт по названию
- можете реализовать с собственным серверным компонентом или придумать, как обойтись без него
## Что реализовано

- Поиск железнодорожных станций по названию и выбор станций на карте.
- Просмотр расписания всех проходящих через выбранную станцию поездов.
- Поиск маршрутов между станций и через станции.
- Избранные станции с сохранением в localStorage.
- Адаптивный интерфейс для смартфонов и десктопа.

## Технологии

- Frontend: React, Vite, React Router, React Leaflet, CSS.
- Backend: Node.js, Express, Axios, CORS, dotenv.
- Внешний источник данных: API Яндекс.Расписаний.
- Пакетный менеджер: npm.

## Как запустить проект

1. Установите зависимости для backend: `npm install` в папке backend.
2. Создайте файл `backend/.env` и добавьте ключ: `YANDEX_RASP_API_KEY=ваш_ключ`. При необходимости добавьте `PORT=5000`.
3. Запустите backend: `npm run dev`.
4. Установите зависимости для frontend: `npm install` в папке frontend.
5. Запустите frontend: `npm run dev`.
6. Откройте адрес из вывода Vite в браузере (обычно `http://localhost:5173`).

## Структура приложения

- Вкладка «По станции»: загрузка расписания для выбранной станции.
- Вкладка «Маршрут»: электрички между станциями отправления и прибытия/целиком проходящие через этот маршрут.
- Backend API: `/api/search-station`, `/api/station-schedule`, `/api/train-route`.
19 changes: 19 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "train-arrival-backend",
"version": "1.0.0",
"description": "Backend for train schedule app",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"axios": "^1.6.0",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не совсем рекомендую использовать axios

https://habr.com/ru/companies/first/articles/1017244/

"cors": "^2.8.5",
"dotenv": "^16.4.0",
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.1.14"
}
}
16 changes: 16 additions & 0 deletions backend/routes/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const express = require('express');
const router = express.Router();
const { getRoutes } = require('../services/yandexApi');

router.get('/', async (req, res) => {
try {
const from = req.query.from;
const to = req.query.to;
const routes = await getRoutes(from, to);
res.json(routes);
} catch (error) {
res.status(500).json({ error: 'Ошибка получения маршрута' });
}
});

module.exports = router;
21 changes: 21 additions & 0 deletions backend/routes/schedule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const express = require('express');
const router = express.Router();
const { getSchedule } = require('../services/yandexApi');
const { formatSchedule } = require('../utils/formatters');

router.get('/', async (req, res) => {
try {
const stationCode = req.query.station;
if (!stationCode) {
return res.status(400).json({ error: 'Параметр station обязателен' });
}

const schedule = await getSchedule(stationCode);
const formatted = formatSchedule(schedule);
res.json(formatted);
} catch (error) {
res.status(500).json({ error: error.message || 'Ошибка получения расписания' });
}
});

module.exports = router;
36 changes: 36 additions & 0 deletions backend/routes/stations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const express = require('express');
const router = express.Router();
const { searchStations, getStationsInBounds } = require('../services/yandexApi');

router.get('/', async (req, res) => {
try {
const query = req.query.query || '';
const zoom = Number(req.query.zoom);
const minLat = Number(req.query.minLat);
const minLng = Number(req.query.minLng);
const maxLat = Number(req.query.maxLat);
const maxLng = Number(req.query.maxLng);

const hasBounds =
Number.isFinite(minLat) &&
Number.isFinite(minLng) &&
Number.isFinite(maxLat) &&
Number.isFinite(maxLng);

if (hasBounds) {
if (!Number.isFinite(zoom) || zoom < 9) {
return res.json([]);
}
const stations = await getStationsInBounds({ minLat, minLng, maxLat, maxLng });
return res.json(stations);
}

const stations = await searchStations(query);
return res.json(stations);
} catch (error) {
console.error('Ошибка поиска станции:', error.message);
res.status(500).json({ error: error.message });
}
});

module.exports = router;
27 changes: 27 additions & 0 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require('dotenv').config();
const express = require('express');
const cors = require('cors');


const stationRoutes = require('./routes/stations');
const scheduleRoutes = require('./routes/schedule');
const routeRoutes = require('./routes/routes');

const app = express();

app.use(cors());
app.use(express.json());

app.use('/api/search-station', stationRoutes);
app.use('/api/station-schedule', scheduleRoutes);
app.use('/api/train-route', routeRoutes);

app.get('/', (req, res) => {
res.send('API Прибывалка.Электрички работает');
});

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
console.log(`Server started on port ${PORT}`);
});
Loading