import 'package:shelf/shelf.dart'; import 'package:shelf_router/shelf_router.dart'; import 'package:bcrypt/bcrypt.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:dotenv/dotenv.dart'; import '../database/database_provider.dart'; import '../middleware/auth_middleware.dart'; import '../middleware/request_logger.dart'; import 'dart:convert'; import 'dart:io'; class AuthRoutes { final DatabaseProvider database; AuthRoutes(this.database); Router get routes { final router = Router(); router.post('/login', _login); router.post('/reg', _register); router.get('/watch', AuthMiddleware(_watch).call); router.get('/api_info', _apiInfo); return router; } Future _login(Request request) async { final body = await request.readAsString(); final data = jsonDecode(body); final login = data['login']; final password = data['password']; final user = await database.findUserByLogin(login); final stopwatch = Stopwatch()..start(); if (user == null || !BCrypt.checkpw(password, user.pwdHash)) { await database.createLog(login, 'Failed login attempt'); stopwatch.stop(); final response = Response(401, body: jsonEncode({'error': 'Invalid credentials'}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'POST', url: '/login', status: 401, duration: stopwatch.elapsed, body: body, responseHeaders: response.headers); return response; } // Генерация JWT токена final dotenv = DotEnv(); final secret = dotenv['JWT_SECRET'] ?? ''; final jwt = JWT( {'user_id': user.id, 'login': user.login}, issuer: 'family_safety_tracker' ); final token = jwt.sign(SecretKey(secret)); await database.createLog(login, 'Successful login'); stopwatch.stop(); final response = Response(200, body: jsonEncode({'token': token}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'POST', url: '/login', status: 200, duration: stopwatch.elapsed, body: body, responseHeaders: response.headers); return response; } Future _register(Request request) async { final stopwatch = Stopwatch()..start(); final body = await request.readAsString(); final data = jsonDecode(body); final login = data['login']; final password = data['password']; await database.createUser(login, password); await database.createLog(login, 'User registration'); stopwatch.stop(); final response = Response(201, body: jsonEncode({'message': 'User registered'}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'POST', url: '/reg', status: 201, duration: stopwatch.elapsed, body: body, responseHeaders: response.headers); return response; } Future _watch(Request request, String login) async { final stopwatch = Stopwatch()..start(); final uniqueId = request.url.queryParameters['share_id']; if (uniqueId == null) { stopwatch.stop(); final response = Response(404, body: jsonEncode({'error': 'Share link not found'}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'GET', url: '/watch', status: 404, duration: stopwatch.elapsed, responseHeaders: response.headers); return response; } final geoId = database.getGeoIdByShareId(uniqueId); if (geoId == null) { await database.createLog(login, 'Accessed invalid share link'); stopwatch.stop(); final response = Response(404, body: jsonEncode({'error': 'Share link not found'}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'GET', url: '/watch', status: 404, duration: stopwatch.elapsed, responseHeaders: response.headers); return response; } final position = await database.getPositionById(geoId); if (position == null) { await database.createLog(login, 'Accessed share link - no position'); stopwatch.stop(); final response = Response(404, body: jsonEncode({'error': 'No position available'}), headers: {'Content-Type': 'application/json'}); logRequest(method: 'GET', url: '/watch', status: 404, duration: stopwatch.elapsed, responseHeaders: response.headers); return response; } await database.createLog(login, 'Accessed share link'); stopwatch.stop(); final response = Response(200, body: jsonEncode({ 'x': position.xValue, 'y': position.yValue, 'last_update': position.lastUpdate.toIso8601String(), 'expires_at': position.expiresAt.toIso8601String(), }), headers: {'Content-Type': 'application/json'}); logRequest(method: 'GET', url: '/watch', status: 200, duration: stopwatch.elapsed, responseHeaders: response.headers); return response; } Future _apiInfo(Request request) async { final swaggerDir = Directory('web/swagger'); final swaggerFile = File('${swaggerDir.path}/index.html'); final html = await swaggerFile.readAsString(); final response = Response(200, body: html, headers: {'Content-Type': 'text/html'}); return response; } }