- Introduced a new `logSafe` function for consistent logging with sensitivity handling. - Replaced direct logger calls with `logSafe` in `api_service.dart`, `app_initializer.dart`, `auth_service.dart`, `permission_service.dart`, and `my_image_compressor.dart`. - Enhanced error handling and logging in various service methods to capture exceptions and provide more context. - Updated image compression logging to include quality and size metrics. - Improved app initialization logging to capture success and error states. - Ensured sensitive information is not logged directly.
279 lines
9.5 KiB
Dart
279 lines
9.5 KiB
Dart
import 'dart:convert';
|
|
import 'package:get/get.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:marco/controller/permission_controller.dart';
|
|
import 'package:marco/controller/project_controller.dart';
|
|
import 'package:marco/helpers/services/api_endpoints.dart';
|
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
|
import 'package:marco/helpers/services/app_logger.dart';
|
|
|
|
class AuthService {
|
|
static const String _baseUrl = ApiEndpoints.baseUrl;
|
|
static const Map<String, String> _headers = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
|
|
static bool isLoggedIn = false;
|
|
|
|
/// Login with email and password
|
|
static Future<Map<String, String>?> loginUser(Map<String, dynamic> data) async {
|
|
try {
|
|
logSafe("Attempting login...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/login-mobile"),
|
|
headers: _headers,
|
|
body: jsonEncode(data),
|
|
);
|
|
|
|
final responseData = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && responseData['data'] != null) {
|
|
await _handleLoginSuccess(responseData['data']);
|
|
return null;
|
|
} else if (response.statusCode == 401) {
|
|
logSafe("Invalid login credentials.", level: LogLevel.warning);
|
|
return {"password": "Invalid email or password"};
|
|
} else {
|
|
logSafe("Login error: ${responseData['message']}", level: LogLevel.warning);
|
|
return {"error": responseData['message'] ?? "Unexpected error occurred"};
|
|
}
|
|
} catch (e, stacktrace) {
|
|
logSafe("Login exception", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Refresh JWT token
|
|
static Future<bool> refreshToken() async {
|
|
final accessToken = await LocalStorage.getJwtToken();
|
|
final refreshToken = await LocalStorage.getRefreshToken();
|
|
|
|
if (accessToken == null || refreshToken == null || accessToken.isEmpty || refreshToken.isEmpty) {
|
|
logSafe("Missing access or refresh token.", level: LogLevel.warning);
|
|
return false;
|
|
}
|
|
|
|
final requestBody = {
|
|
"token": accessToken,
|
|
"refreshToken": refreshToken,
|
|
};
|
|
|
|
try {
|
|
logSafe("Refreshing token...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/refresh-token"),
|
|
headers: _headers,
|
|
body: jsonEncode(requestBody),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) {
|
|
await LocalStorage.setJwtToken(data['data']['token']);
|
|
await LocalStorage.setRefreshToken(data['data']['refreshToken']);
|
|
await LocalStorage.setLoggedInUser(true);
|
|
logSafe("Token refreshed successfully.");
|
|
return true;
|
|
} else {
|
|
logSafe("Refresh token failed: ${data['message']}", level: LogLevel.warning);
|
|
return false;
|
|
}
|
|
} catch (e, stacktrace) {
|
|
logSafe("Token refresh exception", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Forgot password
|
|
static Future<Map<String, String>?> forgotPassword(String email) async {
|
|
try {
|
|
logSafe("Forgot password requested.");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/forgot-password"),
|
|
headers: _headers,
|
|
body: jsonEncode({"email": email}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
|
return {"error": data['message'] ?? "Failed to send reset link."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Forgot password error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Request demo
|
|
static Future<Map<String, String>?> requestDemo(Map<String, dynamic> demoData) async {
|
|
try {
|
|
logSafe("Submitting demo request...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/market/inquiry"),
|
|
headers: _headers,
|
|
body: jsonEncode(demoData),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
|
return {"error": data['message'] ?? "Failed to submit demo request."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Request demo error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Get list of industries
|
|
static Future<List<Map<String, dynamic>>?> getIndustries() async {
|
|
try {
|
|
logSafe("Fetching industries list...");
|
|
final response = await http.get(
|
|
Uri.parse("$_baseUrl/market/industries"),
|
|
headers: _headers,
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) {
|
|
return List<Map<String, dynamic>>.from(data['data']);
|
|
}
|
|
return null;
|
|
} catch (e, stacktrace) {
|
|
logSafe("Get industries error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Generate MPIN
|
|
static Future<Map<String, String>?> generateMpin({
|
|
required String employeeId,
|
|
required String mpin,
|
|
}) async {
|
|
final token = await LocalStorage.getJwtToken();
|
|
|
|
try {
|
|
logSafe("Generating MPIN...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/generate-mpin"),
|
|
headers: {
|
|
..._headers,
|
|
if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token',
|
|
},
|
|
body: jsonEncode({"employeeId": employeeId, "mpin": mpin}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
|
return {"error": data['message'] ?? "Failed to generate MPIN."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Generate MPIN error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Verify MPIN
|
|
static Future<Map<String, String>?> verifyMpin({
|
|
required String mpin,
|
|
required String mpinToken,
|
|
}) async {
|
|
final employeeInfo = LocalStorage.getEmployeeInfo();
|
|
if (employeeInfo == null) return {"error": "Employee info not found."};
|
|
|
|
final token = await LocalStorage.getJwtToken();
|
|
|
|
try {
|
|
logSafe("Verifying MPIN...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/login-mpin"),
|
|
headers: {
|
|
..._headers,
|
|
if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token',
|
|
},
|
|
body: jsonEncode({
|
|
"employeeId": employeeInfo.id,
|
|
"mpin": mpin,
|
|
"mpinToken": mpinToken,
|
|
}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
|
return {"error": data['message'] ?? "MPIN verification failed."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Verify MPIN error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Generate OTP
|
|
static Future<Map<String, String>?> generateOtp(String email) async {
|
|
try {
|
|
logSafe("Generating OTP for email...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/send-otp"),
|
|
headers: _headers,
|
|
body: jsonEncode({"email": email}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
|
return {"error": data['message'] ?? "Failed to generate OTP."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Generate OTP error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Verify OTP and login
|
|
static Future<Map<String, String>?> verifyOtp({
|
|
required String email,
|
|
required String otp,
|
|
}) async {
|
|
try {
|
|
logSafe("Verifying OTP...");
|
|
final response = await http.post(
|
|
Uri.parse("$_baseUrl/auth/login-otp"),
|
|
headers: _headers,
|
|
body: jsonEncode({"email": email, "otp": otp}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (response.statusCode == 200 && data['data'] != null) {
|
|
await _handleLoginSuccess(data['data']);
|
|
return null;
|
|
}
|
|
return {"error": data['message'] ?? "OTP verification failed."};
|
|
} catch (e, stacktrace) {
|
|
logSafe("Verify OTP error", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
|
return {"error": "Network error. Please check your connection."};
|
|
}
|
|
}
|
|
|
|
/// Handle login success flow
|
|
static Future<void> _handleLoginSuccess(Map<String, dynamic> data) async {
|
|
logSafe("Processing login success...");
|
|
|
|
final jwtToken = data['token'];
|
|
final refreshToken = data['refreshToken'];
|
|
final mpinToken = data['mpinToken'];
|
|
|
|
await LocalStorage.setJwtToken(jwtToken);
|
|
await LocalStorage.setLoggedInUser(true);
|
|
|
|
if (refreshToken != null) await LocalStorage.setRefreshToken(refreshToken);
|
|
|
|
if (mpinToken != null && mpinToken.isNotEmpty) {
|
|
await LocalStorage.setMpinToken(mpinToken);
|
|
await LocalStorage.setIsMpin(true);
|
|
} else {
|
|
await LocalStorage.setIsMpin(false);
|
|
await LocalStorage.removeMpinToken();
|
|
}
|
|
|
|
final permissionController = Get.put(PermissionController());
|
|
await permissionController.loadData(jwtToken);
|
|
|
|
await Get.find<ProjectController>().fetchProjects();
|
|
|
|
isLoggedIn = true;
|
|
logSafe("Login flow completed.");
|
|
}
|
|
}
|