Веб-приложение блога с REST API, черновиками, поиском, пагинацией и Swagger-документацией. Реализовано на Flask + SQLite + Bootstrap 5.
# 1. Создать и активировать виртуальное окружение
python -m venv venv
source venv/bin/activate # Linux / macOS
# venv\Scripts\activate # Windows
# 2. Установить зависимости
make install
# 3. Создать / мигрировать базу данных
make init-db
# 4. Запустить приложение
make run
# → http://127.0.0.1:5000
.
├── main.py # Flask-приложение: веб-маршруты, RSS, Sitemap, CSV, API Docs
├── api.py # Blueprint REST API (/api/*)
├── database.py # Общие хелперы БД (get_post, get_published_posts, …)
├── config.py # Константы: SECRET_KEY, API_KEY, DATABASE, POSTS_PER_PAGE
├── init_db.py # Скрипт создания / миграции БД
├── schema.sql # Целевая схема таблицы posts
├── requirements.txt # Зависимости Python
├── Makefile # Команды: install, init-db, run
└── templates/
├── base.html # Базовый шаблон (Bootstrap 5, навбар)
├── index.html # Главная: список постов, поиск, пагинация, сортировка
├── post.html # Страница поста + модал подтверждения удаления
├── create.html # Форма создания поста
├── edit.html # Форма редактирования поста
├── drafts.html # Черновики с кнопкой быстрой публикации
├── stats.html # Статистика блога
└── api_docs.html# Swagger UI
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
published INTEGER NOT NULL DEFAULT 1, -- 1 = опубликован, 0 = черновик
created TEXT NOT NULL DEFAULT (datetime('now'))
);
| Маршрут |
Описание |
GET / |
Список опубликованных постов, поиск, сортировка, пагинация |
GET /<id> |
Страница поста с подсветкой кода |
GET /create |
Форма создания поста |
GET /<id>/edit |
Форма редактирования |
POST /<id>/delete |
Удаление поста |
GET /drafts |
Список черновиков |
POST /<id>/publish |
Опубликовать черновик |
GET /stats |
Статистика (всего / опубликовано / черновиков / даты) |
GET /export/csv |
Скачать все посты в CSV |
GET /rss |
RSS-лента последних 10 постов |
GET /sitemap.xml |
Sitemap для поисковиков |
GET /api/docs |
Swagger UI |
На главной странице доступны параметры URL:
| Параметр |
Значения |
Описание |
q |
любая строка |
Поиск по заголовку и содержимому |
sort |
created_desc / created_asc / title_asc / title_desc |
Порядок сортировки |
page |
целое число |
Номер страницы |
Пример: /?q=flask&sort=title_asc&page=2
Все эндпоинты требуют заголовок X-API-Key.
API-ключ по умолчанию (dev): changeme-secret-api-key
| Метод |
Маршрут |
Описание |
Код ответа |
| GET |
/api/posts |
Список постов |
200 |
| GET |
/api/posts/<id> |
Получить пост |
200 / 404 |
| POST |
/api/posts |
Создать пост |
201 |
| PUT |
/api/posts/<id> |
Обновить пост |
200 / 404 |
| DELETE |
/api/posts/<id> |
Удалить пост |
204 / 404 |
| Параметр |
По умолчанию |
Описание |
page |
1 |
Номер страницы |
per_page |
10 (max 100) |
Постов на страницу |
q |
— |
Поиск по заголовку и содержимому |
sort |
created_desc |
Сортировка: created_desc/asc, title_asc/desc |
published |
1 |
Фильтр: 1, 0, all |
{
"posts": [
{
"id": 1,
"title": "Hello World",
"content": "My first post",
"published": 1,
"created": "2026-03-19 12:00:00"
}
],
"page": 1,
"per_page": 10,
"total": 42,
"total_pages": 5
}
API_KEY="changeme-secret-api-key"
BASE="http://localhost:5000/api"
# Список постов
curl "$BASE/posts" -H "X-API-Key: $API_KEY"
# Поиск с сортировкой и пагинацией
curl "$BASE/posts?q=flask&sort=created_desc&page=1&per_page=5" \
-H "X-API-Key: $API_KEY"
# Только черновики
curl "$BASE/posts?published=0" -H "X-API-Key: $API_KEY"
# Создать пост
curl -X POST "$BASE/posts" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "My post", "content": "Body text", "published": 1}'
# Обновить пост
curl -X PUT "$BASE/posts/1" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Updated title", "published": 0}'
# Удалить пост
curl -X DELETE "$BASE/posts/1" -H "X-API-Key: $API_KEY"
# Без ключа → 401 Unauthorized
curl "$BASE/posts"
| Код |
Значение |
| 200 |
OK |
| 201 |
Created (POST) |
| 204 |
No Content (DELETE) |
| 400 |
Bad Request (не передан title) |
| 401 |
Unauthorized (неверный / отсутствует X-API-Key) |
| 404 |
Not Found |
| 429 |
Too Many Requests (rate limit) |
| Операция |
Лимит |
| GET /api/posts, GET /api/posts/<id> |
60 запросов / час (глобальный) |
| POST /api/posts |
10 запросов / минуту |
| PUT /api/posts/<id> |
10 запросов / минуту |
| DELETE /api/posts/<id> |
10 запросов / минуту |
При превышении лимита возвращается 429 Too Many Requests с заголовком Retry-After.
Все настройки в config.py:
| Переменная |
Значение по умолчанию |
Описание |
SECRET_KEY |
"your secret key" |
Ключ для Flask-сессий и flash-сообщений |
API_KEY |
"changeme-secret-api-key" |
Ключ для REST API |
DATABASE |
"database.db" |
Путь к SQLite-файлу |
POSTS_PER_PAGE |
5 |
Постов на странице на главной |
| Пакет |
Версия |
Назначение |
| Flask |
>=2.3,<3.0 |
Веб-фреймворк |
| flask-limiter |
>=3.5 |
Rate limiting |
| Werkzeug |
>=2.3 |
HTTP-утилиты |