Added jwt authenticaation
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend_splatournament_manager/services/auth_service.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
|
||||
class AuthProvider extends ChangeNotifier {
|
||||
final AuthService _authService = AuthService();
|
||||
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||||
|
||||
bool _isLoggedIn = false;
|
||||
String? _username;
|
||||
@@ -23,6 +26,12 @@ class AuthProvider extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
final user = await _authService.login(username, password);
|
||||
|
||||
final token = user['token'];
|
||||
if (token != null) {
|
||||
await _storage.write(key: 'jwt_token', value: token);
|
||||
}
|
||||
|
||||
_isLoggedIn = true;
|
||||
_username = user['username'];
|
||||
_userId = user['id'];
|
||||
@@ -44,6 +53,12 @@ class AuthProvider extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
final user = await _authService.register(username, password);
|
||||
|
||||
final token = user['token'];
|
||||
if (token != null) {
|
||||
await _storage.write(key: 'jwt_token', value: token);
|
||||
}
|
||||
|
||||
_isLoggedIn = true;
|
||||
_username = user['username'];
|
||||
_userId = user['id'];
|
||||
@@ -58,17 +73,32 @@ class AuthProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void logout() {
|
||||
Future<void> logout() async {
|
||||
_isLoggedIn = false;
|
||||
_username = null;
|
||||
_userId = null;
|
||||
_error = null;
|
||||
await _storage.delete(key: 'jwt_token');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> checkAuthStatus() async {
|
||||
final token = await _storage.read(key: 'jwt_token');
|
||||
if (token != null) {
|
||||
if (JwtDecoder.isExpired(token)) {
|
||||
await logout();
|
||||
} else {
|
||||
Map<String, dynamic> decodedToken = JwtDecoder.decode(token);
|
||||
_isLoggedIn = true;
|
||||
_userId = decodedToken['id'];
|
||||
_username = decodedToken['username'];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,9 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend_splatournament_manager/models/tournament.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../main.dart';
|
||||
import 'package:frontend_splatournament_manager/services/api_client.dart';
|
||||
|
||||
class TournamentProvider extends ChangeNotifier {
|
||||
final String baseUrl = SplatournamentApp.baseUrl;
|
||||
|
||||
List<Tournament> _availableTournaments = [];
|
||||
Future<List<Tournament>>? _initialLoadFuture;
|
||||
@@ -16,7 +13,7 @@ class TournamentProvider extends ChangeNotifier {
|
||||
List<Tournament> get availableTournaments => _availableTournaments;
|
||||
|
||||
Future<List<Tournament>> _fetchTournaments() async {
|
||||
final response = await http.get(Uri.parse('$baseUrl/tournaments'));
|
||||
final response = await ApiClient.get('/tournaments');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception('Failed to load tournaments (${response.statusCode})');
|
||||
}
|
||||
@@ -48,21 +45,15 @@ class TournamentProvider extends ChangeNotifier {
|
||||
DateTime registrationStartDate,
|
||||
DateTime registrationEndDate,
|
||||
) async {
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/tournaments'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
final response = await ApiClient.post(
|
||||
'/tournaments',
|
||||
{
|
||||
'name': name,
|
||||
'description': description,
|
||||
'maxTeamAmount': maxTeamAmount,
|
||||
//weird date formatting
|
||||
'registrationStartDate': registrationStartDate.toIso8601String().split(
|
||||
'T',
|
||||
)[0],
|
||||
'registrationEndDate': registrationEndDate.toIso8601String().split(
|
||||
'T',
|
||||
)[0],
|
||||
}),
|
||||
'registrationStartDate': registrationStartDate.toIso8601String().split('T')[0],
|
||||
'registrationEndDate': registrationEndDate.toIso8601String().split('T')[0],
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != HttpStatus.created) {
|
||||
|
||||
45
frontend_splatournament_manager/lib/services/api_client.dart
Normal file
45
frontend_splatournament_manager/lib/services/api_client.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:frontend_splatournament_manager/main.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class ApiClient {
|
||||
static const String baseUrl = SplatournamentApp.baseUrl;
|
||||
static const _storage = FlutterSecureStorage();
|
||||
|
||||
static Future<Map<String, String>> _getHeaders() async {
|
||||
final token = await _storage.read(key: 'jwt_token');
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
if (token != null) 'Authorization': 'Bearer $token',
|
||||
};
|
||||
}
|
||||
|
||||
static Future<http.Response> get(String endpoint) async {
|
||||
final headers = await _getHeaders();
|
||||
return await http.get(Uri.parse('$baseUrl$endpoint'), headers: headers);
|
||||
}
|
||||
|
||||
static Future<http.Response> post(String endpoint, Map<String, dynamic> body) async {
|
||||
final headers = await _getHeaders();
|
||||
return await http.post(
|
||||
Uri.parse('$baseUrl$endpoint'),
|
||||
headers: headers,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<http.Response> put(String endpoint, Map<String, dynamic> body) async {
|
||||
final headers = await _getHeaders();
|
||||
return await http.put(
|
||||
Uri.parse('$baseUrl$endpoint'),
|
||||
headers: headers,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<http.Response> delete(String endpoint) async {
|
||||
final headers = await _getHeaders();
|
||||
return await http.delete(Uri.parse('$baseUrl$endpoint'), headers: headers);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:frontend_splatournament_manager/main.dart';
|
||||
import 'package:frontend_splatournament_manager/models/team.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:frontend_splatournament_manager/services/api_client.dart';
|
||||
|
||||
class TeamService {
|
||||
final String baseUrl = SplatournamentApp.baseUrl;
|
||||
|
||||
Future<List<Team>> getAllTeams() async {
|
||||
final response = await http.get(Uri.parse('$baseUrl/teams'));
|
||||
final response = await ApiClient.get('/teams');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception('Failed to load teams (${response.statusCode})');
|
||||
}
|
||||
@@ -18,7 +16,7 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<Team> getTeamById(int id) async {
|
||||
final response = await http.get(Uri.parse('$baseUrl/teams/$id'));
|
||||
final response = await ApiClient.get('/teams/$id');
|
||||
if (response.statusCode == HttpStatus.notFound) {
|
||||
throw Exception('Team not found');
|
||||
}
|
||||
@@ -33,10 +31,9 @@ class TeamService {
|
||||
required String tag,
|
||||
String description = '',
|
||||
}) async {
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/teams'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({'name': name, 'tag': tag, 'description': description}),
|
||||
final response = await ApiClient.post(
|
||||
'/teams',
|
||||
{'name': name, 'tag': tag, 'description': description},
|
||||
);
|
||||
if (response.statusCode != HttpStatus.created) {
|
||||
final body = json.decode(response.body);
|
||||
@@ -51,14 +48,13 @@ class TeamService {
|
||||
String? tag,
|
||||
String? description,
|
||||
}) async {
|
||||
final response = await http.put(
|
||||
Uri.parse('$baseUrl/teams/$id'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({
|
||||
'name': ?name,
|
||||
'tag': ?tag,
|
||||
'description': ?description,
|
||||
}),
|
||||
final response = await ApiClient.put(
|
||||
'/teams/$id',
|
||||
{
|
||||
if (name != null) 'name': name,
|
||||
if (tag != null) 'tag': tag,
|
||||
if (description != null) 'description': description,
|
||||
},
|
||||
);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
final body = json.decode(response.body);
|
||||
@@ -67,7 +63,7 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<void> deleteTeam(int id) async {
|
||||
final response = await http.delete(Uri.parse('$baseUrl/teams/$id'));
|
||||
final response = await ApiClient.delete('/teams/$id');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
final body = json.decode(response.body);
|
||||
throw Exception(body['error'] ?? 'Failed to delete team');
|
||||
@@ -75,9 +71,7 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<List<Team>> getTeamsByTournament(int tournamentId) async {
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/tournaments/$tournamentId/teams'),
|
||||
);
|
||||
final response = await ApiClient.get('/tournaments/$tournamentId/teams');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception(
|
||||
'Failed to load teams for tournament (${response.statusCode})',
|
||||
@@ -88,10 +82,9 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<void> registerTeamForTournament(int tournamentId, int teamId) async {
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/tournaments/$tournamentId/teams'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode({'teamId': teamId}),
|
||||
final response = await ApiClient.post(
|
||||
'/tournaments/$tournamentId/teams',
|
||||
{'teamId': teamId},
|
||||
);
|
||||
if (response.statusCode == 409) {
|
||||
throw Exception('Team is already registered for this tournament');
|
||||
@@ -103,9 +96,7 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<void> removeTeamFromTournament(int tournamentId, int teamId) async {
|
||||
final response = await http.delete(
|
||||
Uri.parse('$baseUrl/tournaments/$tournamentId/teams/$teamId'),
|
||||
);
|
||||
final response = await ApiClient.delete('/tournaments/$tournamentId/teams/$teamId');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
final body = json.decode(response.body);
|
||||
throw Exception(body['error'] ?? 'Failed to remove team from tournament');
|
||||
@@ -113,9 +104,7 @@ class TeamService {
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getTournamentsByTeam(int teamId) async {
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/teams/$teamId/tournaments'),
|
||||
);
|
||||
final response = await ApiClient.get('/teams/$teamId/tournaments');
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception(
|
||||
'Failed to load tournaments for team (${response.statusCode})',
|
||||
|
||||
@@ -38,6 +38,8 @@ dependencies:
|
||||
provider: ^6.1.5+1
|
||||
go_router: ^17.1.0
|
||||
intl: ^0.20.2
|
||||
flutter_secure_storage: ^10.0.0
|
||||
jwt_decoder: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user