feat: add Docker support, refactor DB layer, update API responses
This commit is contained in:
@@ -1,18 +1,319 @@
|
||||
A server app built using [Shelf](https://pub.dev/packages/shelf),
|
||||
# Family Safety Tracker API
|
||||
|
||||
Это новый проект.
|
||||
Давай добавим описание проекта -
|
||||
Сервер - приложения для реализации возможности делиться геопозицией с другим человеком.
|
||||
База данных (в данном случае это будет postges) с отметками о геопозиции человека
|
||||
Какие таблицы будут в базе данных -
|
||||
Пользователи - ID, login, pwd
|
||||
Geoposition - id, x value, y value, datetime, lifetime
|
||||
Logs - username, action, datetime
|
||||
REST API для обмена геолокацией между членами семьи.
|
||||
|
||||
Основа приложения - это REST API.
|
||||
Вот какие методы нужны.
|
||||
/login - авторизация.
|
||||
/user - CRUD пользователей.
|
||||
/geo - POST - создание позиции, UPDATE - обновление позиции. (при создании указывается время жизни, после которого данные будут удалены из базы)
|
||||
/watch?{unique id} - возращает геопозицию+время последней отметки + оставшееся время жизни отметки
|
||||
/share - метод который создает одноразовую ссылку (/watch?{unique id} ) - по которой доступны данные о геопозиции. В памяти приложения создается связь между geo из таблицы и {unique id}
|
||||
## Аутентификация
|
||||
|
||||
Сервис использует JWT токены для авторизации.
|
||||
|
||||
### JWT Токен
|
||||
|
||||
При успешном `POST /login` возвращается JWT токен:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {...},
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
**Структура токена:**
|
||||
- `issuer: "family_safety_tracker"`
|
||||
- `payload: {"user_id": N, "login": "user"}`
|
||||
|
||||
### Авторизация
|
||||
|
||||
Защищённые эндпоинты (`/watch`, `/geo`, `/share`) требуют JWT в заголовке:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <jwt_token>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Таблица эндпоинтов
|
||||
|
||||
| Метод | URL | Описание | Защита |
|
||||
|-------|-----|----------|--------|
|
||||
| `POST` | `/login` | Вход в систему | — |
|
||||
| `POST` | `/reg` | Регистрация нового пользователя | — |
|
||||
| `GET` | `/watch?unique_id=...` | Получить последнюю позицию по share-ссылке | ✅ |
|
||||
| `PUT` | `/geo?id=N` | Обновить геопозицию | ✅ |
|
||||
| `POST` | `/share` | Создать share-ссылку | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Эндпоинты
|
||||
|
||||
### `POST /login` — Вход
|
||||
|
||||
#### Запрос
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9090/reg \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"login": "ivan", "password": "secret123"}'
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
$body = @{ login = "ivan"; password = "secret123" } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri http://localhost:9090/reg -Method Post -Body $body -ContentType "application/json"
|
||||
```
|
||||
|
||||
#### Успешный ответ (200)
|
||||
|
||||
```json
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
```
|
||||
|
||||
#### Ошибки
|
||||
|
||||
| Код | Сообщение |
|
||||
|-----|-----------|
|
||||
| `401` | `Invalid credentials` — неверный логин или пароль |
|
||||
|
||||
---
|
||||
|
||||
### `POST /reg` — Регистрация
|
||||
|
||||
#### Запрос
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
|
||||
{"login": "maria", "password": "newpass"}
|
||||
```
|
||||
|
||||
#### Успешный ответ (201)
|
||||
|
||||
Ничего не возвращается.
|
||||
|
||||
---
|
||||
|
||||
### `GET /watch?unique_id=...` — Получить позицию
|
||||
|
||||
#### Запрос
|
||||
|
||||
```http
|
||||
Authorization: Bearer <jwt_token>
|
||||
|
||||
GET /watch?unique_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
```
|
||||
|
||||
#### Успешный ответ (200)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 42,
|
||||
"x": 55.7558,
|
||||
"y": 37.6173,
|
||||
"created_at": "2024-01-01T12:00:00Z",
|
||||
"expires_at": "2024-01-01T12:05:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Ошибки
|
||||
|
||||
| Код | Сообщение |
|
||||
|-----|-----------|
|
||||
| `404` | `Share link not found` — неверный `unique_id` |
|
||||
| `404` | `No position available` — share-ссылка создана, но позиция не была добавлена |
|
||||
|
||||
---
|
||||
|
||||
### `PUT /geo?id=N` — Обновить позицию
|
||||
|
||||
#### Запрос
|
||||
|
||||
```http
|
||||
Authorization: Bearer <jwt_token>
|
||||
Content-Type: application/json
|
||||
|
||||
PUT /geo?id=1
|
||||
|
||||
{
|
||||
"x": 55.7558,
|
||||
"y": 37.6173
|
||||
}
|
||||
```
|
||||
|
||||
#### Успешный ответ (200)
|
||||
|
||||
Ничего не возвращается.
|
||||
|
||||
---
|
||||
|
||||
### `POST /share` — Создать share-ссылку
|
||||
|
||||
#### Запрос
|
||||
|
||||
```http
|
||||
Authorization: Bearer <jwt_token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"x": 55.7558,
|
||||
"y": 37.6173
|
||||
}
|
||||
```
|
||||
|
||||
#### Успешный ответ (201)
|
||||
|
||||
```json
|
||||
{
|
||||
"geo_id": 5,
|
||||
"share_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Примеры запросов
|
||||
|
||||
### Регистрация
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/reg \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"login": "ivan", "password": "secret123"}'
|
||||
```
|
||||
|
||||
### Вход
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9090/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"login": "ivan", "password": "secret123"}'
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
$body = @{ login = "ivan"; password = "secret123" } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri http://localhost:9090/login -Method Post -Body $body -ContentType "application/json"
|
||||
```
|
||||
|
||||
### Создание share-ссылки
|
||||
|
||||
```bash
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
|
||||
curl -X POST http://localhost:9090/share \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"x": 55.7558, "y": 37.6173}'
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
$body = @{ x = 55.7558; y = 37.6173 } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri http://localhost:9090/share -Method Post -Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } -Body $body
|
||||
```
|
||||
|
||||
### Получение позиции
|
||||
|
||||
```bash
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
SHARE_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
|
||||
curl "http://localhost:9090/watch?unique_id=$SHARE_ID" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
$shareId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
Invoke-RestMethod -Uri "http://localhost:9090/watch?unique_id=$shareId" -Method Get -Headers @{ Authorization = "Bearer $token" }
|
||||
```
|
||||
|
||||
### Обновление позиции
|
||||
|
||||
```bash
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
|
||||
curl -X PUT "http://localhost:9090/geo?id=1" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"x": 55.7558, "y": 37.6173}'
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
$body = @{ x = 55.7558; y = 37.6173 } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri "http://localhost:9090/geo?id=1" -Method Put -Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } -Body $body
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Технические детали
|
||||
|
||||
### Сервер
|
||||
- Порт: `9090` (изменяется через `PORT`)
|
||||
|
||||
### Технологии
|
||||
- **Dart** (SDK `^3.10.1`)
|
||||
- **Shelf** + **Shelf Router** — HTTP фреймворк
|
||||
- **PostgreSQL** — база данных
|
||||
- **bcrypt** — хэширование паролей
|
||||
- **dart_jsonwebtoken** — JWT-токены
|
||||
- **dotenv** — переменные окружения
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
| Переменная | Описание |
|
||||
|------------|----------|
|
||||
| `PORT` | Порт сервера (по умолчанию 8080) |
|
||||
| `POSTGRES_HOST` | Хост PostgreSQL |
|
||||
| `POSTGRES_DB` | База данных |
|
||||
| `POSTGRES_USER` | Пользователь |
|
||||
| `POSTGRES_PASSWORD` | Пароль |
|
||||
| `JWT_SECRET` | Секрет для JWT-токенов |
|
||||
|
||||
### Автоматическая миграция
|
||||
|
||||
При запуске сервера таблицы создаются автоматически:
|
||||
- `users` — пользователи
|
||||
- `geopositions` — геопозиции
|
||||
- `logs` — логи
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
bin/
|
||||
├── server.dart # Точка входа
|
||||
├── routes/
|
||||
│ ├── auth_routes.dart # Маршруты авторизации
|
||||
│ └── geo_routes.dart # Маршруты геолокации
|
||||
├── repositories/
|
||||
│ ├── user_repo.dart
|
||||
│ └── geoposition_repo.dart
|
||||
├── models/
|
||||
│ ├── user.dart
|
||||
│ ├── geoposition.dart
|
||||
│ └── log.dart
|
||||
└── database/
|
||||
└── database_provider.dart
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
dart pub get
|
||||
dart run bin/server.dart
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user