@neofaceid/web-sdk 1.3.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4,6 +4,7 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
+
var _a;
|
|
7
8
|
import require$$0$1, { useRef, useState, useCallback, useReducer, useEffect } from "react";
|
|
8
9
|
import require$$0 from "react-dom";
|
|
9
10
|
import * as faceapi from "face-api.js";
|
|
@@ -1006,7 +1007,7 @@ const useCamera = () => {
|
|
|
1006
1007
|
};
|
|
1007
1008
|
const startCamera = useCallback(
|
|
1008
1009
|
async (constraints) => {
|
|
1009
|
-
var
|
|
1010
|
+
var _a2;
|
|
1010
1011
|
console.log("🎬 [useCamera] startCamera chamado", {
|
|
1011
1012
|
hasExistingStream: !!streamRef.current,
|
|
1012
1013
|
isInitializing: isInitializingRef.current
|
|
@@ -1048,7 +1049,7 @@ const useCamera = () => {
|
|
|
1048
1049
|
console.log("📋 [useCamera] Detalhes do erro:", {
|
|
1049
1050
|
name: error.name,
|
|
1050
1051
|
message: error.message,
|
|
1051
|
-
stack: (
|
|
1052
|
+
stack: (_a2 = error.stack) == null ? void 0 : _a2.substring(0, 200)
|
|
1052
1053
|
});
|
|
1053
1054
|
if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") {
|
|
1054
1055
|
throw new CameraPermissionDenied(
|
|
@@ -1135,10 +1136,11 @@ class NeoFaceError extends Error {
|
|
|
1135
1136
|
this.type = type;
|
|
1136
1137
|
}
|
|
1137
1138
|
}
|
|
1138
|
-
const API_BASE_URL = "
|
|
1139
|
+
const API_BASE_URL = ((_a = { "BASE_URL": "/", "MODE": "production", "DEV": false, "PROD": true, "SSR": false }) == null ? void 0 : _a.VITE_API_BASE_URL) || "http://localhost:8000";
|
|
1139
1140
|
const REQUEST_TIMEOUT = 1e4;
|
|
1140
1141
|
const ensureSecureContext = () => {
|
|
1141
|
-
|
|
1142
|
+
const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname === "";
|
|
1143
|
+
if (!window.isSecureContext && !isLocalhost) {
|
|
1142
1144
|
throw new NeoFaceError(
|
|
1143
1145
|
"Secure context (HTTPS) is required for this operation",
|
|
1144
1146
|
ErrorType.NETWORK
|
|
@@ -1184,7 +1186,7 @@ const validateToken = async (applicationToken) => {
|
|
|
1184
1186
|
ensureSecureContext();
|
|
1185
1187
|
const controller = createTimeoutController();
|
|
1186
1188
|
try {
|
|
1187
|
-
const response = await fetch(`${API_BASE_URL}/api/v1/
|
|
1189
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/auth/application/validate_token/`, {
|
|
1188
1190
|
method: "POST",
|
|
1189
1191
|
headers: {
|
|
1190
1192
|
"Content-Type": "application/json",
|
|
@@ -1239,6 +1241,41 @@ const getOnboardingDetails = async (applicationToken, onboardingToken) => {
|
|
|
1239
1241
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1240
1242
|
}
|
|
1241
1243
|
};
|
|
1244
|
+
const validateOnboardingToken = async (applicationToken, onboardingToken) => {
|
|
1245
|
+
ensureSecureContext();
|
|
1246
|
+
const controller = createTimeoutController();
|
|
1247
|
+
try {
|
|
1248
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`, {
|
|
1249
|
+
method: "GET",
|
|
1250
|
+
headers: {
|
|
1251
|
+
"X-App-Token": applicationToken
|
|
1252
|
+
},
|
|
1253
|
+
signal: controller.signal
|
|
1254
|
+
});
|
|
1255
|
+
if (!response.ok) {
|
|
1256
|
+
if (response.status === 401 || response.status === 403) {
|
|
1257
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1258
|
+
}
|
|
1259
|
+
if (response.status === 404) {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
throw new NeoFaceError(`Falha ao validar token (${response.status})`, ErrorType.NETWORK);
|
|
1263
|
+
}
|
|
1264
|
+
const data = await response.json();
|
|
1265
|
+
return (data == null ? void 0 : data.is_valid) === true;
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
if (error instanceof Error) {
|
|
1268
|
+
if (error.name === "AbortError") {
|
|
1269
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1270
|
+
}
|
|
1271
|
+
if (error instanceof NeoFaceError) {
|
|
1272
|
+
throw error;
|
|
1273
|
+
}
|
|
1274
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1275
|
+
}
|
|
1276
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1242
1279
|
const completeOnboarding = async (applicationToken, onboardingToken, faceImage, documentImage) => {
|
|
1243
1280
|
ensureSecureContext();
|
|
1244
1281
|
const controller = createTimeoutController();
|
|
@@ -1290,6 +1327,82 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
|
|
|
1290
1327
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1291
1328
|
}
|
|
1292
1329
|
};
|
|
1330
|
+
const completeOnboardingWithData = async (applicationToken, onboardingToken, personData, personImages, documentImage) => {
|
|
1331
|
+
ensureSecureContext();
|
|
1332
|
+
if (!personImages || personImages.length === 0) {
|
|
1333
|
+
throw new NeoFaceError("Pelo menos uma imagem da pessoa é obrigatória", ErrorType.VALIDATION_ERROR);
|
|
1334
|
+
}
|
|
1335
|
+
const controller = createTimeoutController();
|
|
1336
|
+
try {
|
|
1337
|
+
const compressedPersonImages = await Promise.all(
|
|
1338
|
+
personImages.map((img) => compressImage(img))
|
|
1339
|
+
);
|
|
1340
|
+
const compressedDocument = await compressImage(documentImage);
|
|
1341
|
+
const formData = new FormData();
|
|
1342
|
+
compressedPersonImages.forEach((img, index) => {
|
|
1343
|
+
formData.append("person_images", img, `person_${index + 1}.jpg`);
|
|
1344
|
+
});
|
|
1345
|
+
formData.append("document_image", compressedDocument, "document.jpg");
|
|
1346
|
+
if (personData.name) {
|
|
1347
|
+
formData.append("name", personData.name);
|
|
1348
|
+
}
|
|
1349
|
+
if (personData.birth_date) {
|
|
1350
|
+
formData.append("birth_date", personData.birth_date);
|
|
1351
|
+
}
|
|
1352
|
+
if (personData.cpf) {
|
|
1353
|
+
formData.append("cpf", personData.cpf);
|
|
1354
|
+
}
|
|
1355
|
+
if (personData.cnpj) {
|
|
1356
|
+
formData.append("cnpj", personData.cnpj);
|
|
1357
|
+
}
|
|
1358
|
+
if (personData.email) {
|
|
1359
|
+
formData.append("email", personData.email);
|
|
1360
|
+
}
|
|
1361
|
+
const response = await fetch(
|
|
1362
|
+
`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,
|
|
1363
|
+
{
|
|
1364
|
+
method: "POST",
|
|
1365
|
+
headers: {
|
|
1366
|
+
"X-App-Token": applicationToken
|
|
1367
|
+
},
|
|
1368
|
+
body: formData,
|
|
1369
|
+
signal: controller.signal
|
|
1370
|
+
}
|
|
1371
|
+
);
|
|
1372
|
+
const data = await response.json();
|
|
1373
|
+
if (!response.ok) {
|
|
1374
|
+
if (response.status === 401 || response.status === 403) {
|
|
1375
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1376
|
+
}
|
|
1377
|
+
if (response.status === 404) {
|
|
1378
|
+
throw new NeoFaceError("Link de onboarding não encontrado", ErrorType.NOT_FOUND);
|
|
1379
|
+
}
|
|
1380
|
+
throw new NeoFaceError((data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding", ErrorType.NETWORK);
|
|
1381
|
+
}
|
|
1382
|
+
return {
|
|
1383
|
+
success: !!data.success,
|
|
1384
|
+
message: data.message || "Onboarding concluído com sucesso",
|
|
1385
|
+
person_id: data.person_id,
|
|
1386
|
+
identity_data_id: data.identity_data_id,
|
|
1387
|
+
confidence_score: data.confidence_score,
|
|
1388
|
+
processing_time: data.processing_time,
|
|
1389
|
+
liveness_passed: data.liveness_passed,
|
|
1390
|
+
document_verified: data.document_verified,
|
|
1391
|
+
person_created: data.person_created
|
|
1392
|
+
};
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
if (error instanceof Error) {
|
|
1395
|
+
if (error.name === "AbortError") {
|
|
1396
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1397
|
+
}
|
|
1398
|
+
if (error instanceof NeoFaceError) {
|
|
1399
|
+
throw error;
|
|
1400
|
+
}
|
|
1401
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1402
|
+
}
|
|
1403
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1293
1406
|
const recognize = async (image, applicationToken) => {
|
|
1294
1407
|
ensureSecureContext();
|
|
1295
1408
|
const controller = createTimeoutController();
|
|
@@ -1332,8 +1445,8 @@ const recognize = async (image, applicationToken) => {
|
|
|
1332
1445
|
throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
|
|
1333
1446
|
}
|
|
1334
1447
|
};
|
|
1335
|
-
const loginWithBiometric = async (image, applicationToken) => {
|
|
1336
|
-
var
|
|
1448
|
+
const loginWithBiometric$1 = async (image, applicationToken) => {
|
|
1449
|
+
var _a2, _b, _c, _d, _e;
|
|
1337
1450
|
ensureSecureContext();
|
|
1338
1451
|
const controller = createTimeoutController();
|
|
1339
1452
|
const compressedImage = await compressImage(image);
|
|
@@ -1363,7 +1476,7 @@ const loginWithBiometric = async (image, applicationToken) => {
|
|
|
1363
1476
|
accessToken: data.accessToken,
|
|
1364
1477
|
alias: data.alias,
|
|
1365
1478
|
user: {
|
|
1366
|
-
id: ((
|
|
1479
|
+
id: ((_a2 = data.user) == null ? void 0 : _a2.id) || "",
|
|
1367
1480
|
email: ((_b = data.user) == null ? void 0 : _b.email) || "",
|
|
1368
1481
|
role: ((_c = data.user) == null ? void 0 : _c.role) || "",
|
|
1369
1482
|
validated: ((_d = data.user) == null ? void 0 : _d.validated) || false,
|
|
@@ -1576,6 +1689,7 @@ const registerPersonWithoutFace = async (personData, applicationToken) => {
|
|
|
1576
1689
|
}
|
|
1577
1690
|
};
|
|
1578
1691
|
const registerPersonWithBiometric = async (personData, facePhotos, applicationToken) => {
|
|
1692
|
+
var _a2, _b, _c, _d, _e, _f, _g, _h;
|
|
1579
1693
|
ensureSecureContext();
|
|
1580
1694
|
if (!facePhotos || facePhotos.length === 0) {
|
|
1581
1695
|
throw new NeoFaceError("At least one face photo is required for biometric registration", ErrorType.VALIDATION_ERROR);
|
|
@@ -1622,29 +1736,56 @@ const registerPersonWithBiometric = async (personData, facePhotos, applicationTo
|
|
|
1622
1736
|
}
|
|
1623
1737
|
try {
|
|
1624
1738
|
const errorData = await response.json();
|
|
1739
|
+
if (errorData.errors) {
|
|
1740
|
+
const errorMessages = Object.entries(errorData.errors).map(([field, messages]) => {
|
|
1741
|
+
const fieldName = field === "password" ? "senha" : field === "password_confirm" ? "confirmação de senha" : field;
|
|
1742
|
+
const messageList = Array.isArray(messages) ? messages.join(", ") : String(messages);
|
|
1743
|
+
return `${fieldName}: ${messageList}`;
|
|
1744
|
+
}).join("; ");
|
|
1745
|
+
throw new NeoFaceError(`Erro de validação: ${errorMessages}`, ErrorType.VALIDATION_ERROR);
|
|
1746
|
+
}
|
|
1625
1747
|
if (errorData.error === "fraud_detected") {
|
|
1626
1748
|
throw new NeoFaceError("Fraud detected in biometric images. Registration blocked.", ErrorType.VALIDATION_ERROR);
|
|
1627
1749
|
}
|
|
1628
|
-
if (errorData.error === "user_exists") {
|
|
1750
|
+
if (errorData.error === "user_exists" || errorData.error_code === "ALREADY_EXISTS") {
|
|
1629
1751
|
throw new NeoFaceError("User with this email already exists", ErrorType.VALIDATION_ERROR);
|
|
1630
1752
|
}
|
|
1631
1753
|
if (errorData.error === "person_exists") {
|
|
1632
1754
|
throw new NeoFaceError("Person with this CPF already exists", ErrorType.VALIDATION_ERROR);
|
|
1633
1755
|
}
|
|
1634
|
-
|
|
1756
|
+
const errorMessage = errorData.message || errorData.error || `Server returned status ${response.status}`;
|
|
1757
|
+
throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);
|
|
1635
1758
|
} catch (parseError) {
|
|
1759
|
+
if (parseError instanceof NeoFaceError) {
|
|
1760
|
+
throw parseError;
|
|
1761
|
+
}
|
|
1636
1762
|
throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);
|
|
1637
1763
|
}
|
|
1638
1764
|
}
|
|
1639
1765
|
const data = await response.json();
|
|
1766
|
+
if (response.status === 202 && ((_a2 = data.data) == null ? void 0 : _a2.task_id)) {
|
|
1767
|
+
return {
|
|
1768
|
+
success: data.success,
|
|
1769
|
+
message: data.message || "Registration is being processed. You will receive an email when completed.",
|
|
1770
|
+
task_id: data.data.task_id,
|
|
1771
|
+
status: data.data.status || "processing",
|
|
1772
|
+
person_id: void 0,
|
|
1773
|
+
user_id: void 0,
|
|
1774
|
+
person_validated: void 0,
|
|
1775
|
+
face_processing: void 0,
|
|
1776
|
+
onboarding_link: void 0
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1640
1779
|
return {
|
|
1641
1780
|
success: data.success,
|
|
1642
1781
|
message: data.message,
|
|
1643
|
-
person_id: data.person_id,
|
|
1644
|
-
user_id: data.user_id,
|
|
1645
|
-
person_validated: data.person_validated,
|
|
1646
|
-
face_processing: data.face_processing,
|
|
1647
|
-
onboarding_link: data.onboarding_link
|
|
1782
|
+
person_id: ((_b = data.data) == null ? void 0 : _b.person_id) || data.person_id,
|
|
1783
|
+
user_id: ((_c = data.data) == null ? void 0 : _c.user_id) || data.user_id,
|
|
1784
|
+
person_validated: ((_d = data.data) == null ? void 0 : _d.person_validated) || data.person_validated,
|
|
1785
|
+
face_processing: ((_e = data.data) == null ? void 0 : _e.face_processing) || data.face_processing,
|
|
1786
|
+
onboarding_link: ((_f = data.data) == null ? void 0 : _f.onboarding_link) || data.onboarding_link,
|
|
1787
|
+
task_id: (_g = data.data) == null ? void 0 : _g.task_id,
|
|
1788
|
+
status: (_h = data.data) == null ? void 0 : _h.status
|
|
1648
1789
|
};
|
|
1649
1790
|
} catch (error) {
|
|
1650
1791
|
if (error instanceof Error) {
|
|
@@ -1706,7 +1847,7 @@ const loginWithEmail = async (email, password, applicationToken) => {
|
|
|
1706
1847
|
}
|
|
1707
1848
|
};
|
|
1708
1849
|
const identifyPerson = async (image, applicationToken) => {
|
|
1709
|
-
var
|
|
1850
|
+
var _a2, _b;
|
|
1710
1851
|
ensureSecureContext();
|
|
1711
1852
|
const controller = createTimeoutController();
|
|
1712
1853
|
const compressedImage = await compressImage(image);
|
|
@@ -1742,7 +1883,7 @@ const identifyPerson = async (image, applicationToken) => {
|
|
|
1742
1883
|
if (!data.success) {
|
|
1743
1884
|
throw new NeoFaceError(data.message || "Person identification failed", ErrorType.RECOGNITION_FAILED);
|
|
1744
1885
|
}
|
|
1745
|
-
const personName = ((
|
|
1886
|
+
const personName = ((_a2 = data.person) == null ? void 0 : _a2.name) || "";
|
|
1746
1887
|
const birthDateStr = (_b = data.person) == null ? void 0 : _b.birth_date;
|
|
1747
1888
|
if (!personName) {
|
|
1748
1889
|
throw new NeoFaceError("Person name not found in response", ErrorType.PERSON_NOT_FOUND);
|
|
@@ -2018,8 +2159,8 @@ function FaceCaptureModal({ accessToken, onClose, onSuccess }) {
|
|
|
2018
2159
|
canvasRef.current.height = videoRef.current.videoHeight;
|
|
2019
2160
|
context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
2020
2161
|
const blob = await new Promise((resolve, reject) => {
|
|
2021
|
-
var
|
|
2022
|
-
(
|
|
2162
|
+
var _a2;
|
|
2163
|
+
(_a2 = canvasRef.current) == null ? void 0 : _a2.toBlob(
|
|
2023
2164
|
(b) => {
|
|
2024
2165
|
if (b)
|
|
2025
2166
|
resolve(b);
|
|
@@ -2156,8 +2297,8 @@ function BiometricRegistrationModal({
|
|
|
2156
2297
|
onClose,
|
|
2157
2298
|
onSuccess,
|
|
2158
2299
|
onError,
|
|
2159
|
-
useRealApi =
|
|
2160
|
-
//
|
|
2300
|
+
useRealApi = true
|
|
2301
|
+
// Sempre usa API real em produção
|
|
2161
2302
|
}) {
|
|
2162
2303
|
const videoRef = useRef(null);
|
|
2163
2304
|
const canvasRef = useRef(null);
|
|
@@ -2419,8 +2560,8 @@ function BiometricRegistrationModal({
|
|
|
2419
2560
|
}
|
|
2420
2561
|
};
|
|
2421
2562
|
const startTimer = setTimeout(() => {
|
|
2422
|
-
var
|
|
2423
|
-
if (isRunning && ((
|
|
2563
|
+
var _a2;
|
|
2564
|
+
if (isRunning && ((_a2 = videoRef.current) == null ? void 0 : _a2.readyState) === 4) {
|
|
2424
2565
|
detectFace2();
|
|
2425
2566
|
}
|
|
2426
2567
|
}, 1e3);
|
|
@@ -2481,60 +2622,19 @@ function BiometricRegistrationModal({
|
|
|
2481
2622
|
};
|
|
2482
2623
|
const processRegistration = async (photos) => {
|
|
2483
2624
|
try {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
onboarding_link: result2.onboarding_link
|
|
2495
|
-
};
|
|
2496
|
-
dispatch({ type: "SUCCESS" });
|
|
2497
|
-
onSuccess(adaptedResult);
|
|
2498
|
-
return;
|
|
2499
|
-
}
|
|
2500
|
-
console.log("🎭 Usando modo SIMULADO para testes...");
|
|
2501
|
-
const facePhotosBase64 = await Promise.all(
|
|
2502
|
-
photos.map(async (photo) => {
|
|
2503
|
-
return new Promise((resolve, reject) => {
|
|
2504
|
-
const reader = new FileReader();
|
|
2505
|
-
reader.onload = () => {
|
|
2506
|
-
const result2 = reader.result;
|
|
2507
|
-
resolve(result2);
|
|
2508
|
-
};
|
|
2509
|
-
reader.onerror = reject;
|
|
2510
|
-
reader.readAsDataURL(photo);
|
|
2511
|
-
});
|
|
2512
|
-
})
|
|
2513
|
-
);
|
|
2514
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2515
|
-
const result = {
|
|
2516
|
-
success: true,
|
|
2517
|
-
message: "Registro biométrico realizado com sucesso",
|
|
2518
|
-
person_id: "test-person-123",
|
|
2519
|
-
user_id: "test-user-456",
|
|
2520
|
-
person_validated: true,
|
|
2521
|
-
face_processing: {
|
|
2522
|
-
total_photos_captured: photos.length,
|
|
2523
|
-
photo_sizes: photos.map((p, i) => ({
|
|
2524
|
-
photo_index: i + 1,
|
|
2525
|
-
size_kb: Math.round(p.size / 1024)
|
|
2526
|
-
})),
|
|
2527
|
-
// Mostra apenas preview dos primeiros 50 caracteres de cada foto em base64
|
|
2528
|
-
photos_preview: facePhotosBase64.map((b64, i) => ({
|
|
2529
|
-
photo_index: i + 1,
|
|
2530
|
-
format: "data:image/jpeg;base64,...",
|
|
2531
|
-
preview: b64.substring(0, 50) + "...",
|
|
2532
|
-
full_length: b64.length
|
|
2533
|
-
}))
|
|
2534
|
-
}
|
|
2625
|
+
console.log("🚀 Fazendo requisição ao backend...");
|
|
2626
|
+
const result = await registerPersonWithBiometric(personData, photos, applicationToken);
|
|
2627
|
+
const adaptedResult = {
|
|
2628
|
+
success: result.success,
|
|
2629
|
+
message: result.message,
|
|
2630
|
+
person_id: result.person_id,
|
|
2631
|
+
user_id: result.user_id,
|
|
2632
|
+
person_validated: result.person_validated,
|
|
2633
|
+
face_processing: result.face_processing,
|
|
2634
|
+
onboarding_link: result.onboarding_link
|
|
2535
2635
|
};
|
|
2536
2636
|
dispatch({ type: "SUCCESS" });
|
|
2537
|
-
onSuccess(
|
|
2637
|
+
onSuccess(adaptedResult);
|
|
2538
2638
|
} catch (error) {
|
|
2539
2639
|
const errorMessage = error instanceof Error ? error.message : "Erro desconhecido";
|
|
2540
2640
|
dispatch({ type: "ERROR", message: errorMessage });
|
|
@@ -3216,7 +3316,7 @@ function BiometricRegistrationModal({
|
|
|
3216
3316
|
/* @__PURE__ */ jsxRuntimeExports.jsx("canvas", { ref: canvasRef })
|
|
3217
3317
|
] });
|
|
3218
3318
|
}
|
|
3219
|
-
|
|
3319
|
+
let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
|
|
3220
3320
|
constructor(options) {
|
|
3221
3321
|
__publicField(this, "modal", null);
|
|
3222
3322
|
__publicField(this, "video", null);
|
|
@@ -3418,21 +3518,21 @@ class BiometricCaptureModal {
|
|
|
3418
3518
|
const cancelBtn = this.modal.querySelector(".neofaceid-cancel-btn");
|
|
3419
3519
|
const captureBtn = this.modal.querySelector(".neofaceid-capture-btn");
|
|
3420
3520
|
closeBtn == null ? void 0 : closeBtn.addEventListener("click", () => {
|
|
3421
|
-
var
|
|
3521
|
+
var _a2, _b;
|
|
3422
3522
|
this.close();
|
|
3423
|
-
(_b = (
|
|
3523
|
+
(_b = (_a2 = this.options).onCancel) == null ? void 0 : _b.call(_a2);
|
|
3424
3524
|
});
|
|
3425
3525
|
cancelBtn == null ? void 0 : cancelBtn.addEventListener("click", () => {
|
|
3426
|
-
var
|
|
3526
|
+
var _a2, _b;
|
|
3427
3527
|
this.close();
|
|
3428
|
-
(_b = (
|
|
3528
|
+
(_b = (_a2 = this.options).onCancel) == null ? void 0 : _b.call(_a2);
|
|
3429
3529
|
});
|
|
3430
3530
|
captureBtn == null ? void 0 : captureBtn.addEventListener("click", () => {
|
|
3431
3531
|
this.captureImage();
|
|
3432
3532
|
});
|
|
3433
3533
|
this.modal.addEventListener("click", (e) => {
|
|
3434
|
-
var
|
|
3435
|
-
if (e.target === ((
|
|
3534
|
+
var _a2, _b, _c;
|
|
3535
|
+
if (e.target === ((_a2 = this.modal) == null ? void 0 : _a2.querySelector(".neofaceid-modal-overlay"))) {
|
|
3436
3536
|
this.close();
|
|
3437
3537
|
(_c = (_b = this.options).onCancel) == null ? void 0 : _c.call(_b);
|
|
3438
3538
|
}
|
|
@@ -3715,52 +3815,837 @@ class BiometricCaptureModal {
|
|
|
3715
3815
|
`;
|
|
3716
3816
|
document.head.appendChild(style);
|
|
3717
3817
|
}
|
|
3818
|
+
};
|
|
3819
|
+
function BiometricStatusOverlayComponent({ status, onClose }) {
|
|
3820
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
3821
|
+
useEffect(() => {
|
|
3822
|
+
if (status === "success") {
|
|
3823
|
+
const timer = setTimeout(() => {
|
|
3824
|
+
setIsVisible(false);
|
|
3825
|
+
setTimeout(() => {
|
|
3826
|
+
onClose == null ? void 0 : onClose();
|
|
3827
|
+
}, 300);
|
|
3828
|
+
}, 800);
|
|
3829
|
+
return () => clearTimeout(timer);
|
|
3830
|
+
}
|
|
3831
|
+
}, [status, onClose]);
|
|
3832
|
+
if (!isVisible)
|
|
3833
|
+
return null;
|
|
3834
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3835
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
3836
|
+
@keyframes fadeIn {
|
|
3837
|
+
from { opacity: 0; transform: scale(0.9); }
|
|
3838
|
+
to { opacity: 1; transform: scale(1); }
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
@keyframes fadeOut {
|
|
3842
|
+
from { opacity: 1; transform: scale(1); }
|
|
3843
|
+
to { opacity: 0; transform: scale(0.9); }
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
@keyframes pulse {
|
|
3847
|
+
0%, 100% {
|
|
3848
|
+
transform: scale(1);
|
|
3849
|
+
opacity: 1;
|
|
3850
|
+
}
|
|
3851
|
+
50% {
|
|
3852
|
+
transform: scale(1.1);
|
|
3853
|
+
opacity: 0.9;
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
@keyframes spin {
|
|
3858
|
+
from { transform: rotate(0deg); }
|
|
3859
|
+
to { transform: rotate(360deg); }
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
@keyframes checkmark {
|
|
3863
|
+
0% {
|
|
3864
|
+
stroke-dashoffset: 100;
|
|
3865
|
+
opacity: 0;
|
|
3866
|
+
}
|
|
3867
|
+
50% {
|
|
3868
|
+
opacity: 1;
|
|
3869
|
+
}
|
|
3870
|
+
100% {
|
|
3871
|
+
stroke-dashoffset: 0;
|
|
3872
|
+
opacity: 1;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
@keyframes errorShake {
|
|
3877
|
+
0%, 100% { transform: translateX(0) scale(1); }
|
|
3878
|
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }
|
|
3879
|
+
20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
@keyframes scaleIn {
|
|
3883
|
+
0% {
|
|
3884
|
+
transform: scale(0);
|
|
3885
|
+
opacity: 0;
|
|
3886
|
+
}
|
|
3887
|
+
50% {
|
|
3888
|
+
transform: scale(1.1);
|
|
3889
|
+
}
|
|
3890
|
+
100% {
|
|
3891
|
+
transform: scale(1);
|
|
3892
|
+
opacity: 1;
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
.neofaceid-overlay-container {
|
|
3897
|
+
position: fixed;
|
|
3898
|
+
top: 0;
|
|
3899
|
+
left: 0;
|
|
3900
|
+
right: 0;
|
|
3901
|
+
bottom: 0;
|
|
3902
|
+
display: flex;
|
|
3903
|
+
align-items: center;
|
|
3904
|
+
justify-content: center;
|
|
3905
|
+
z-index: 10000;
|
|
3906
|
+
pointer-events: none;
|
|
3907
|
+
animation: fadeIn 0.3s ease-out;
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
.neofaceid-overlay-container.fade-out {
|
|
3911
|
+
animation: fadeOut 0.3s ease-out;
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
.neofaceid-overlay-content {
|
|
3915
|
+
display: flex;
|
|
3916
|
+
flex-direction: column;
|
|
3917
|
+
align-items: center;
|
|
3918
|
+
justify-content: center;
|
|
3919
|
+
gap: 24px;
|
|
3920
|
+
pointer-events: auto;
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
.neofaceid-logo-wrapper {
|
|
3924
|
+
position: relative;
|
|
3925
|
+
width: 120px;
|
|
3926
|
+
height: 120px;
|
|
3927
|
+
display: flex;
|
|
3928
|
+
align-items: center;
|
|
3929
|
+
justify-content: center;
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
.neofaceid-logo {
|
|
3933
|
+
width: 80px;
|
|
3934
|
+
height: 80px;
|
|
3935
|
+
object-fit: contain;
|
|
3936
|
+
filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
.neofaceid-logo.pulsing {
|
|
3940
|
+
animation: pulse 2s ease-in-out infinite;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
.neofaceid-status-icon {
|
|
3944
|
+
position: absolute;
|
|
3945
|
+
inset: 0;
|
|
3946
|
+
display: flex;
|
|
3947
|
+
align-items: center;
|
|
3948
|
+
justify-content: center;
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
.neofaceid-success-icon {
|
|
3952
|
+
width: 60px;
|
|
3953
|
+
height: 60px;
|
|
3954
|
+
color: #10b981;
|
|
3955
|
+
animation: scaleIn 0.6s ease-out;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
.neofaceid-success-icon svg circle {
|
|
3959
|
+
stroke-dasharray: 100;
|
|
3960
|
+
stroke-dashoffset: 100;
|
|
3961
|
+
animation: checkmark 0.6s ease-out 0.2s forwards;
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
.neofaceid-error-icon {
|
|
3965
|
+
width: 60px;
|
|
3966
|
+
height: 60px;
|
|
3967
|
+
color: #ef4444;
|
|
3968
|
+
animation: errorShake 0.5s ease-out, scaleIn 0.4s ease-out;
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
/* Responsive */
|
|
3972
|
+
@media (max-width: 640px) {
|
|
3973
|
+
.neofaceid-logo-wrapper {
|
|
3974
|
+
width: 100px;
|
|
3975
|
+
height: 100px;
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
.neofaceid-logo {
|
|
3979
|
+
width: 70px;
|
|
3980
|
+
height: 70px;
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
` }),
|
|
3984
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `neofaceid-overlay-container ${!isVisible ? "fade-out" : ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-overlay-content", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-logo-wrapper", children: [
|
|
3985
|
+
(status === "preparing" || status === "verifying") && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3986
|
+
"img",
|
|
3987
|
+
{
|
|
3988
|
+
src: "/public/logo-icone-no-backgorund.png",
|
|
3989
|
+
alt: "NeoFaceId",
|
|
3990
|
+
className: `neofaceid-logo ${status === "verifying" ? "pulsing" : ""}`,
|
|
3991
|
+
onError: (e) => {
|
|
3992
|
+
var _a2;
|
|
3993
|
+
const img = e.target;
|
|
3994
|
+
img.style.display = "none";
|
|
3995
|
+
const fallback = document.createElement("div");
|
|
3996
|
+
fallback.innerHTML = `
|
|
3997
|
+
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3998
|
+
<circle cx="40" cy="40" r="35" fill="#7c3aed" opacity="0.2"/>
|
|
3999
|
+
<path d="M40 20 L40 60 M20 40 L60 40" stroke="#7c3aed" stroke-width="4" stroke-linecap="round"/>
|
|
4000
|
+
</svg>
|
|
4001
|
+
`;
|
|
4002
|
+
fallback.style.cssText = "width: 80px; height: 80px; display: flex; align-items: center; justify-content: center;";
|
|
4003
|
+
(_a2 = img.parentElement) == null ? void 0 : _a2.appendChild(fallback);
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
) }),
|
|
4007
|
+
status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4008
|
+
"svg",
|
|
4009
|
+
{
|
|
4010
|
+
className: "neofaceid-success-icon",
|
|
4011
|
+
viewBox: "0 0 60 60",
|
|
4012
|
+
fill: "none",
|
|
4013
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4014
|
+
children: [
|
|
4015
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4016
|
+
"circle",
|
|
4017
|
+
{
|
|
4018
|
+
cx: "30",
|
|
4019
|
+
cy: "30",
|
|
4020
|
+
r: "28",
|
|
4021
|
+
stroke: "currentColor",
|
|
4022
|
+
strokeWidth: "3",
|
|
4023
|
+
fill: "none"
|
|
4024
|
+
}
|
|
4025
|
+
),
|
|
4026
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4027
|
+
"path",
|
|
4028
|
+
{
|
|
4029
|
+
d: "M15 30 L25 40 L45 20",
|
|
4030
|
+
stroke: "currentColor",
|
|
4031
|
+
strokeWidth: "3",
|
|
4032
|
+
strokeLinecap: "round",
|
|
4033
|
+
strokeLinejoin: "round",
|
|
4034
|
+
fill: "none"
|
|
4035
|
+
}
|
|
4036
|
+
)
|
|
4037
|
+
]
|
|
4038
|
+
}
|
|
4039
|
+
) }),
|
|
4040
|
+
status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4041
|
+
"svg",
|
|
4042
|
+
{
|
|
4043
|
+
className: "neofaceid-error-icon",
|
|
4044
|
+
viewBox: "0 0 60 60",
|
|
4045
|
+
fill: "none",
|
|
4046
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4047
|
+
children: [
|
|
4048
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4049
|
+
"circle",
|
|
4050
|
+
{
|
|
4051
|
+
cx: "30",
|
|
4052
|
+
cy: "30",
|
|
4053
|
+
r: "28",
|
|
4054
|
+
stroke: "currentColor",
|
|
4055
|
+
strokeWidth: "3",
|
|
4056
|
+
fill: "none"
|
|
4057
|
+
}
|
|
4058
|
+
),
|
|
4059
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4060
|
+
"path",
|
|
4061
|
+
{
|
|
4062
|
+
d: "M20 20 L40 40 M40 20 L20 40",
|
|
4063
|
+
stroke: "currentColor",
|
|
4064
|
+
strokeWidth: "3",
|
|
4065
|
+
strokeLinecap: "round"
|
|
4066
|
+
}
|
|
4067
|
+
)
|
|
4068
|
+
]
|
|
4069
|
+
}
|
|
4070
|
+
) })
|
|
4071
|
+
] }) }) })
|
|
4072
|
+
] });
|
|
3718
4073
|
}
|
|
3719
|
-
class
|
|
3720
|
-
constructor(
|
|
3721
|
-
__publicField(this, "
|
|
3722
|
-
__publicField(this, "
|
|
3723
|
-
this
|
|
4074
|
+
class BiometricStatusOverlay {
|
|
4075
|
+
constructor() {
|
|
4076
|
+
__publicField(this, "container", null);
|
|
4077
|
+
__publicField(this, "root", null);
|
|
4078
|
+
__publicField(this, "currentStatus", "preparing");
|
|
3724
4079
|
}
|
|
3725
4080
|
/**
|
|
3726
|
-
*
|
|
4081
|
+
* Cria e exibe o overlay
|
|
3727
4082
|
*/
|
|
3728
|
-
|
|
3729
|
-
this.
|
|
3730
|
-
|
|
4083
|
+
show(status = "preparing") {
|
|
4084
|
+
if (this.container) {
|
|
4085
|
+
this.updateStatus(status);
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
this.container = document.createElement("div");
|
|
4089
|
+
this.container.id = "neofaceid-status-overlay";
|
|
4090
|
+
document.body.appendChild(this.container);
|
|
4091
|
+
this.root = createRoot(this.container);
|
|
4092
|
+
this.currentStatus = status;
|
|
4093
|
+
this.render();
|
|
3731
4094
|
}
|
|
3732
4095
|
/**
|
|
3733
|
-
*
|
|
4096
|
+
* Atualiza o status do overlay
|
|
3734
4097
|
*/
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
this.
|
|
4098
|
+
updateStatus(status) {
|
|
4099
|
+
this.currentStatus = status;
|
|
4100
|
+
if (this.root) {
|
|
4101
|
+
this.render();
|
|
3739
4102
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
4103
|
+
}
|
|
4104
|
+
/**
|
|
4105
|
+
* Fecha e remove o overlay
|
|
4106
|
+
*/
|
|
4107
|
+
close() {
|
|
4108
|
+
if (this.container) {
|
|
4109
|
+
if (this.root) {
|
|
4110
|
+
this.root.render(
|
|
4111
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4112
|
+
BiometricStatusOverlayComponent,
|
|
4113
|
+
{
|
|
4114
|
+
status: this.currentStatus,
|
|
4115
|
+
onClose: () => {
|
|
4116
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4117
|
+
document.body.removeChild(this.container);
|
|
4118
|
+
}
|
|
4119
|
+
this.container = null;
|
|
4120
|
+
this.root = null;
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
)
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
setTimeout(() => {
|
|
4127
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4128
|
+
document.body.removeChild(this.container);
|
|
4129
|
+
}
|
|
4130
|
+
this.container = null;
|
|
4131
|
+
this.root = null;
|
|
4132
|
+
}, 300);
|
|
3742
4133
|
}
|
|
3743
4134
|
}
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
4135
|
+
/**
|
|
4136
|
+
* Renderiza o componente
|
|
4137
|
+
*/
|
|
4138
|
+
render() {
|
|
4139
|
+
if (!this.root || !this.container)
|
|
4140
|
+
return;
|
|
4141
|
+
this.root.render(
|
|
4142
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(BiometricStatusOverlayComponent, { status: this.currentStatus, onClose: () => this.close() })
|
|
4143
|
+
);
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
function FallbackPromptComponent({
|
|
4147
|
+
error,
|
|
4148
|
+
onRetry,
|
|
4149
|
+
onCredentials,
|
|
4150
|
+
onCancel
|
|
4151
|
+
}) {
|
|
4152
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
4153
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
4154
|
+
@keyframes slideUp {
|
|
4155
|
+
from {
|
|
4156
|
+
opacity: 0;
|
|
4157
|
+
transform: translateY(20px);
|
|
4158
|
+
}
|
|
4159
|
+
to {
|
|
4160
|
+
opacity: 1;
|
|
4161
|
+
transform: translateY(0);
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
@keyframes fadeIn {
|
|
4166
|
+
from { opacity: 0; }
|
|
4167
|
+
to { opacity: 1; }
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
.neofaceid-fallback-overlay {
|
|
4171
|
+
position: fixed;
|
|
4172
|
+
inset: 0;
|
|
4173
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4174
|
+
backdrop-filter: blur(8px);
|
|
4175
|
+
display: flex;
|
|
4176
|
+
align-items: center;
|
|
4177
|
+
justify-content: center;
|
|
4178
|
+
z-index: 10001;
|
|
4179
|
+
padding: 20px;
|
|
4180
|
+
animation: fadeIn 0.3s ease-out;
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
.neofaceid-fallback-prompt {
|
|
4184
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4185
|
+
border-radius: 20px;
|
|
4186
|
+
padding: 32px;
|
|
4187
|
+
max-width: 400px;
|
|
4188
|
+
width: 100%;
|
|
4189
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4190
|
+
animation: slideUp 0.4s ease-out;
|
|
4191
|
+
text-align: center;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
.neofaceid-fallback-icon {
|
|
4195
|
+
width: 64px;
|
|
4196
|
+
height: 64px;
|
|
4197
|
+
margin: 0 auto 20px;
|
|
4198
|
+
color: #ef4444;
|
|
4199
|
+
animation: scaleIn 0.4s ease-out;
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
@keyframes scaleIn {
|
|
4203
|
+
0% {
|
|
4204
|
+
transform: scale(0);
|
|
4205
|
+
opacity: 0;
|
|
4206
|
+
}
|
|
4207
|
+
50% {
|
|
4208
|
+
transform: scale(1.1);
|
|
4209
|
+
}
|
|
4210
|
+
100% {
|
|
4211
|
+
transform: scale(1);
|
|
4212
|
+
opacity: 1;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
.neofaceid-fallback-title {
|
|
4217
|
+
font-size: 22px;
|
|
4218
|
+
font-weight: 700;
|
|
4219
|
+
color: white;
|
|
4220
|
+
margin: 0 0 12px 0;
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
.neofaceid-fallback-message {
|
|
4224
|
+
font-size: 15px;
|
|
4225
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4226
|
+
margin: 0 0 28px 0;
|
|
4227
|
+
line-height: 1.5;
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
.neofaceid-fallback-actions {
|
|
4231
|
+
display: flex;
|
|
4232
|
+
flex-direction: column;
|
|
4233
|
+
gap: 12px;
|
|
4234
|
+
}
|
|
4235
|
+
|
|
4236
|
+
.neofaceid-fallback-button {
|
|
4237
|
+
padding: 14px 24px;
|
|
4238
|
+
border-radius: 12px;
|
|
4239
|
+
border: none;
|
|
4240
|
+
font-size: 16px;
|
|
4241
|
+
font-weight: 600;
|
|
4242
|
+
cursor: pointer;
|
|
4243
|
+
transition: all 0.2s;
|
|
4244
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
.neofaceid-fallback-button-primary {
|
|
4248
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4249
|
+
color: white;
|
|
4250
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
.neofaceid-fallback-button-primary:hover {
|
|
4254
|
+
transform: translateY(-2px);
|
|
4255
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
.neofaceid-fallback-button-primary:active {
|
|
4259
|
+
transform: translateY(0);
|
|
4260
|
+
}
|
|
4261
|
+
|
|
4262
|
+
.neofaceid-fallback-button-secondary {
|
|
4263
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4264
|
+
color: white;
|
|
4265
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4266
|
+
}
|
|
4267
|
+
|
|
4268
|
+
.neofaceid-fallback-button-secondary:hover {
|
|
4269
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4270
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
.neofaceid-fallback-button-ghost {
|
|
4274
|
+
background: transparent;
|
|
4275
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4276
|
+
padding: 10px;
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
.neofaceid-fallback-button-ghost:hover {
|
|
4280
|
+
color: white;
|
|
4281
|
+
background: rgba(255, 255, 255, 0.05);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
@media (max-width: 640px) {
|
|
4285
|
+
.neofaceid-fallback-prompt {
|
|
4286
|
+
padding: 24px;
|
|
4287
|
+
border-radius: 16px;
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
.neofaceid-fallback-title {
|
|
4291
|
+
font-size: 20px;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
` }),
|
|
4295
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-prompt", children: [
|
|
4296
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
4297
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: "30", stroke: "currentColor", strokeWidth: "3", fill: "none" }),
|
|
4298
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4299
|
+
"path",
|
|
4300
|
+
{
|
|
4301
|
+
d: "M20 20 L44 44 M44 20 L20 44",
|
|
4302
|
+
stroke: "currentColor",
|
|
4303
|
+
strokeWidth: "3",
|
|
4304
|
+
strokeLinecap: "round"
|
|
4305
|
+
}
|
|
4306
|
+
)
|
|
4307
|
+
] }) }),
|
|
4308
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "neofaceid-fallback-title", children: "Não foi possível reconhecer" }),
|
|
4309
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "neofaceid-fallback-message", children: (error == null ? void 0 : error.message) || "Não conseguimos reconhecer sua face. Tente novamente ou use email e senha." }),
|
|
4310
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-actions", children: [
|
|
4311
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4312
|
+
"button",
|
|
4313
|
+
{
|
|
4314
|
+
type: "button",
|
|
4315
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-primary",
|
|
4316
|
+
onClick: onRetry,
|
|
4317
|
+
children: "Tentar Novamente"
|
|
4318
|
+
}
|
|
4319
|
+
),
|
|
4320
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4321
|
+
"button",
|
|
4322
|
+
{
|
|
4323
|
+
type: "button",
|
|
4324
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-secondary",
|
|
4325
|
+
onClick: onCredentials,
|
|
4326
|
+
children: "Entrar com Email e Senha"
|
|
4327
|
+
}
|
|
4328
|
+
),
|
|
4329
|
+
onCancel && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4330
|
+
"button",
|
|
4331
|
+
{
|
|
4332
|
+
type: "button",
|
|
4333
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-ghost",
|
|
4334
|
+
onClick: onCancel,
|
|
4335
|
+
children: "Cancelar"
|
|
4336
|
+
}
|
|
4337
|
+
)
|
|
4338
|
+
] })
|
|
4339
|
+
] }) })
|
|
4340
|
+
] });
|
|
4341
|
+
}
|
|
4342
|
+
class FallbackPrompt {
|
|
4343
|
+
constructor() {
|
|
4344
|
+
__publicField(this, "container", null);
|
|
4345
|
+
__publicField(this, "root", null);
|
|
4346
|
+
}
|
|
4347
|
+
/**
|
|
4348
|
+
* Exibe o prompt de fallback
|
|
4349
|
+
*/
|
|
4350
|
+
show(error, onRetry, onCredentials, onCancel) {
|
|
4351
|
+
if (this.container) {
|
|
4352
|
+
return;
|
|
4353
|
+
}
|
|
4354
|
+
this.container = document.createElement("div");
|
|
4355
|
+
this.container.id = "neofaceid-fallback-prompt";
|
|
4356
|
+
document.body.appendChild(this.container);
|
|
4357
|
+
this.root = createRoot(this.container);
|
|
4358
|
+
this.render(error, onRetry, onCredentials, onCancel);
|
|
4359
|
+
}
|
|
4360
|
+
/**
|
|
4361
|
+
* Fecha o prompt
|
|
4362
|
+
*/
|
|
4363
|
+
close() {
|
|
4364
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4365
|
+
if (this.root) {
|
|
4366
|
+
this.root.unmount();
|
|
4367
|
+
}
|
|
4368
|
+
document.body.removeChild(this.container);
|
|
4369
|
+
this.container = null;
|
|
4370
|
+
this.root = null;
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
/**
|
|
4374
|
+
* Renderiza o componente
|
|
4375
|
+
*/
|
|
4376
|
+
render(error, onRetry, onCredentials, onCancel) {
|
|
4377
|
+
if (!this.root || !this.container)
|
|
4378
|
+
return;
|
|
4379
|
+
this.root.render(
|
|
4380
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4381
|
+
FallbackPromptComponent,
|
|
4382
|
+
{
|
|
4383
|
+
error,
|
|
4384
|
+
onRetry: () => {
|
|
4385
|
+
this.close();
|
|
4386
|
+
onRetry();
|
|
4387
|
+
},
|
|
4388
|
+
onCredentials: () => {
|
|
4389
|
+
this.close();
|
|
4390
|
+
onCredentials();
|
|
4391
|
+
},
|
|
4392
|
+
onCancel: () => {
|
|
4393
|
+
this.close();
|
|
4394
|
+
onCancel == null ? void 0 : onCancel();
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
)
|
|
4398
|
+
);
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
class EmailPasswordModal {
|
|
4402
|
+
constructor(options) {
|
|
4403
|
+
__publicField(this, "options");
|
|
4404
|
+
__publicField(this, "modalElement", null);
|
|
4405
|
+
this.options = options;
|
|
4406
|
+
}
|
|
4407
|
+
/**
|
|
4408
|
+
* Abre o modal de login com email e senha.
|
|
4409
|
+
*/
|
|
4410
|
+
async open() {
|
|
4411
|
+
this.createModal();
|
|
4412
|
+
document.body.appendChild(this.modalElement);
|
|
4413
|
+
}
|
|
4414
|
+
/**
|
|
4415
|
+
* Fecha o modal.
|
|
4416
|
+
*/
|
|
4417
|
+
close() {
|
|
4418
|
+
if (this.modalElement) {
|
|
4419
|
+
document.body.removeChild(this.modalElement);
|
|
4420
|
+
this.modalElement = null;
|
|
4421
|
+
}
|
|
4422
|
+
if (this.options.onCancel) {
|
|
4423
|
+
this.options.onCancel();
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4426
|
+
createModal() {
|
|
4427
|
+
this.addStyles();
|
|
4428
|
+
this.modalElement = document.createElement("div");
|
|
4429
|
+
this.modalElement.className = "neoface-email-password-modal";
|
|
4430
|
+
this.modalElement.innerHTML = `
|
|
4431
|
+
<div class="neoface-email-modal-overlay">
|
|
4432
|
+
<div class="neoface-email-modal-content">
|
|
4433
|
+
<h2>${this.options.title || "Login com Email e Senha"}</h2>
|
|
4434
|
+
<p>${this.options.subtitle || "Informe seus dados para login."}</p>
|
|
4435
|
+
<form id="email-password-form">
|
|
4436
|
+
<input type="email" id="email" placeholder="Email" required />
|
|
4437
|
+
<input type="password" id="password" placeholder="Senha" required />
|
|
4438
|
+
<button type="submit" class="neoface-email-submit-btn">Entrar</button>
|
|
4439
|
+
</form>
|
|
4440
|
+
<button class="neoface-email-cancel-btn">Cancelar</button>
|
|
4441
|
+
|
|
4442
|
+
<!-- Branding -->
|
|
4443
|
+
<div class="neoface-email-branding">
|
|
4444
|
+
<img src="/public/logo-icone-no-backgorund.png" alt="NeoFaceId" class="neoface-email-brand-logo" onerror="this.style.display='none'" />
|
|
4445
|
+
<span>NeoFaceId by</span>
|
|
4446
|
+
<span class="neoface-email-brand-name">OCTA</span>
|
|
4447
|
+
</div>
|
|
4448
|
+
</div>
|
|
4449
|
+
</div>
|
|
3758
4450
|
`;
|
|
3759
4451
|
const form = this.modalElement.querySelector("#email-password-form");
|
|
3760
4452
|
form.addEventListener("submit", this.handleSubmit.bind(this));
|
|
3761
|
-
const cancelButton = this.modalElement.querySelector(
|
|
4453
|
+
const cancelButton = this.modalElement.querySelector(
|
|
4454
|
+
".neoface-email-cancel-btn"
|
|
4455
|
+
);
|
|
3762
4456
|
cancelButton.addEventListener("click", this.close.bind(this));
|
|
3763
4457
|
}
|
|
4458
|
+
addStyles() {
|
|
4459
|
+
const styleId = "neoface-email-password-modal-styles";
|
|
4460
|
+
const existingStyles = document.getElementById(styleId);
|
|
4461
|
+
if (existingStyles) {
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
const style = document.createElement("style");
|
|
4465
|
+
style.id = styleId;
|
|
4466
|
+
style.textContent = `
|
|
4467
|
+
.neoface-email-password-modal {
|
|
4468
|
+
position: fixed;
|
|
4469
|
+
top: 0;
|
|
4470
|
+
left: 0;
|
|
4471
|
+
width: 100%;
|
|
4472
|
+
height: 100%;
|
|
4473
|
+
z-index: 10002;
|
|
4474
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
.neoface-email-modal-overlay {
|
|
4478
|
+
position: absolute;
|
|
4479
|
+
inset: 0;
|
|
4480
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4481
|
+
backdrop-filter: blur(8px);
|
|
4482
|
+
display: flex;
|
|
4483
|
+
align-items: center;
|
|
4484
|
+
justify-content: center;
|
|
4485
|
+
padding: 20px;
|
|
4486
|
+
animation: fadeIn 0.3s ease-out;
|
|
4487
|
+
}
|
|
4488
|
+
|
|
4489
|
+
@keyframes fadeIn {
|
|
4490
|
+
from { opacity: 0; }
|
|
4491
|
+
to { opacity: 1; }
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
@keyframes slideUp {
|
|
4495
|
+
from {
|
|
4496
|
+
opacity: 0;
|
|
4497
|
+
transform: translateY(20px);
|
|
4498
|
+
}
|
|
4499
|
+
to {
|
|
4500
|
+
opacity: 1;
|
|
4501
|
+
transform: translateY(0);
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
.neoface-email-modal-content {
|
|
4506
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4507
|
+
border-radius: 20px;
|
|
4508
|
+
padding: 32px;
|
|
4509
|
+
max-width: 400px;
|
|
4510
|
+
width: 100%;
|
|
4511
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4512
|
+
animation: slideUp 0.4s ease-out;
|
|
4513
|
+
}
|
|
4514
|
+
|
|
4515
|
+
.neoface-email-modal-content h2 {
|
|
4516
|
+
font-size: 24px;
|
|
4517
|
+
font-weight: 700;
|
|
4518
|
+
color: white;
|
|
4519
|
+
margin: 0 0 12px 0;
|
|
4520
|
+
text-align: center;
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
.neoface-email-modal-content p {
|
|
4524
|
+
font-size: 15px;
|
|
4525
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4526
|
+
margin: 0 0 24px 0;
|
|
4527
|
+
text-align: center;
|
|
4528
|
+
line-height: 1.5;
|
|
4529
|
+
}
|
|
4530
|
+
|
|
4531
|
+
.neoface-email-modal-content form {
|
|
4532
|
+
display: flex;
|
|
4533
|
+
flex-direction: column;
|
|
4534
|
+
gap: 16px;
|
|
4535
|
+
margin-bottom: 16px;
|
|
4536
|
+
}
|
|
4537
|
+
|
|
4538
|
+
.neoface-email-modal-content input {
|
|
4539
|
+
width: 100%;
|
|
4540
|
+
padding: 14px 16px;
|
|
4541
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
4542
|
+
border-radius: 12px;
|
|
4543
|
+
font-size: 15px;
|
|
4544
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4545
|
+
color: white;
|
|
4546
|
+
font-family: inherit;
|
|
4547
|
+
transition: all 0.2s;
|
|
4548
|
+
box-sizing: border-box;
|
|
4549
|
+
}
|
|
4550
|
+
|
|
4551
|
+
.neoface-email-modal-content input::placeholder {
|
|
4552
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
.neoface-email-modal-content input:focus {
|
|
4556
|
+
outline: none;
|
|
4557
|
+
border-color: #7c3aed;
|
|
4558
|
+
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2);
|
|
4559
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
.neoface-email-submit-btn {
|
|
4563
|
+
width: 100%;
|
|
4564
|
+
padding: 14px 24px;
|
|
4565
|
+
border-radius: 12px;
|
|
4566
|
+
border: none;
|
|
4567
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4568
|
+
color: white;
|
|
4569
|
+
font-size: 16px;
|
|
4570
|
+
font-weight: 600;
|
|
4571
|
+
cursor: pointer;
|
|
4572
|
+
transition: all 0.2s;
|
|
4573
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4574
|
+
font-family: inherit;
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
.neoface-email-submit-btn:hover {
|
|
4578
|
+
transform: translateY(-2px);
|
|
4579
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
.neoface-email-submit-btn:active {
|
|
4583
|
+
transform: translateY(0);
|
|
4584
|
+
}
|
|
4585
|
+
|
|
4586
|
+
.neoface-email-submit-btn:disabled {
|
|
4587
|
+
opacity: 0.6;
|
|
4588
|
+
cursor: not-allowed;
|
|
4589
|
+
transform: none;
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
.neoface-email-cancel-btn {
|
|
4593
|
+
width: 100%;
|
|
4594
|
+
padding: 12px 24px;
|
|
4595
|
+
border-radius: 12px;
|
|
4596
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4597
|
+
background: transparent;
|
|
4598
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4599
|
+
font-size: 15px;
|
|
4600
|
+
font-weight: 600;
|
|
4601
|
+
cursor: pointer;
|
|
4602
|
+
transition: all 0.2s;
|
|
4603
|
+
font-family: inherit;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
.neoface-email-cancel-btn:hover {
|
|
4607
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4608
|
+
color: white;
|
|
4609
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4610
|
+
}
|
|
4611
|
+
|
|
4612
|
+
.neoface-email-branding {
|
|
4613
|
+
display: flex;
|
|
4614
|
+
align-items: center;
|
|
4615
|
+
justify-content: center;
|
|
4616
|
+
gap: 8px;
|
|
4617
|
+
margin-top: 32px;
|
|
4618
|
+
padding-top: 24px;
|
|
4619
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
4620
|
+
font-size: 13px;
|
|
4621
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4622
|
+
}
|
|
4623
|
+
|
|
4624
|
+
.neoface-email-brand-logo {
|
|
4625
|
+
height: 20px;
|
|
4626
|
+
width: auto;
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4629
|
+
.neoface-email-brand-name {
|
|
4630
|
+
font-weight: 600;
|
|
4631
|
+
font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;
|
|
4632
|
+
color: #ffffff;
|
|
4633
|
+
letter-spacing: 0.3px;
|
|
4634
|
+
}
|
|
4635
|
+
|
|
4636
|
+
@media (max-width: 640px) {
|
|
4637
|
+
.neoface-email-modal-content {
|
|
4638
|
+
padding: 24px;
|
|
4639
|
+
border-radius: 16px;
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4642
|
+
.neoface-email-modal-content h2 {
|
|
4643
|
+
font-size: 20px;
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
`;
|
|
4647
|
+
document.head.appendChild(style);
|
|
4648
|
+
}
|
|
3764
4649
|
async handleSubmit(event) {
|
|
3765
4650
|
event.preventDefault();
|
|
3766
4651
|
const emailInput = this.modalElement.querySelector("#email");
|
|
@@ -3768,7 +4653,9 @@ class EmailPasswordModal {
|
|
|
3768
4653
|
const email = emailInput.value.trim();
|
|
3769
4654
|
const password = passwordInput.value.trim();
|
|
3770
4655
|
if (!email || !password) {
|
|
3771
|
-
this.options.onError(
|
|
4656
|
+
this.options.onError(
|
|
4657
|
+
new NeoFaceError("Email e senha são obrigatórios", ErrorType.VALIDATION_ERROR)
|
|
4658
|
+
);
|
|
3772
4659
|
return;
|
|
3773
4660
|
}
|
|
3774
4661
|
try {
|
|
@@ -3780,6 +4667,230 @@ class EmailPasswordModal {
|
|
|
3780
4667
|
}
|
|
3781
4668
|
}
|
|
3782
4669
|
}
|
|
4670
|
+
const MAX_ATTEMPTS = 3;
|
|
4671
|
+
const RETRY_DELAY = 500;
|
|
4672
|
+
async function detectFaceContinuously(video, minDetectionTime = 3e3, detectionInterval = 200) {
|
|
4673
|
+
return new Promise((resolve) => {
|
|
4674
|
+
let faceDetectedCount = 0;
|
|
4675
|
+
let totalChecks = 0;
|
|
4676
|
+
const requiredChecks = Math.ceil(minDetectionTime / detectionInterval);
|
|
4677
|
+
const minSuccessRate = 0.6;
|
|
4678
|
+
let timeoutId = null;
|
|
4679
|
+
let isResolved = false;
|
|
4680
|
+
const finish = (success) => {
|
|
4681
|
+
if (isResolved)
|
|
4682
|
+
return;
|
|
4683
|
+
isResolved = true;
|
|
4684
|
+
if (timeoutId) {
|
|
4685
|
+
clearTimeout(timeoutId);
|
|
4686
|
+
}
|
|
4687
|
+
resolve(success);
|
|
4688
|
+
};
|
|
4689
|
+
const checkFace = async () => {
|
|
4690
|
+
if (isResolved)
|
|
4691
|
+
return;
|
|
4692
|
+
try {
|
|
4693
|
+
const detection = await faceapi.detectSingleFace(
|
|
4694
|
+
video,
|
|
4695
|
+
new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.4 })
|
|
4696
|
+
);
|
|
4697
|
+
totalChecks++;
|
|
4698
|
+
if (detection) {
|
|
4699
|
+
faceDetectedCount++;
|
|
4700
|
+
}
|
|
4701
|
+
if (totalChecks >= requiredChecks) {
|
|
4702
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4703
|
+
finish(successRate >= minSuccessRate);
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4706
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4707
|
+
} catch (error) {
|
|
4708
|
+
totalChecks++;
|
|
4709
|
+
if (totalChecks >= requiredChecks) {
|
|
4710
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4711
|
+
finish(successRate >= minSuccessRate);
|
|
4712
|
+
} else {
|
|
4713
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
};
|
|
4717
|
+
const safetyTimeout = window.setTimeout(() => {
|
|
4718
|
+
if (!isResolved) {
|
|
4719
|
+
const successRate = totalChecks > 0 ? faceDetectedCount / totalChecks : 0;
|
|
4720
|
+
finish(successRate >= minSuccessRate);
|
|
4721
|
+
}
|
|
4722
|
+
}, minDetectionTime + 1e3);
|
|
4723
|
+
timeoutId = window.setTimeout(() => {
|
|
4724
|
+
checkFace();
|
|
4725
|
+
clearTimeout(safetyTimeout);
|
|
4726
|
+
}, detectionInterval);
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4729
|
+
async function attemptLogin(applicationToken, overlay) {
|
|
4730
|
+
overlay.updateStatus("verifying");
|
|
4731
|
+
const video = document.createElement("video");
|
|
4732
|
+
video.style.position = "fixed";
|
|
4733
|
+
video.style.top = "-9999px";
|
|
4734
|
+
video.style.left = "-9999px";
|
|
4735
|
+
video.style.width = "1px";
|
|
4736
|
+
video.style.height = "1px";
|
|
4737
|
+
video.style.opacity = "0";
|
|
4738
|
+
video.setAttribute("autoplay", "");
|
|
4739
|
+
video.setAttribute("muted", "");
|
|
4740
|
+
video.setAttribute("playsinline", "");
|
|
4741
|
+
document.body.appendChild(video);
|
|
4742
|
+
let stream = null;
|
|
4743
|
+
try {
|
|
4744
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
4745
|
+
video: {
|
|
4746
|
+
width: { ideal: 640 },
|
|
4747
|
+
height: { ideal: 480 },
|
|
4748
|
+
facingMode: "user"
|
|
4749
|
+
},
|
|
4750
|
+
audio: false
|
|
4751
|
+
});
|
|
4752
|
+
video.srcObject = stream;
|
|
4753
|
+
await video.play();
|
|
4754
|
+
await new Promise((resolve) => {
|
|
4755
|
+
if (video.readyState >= 2) {
|
|
4756
|
+
resolve(void 0);
|
|
4757
|
+
} else {
|
|
4758
|
+
video.onloadedmetadata = () => resolve(void 0);
|
|
4759
|
+
}
|
|
4760
|
+
});
|
|
4761
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
4762
|
+
const faceDetected = await detectFaceContinuously(video, 3e3);
|
|
4763
|
+
if (!faceDetected) {
|
|
4764
|
+
if (stream) {
|
|
4765
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4766
|
+
}
|
|
4767
|
+
if (video.parentElement) {
|
|
4768
|
+
video.parentElement.removeChild(video);
|
|
4769
|
+
}
|
|
4770
|
+
throw new NeoFaceError("Rosto não detectado. Posicione seu rosto na frente da câmera.", ErrorType.VALIDATION_ERROR);
|
|
4771
|
+
}
|
|
4772
|
+
const canvas = document.createElement("canvas");
|
|
4773
|
+
canvas.width = video.videoWidth;
|
|
4774
|
+
canvas.height = video.videoHeight;
|
|
4775
|
+
const ctx = canvas.getContext("2d");
|
|
4776
|
+
if (!ctx) {
|
|
4777
|
+
if (stream) {
|
|
4778
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4779
|
+
}
|
|
4780
|
+
if (video.parentElement) {
|
|
4781
|
+
video.parentElement.removeChild(video);
|
|
4782
|
+
}
|
|
4783
|
+
throw new NeoFaceError("Erro ao capturar imagem", ErrorType.CAPTURE_ERROR);
|
|
4784
|
+
}
|
|
4785
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
4786
|
+
if (stream) {
|
|
4787
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4788
|
+
}
|
|
4789
|
+
if (video.parentElement) {
|
|
4790
|
+
video.parentElement.removeChild(video);
|
|
4791
|
+
}
|
|
4792
|
+
const imageBlob = await new Promise((resolve, reject) => {
|
|
4793
|
+
canvas.toBlob(
|
|
4794
|
+
(blob) => {
|
|
4795
|
+
canvas.remove();
|
|
4796
|
+
if (blob) {
|
|
4797
|
+
resolve(blob);
|
|
4798
|
+
} else {
|
|
4799
|
+
reject(new Error("Failed to capture image"));
|
|
4800
|
+
}
|
|
4801
|
+
},
|
|
4802
|
+
"image/jpeg",
|
|
4803
|
+
0.85
|
|
4804
|
+
);
|
|
4805
|
+
});
|
|
4806
|
+
const result = await loginWithBiometric$1(imageBlob, applicationToken);
|
|
4807
|
+
if (!result.success) {
|
|
4808
|
+
throw new NeoFaceError("Login falhou", ErrorType.LOGIN_FAILED);
|
|
4809
|
+
}
|
|
4810
|
+
return result;
|
|
4811
|
+
} catch (error) {
|
|
4812
|
+
if (stream) {
|
|
4813
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4814
|
+
}
|
|
4815
|
+
if (video.parentElement) {
|
|
4816
|
+
video.parentElement.removeChild(video);
|
|
4817
|
+
}
|
|
4818
|
+
if (error instanceof NeoFaceError) {
|
|
4819
|
+
throw error;
|
|
4820
|
+
}
|
|
4821
|
+
throw new NeoFaceError(
|
|
4822
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4823
|
+
ErrorType.UNKNOWN
|
|
4824
|
+
);
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
async function executeBiometricLoginFlow(options) {
|
|
4828
|
+
const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel } = options;
|
|
4829
|
+
const overlay = new BiometricStatusOverlay();
|
|
4830
|
+
const fallbackPrompt = new FallbackPrompt();
|
|
4831
|
+
let attempts = 0;
|
|
4832
|
+
let lastError;
|
|
4833
|
+
try {
|
|
4834
|
+
const MODEL_URL = "https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model";
|
|
4835
|
+
await Promise.all([
|
|
4836
|
+
faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
|
|
4837
|
+
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)
|
|
4838
|
+
]);
|
|
4839
|
+
} catch (error) {
|
|
4840
|
+
console.warn("Face-api.js models not loaded, continuing without face detection");
|
|
4841
|
+
}
|
|
4842
|
+
overlay.show("preparing");
|
|
4843
|
+
const tryLogin = async () => {
|
|
4844
|
+
try {
|
|
4845
|
+
attempts++;
|
|
4846
|
+
const result = await attemptLogin(applicationToken, overlay);
|
|
4847
|
+
overlay.updateStatus("success");
|
|
4848
|
+
setTimeout(() => {
|
|
4849
|
+
overlay.close();
|
|
4850
|
+
onSuccess(result);
|
|
4851
|
+
}, 800);
|
|
4852
|
+
} catch (error) {
|
|
4853
|
+
lastError = error instanceof NeoFaceError ? error : new NeoFaceError(
|
|
4854
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4855
|
+
ErrorType.UNKNOWN
|
|
4856
|
+
);
|
|
4857
|
+
overlay.updateStatus("error");
|
|
4858
|
+
if (attempts < MAX_ATTEMPTS) {
|
|
4859
|
+
setTimeout(() => {
|
|
4860
|
+
tryLogin();
|
|
4861
|
+
}, RETRY_DELAY);
|
|
4862
|
+
} else {
|
|
4863
|
+
setTimeout(() => {
|
|
4864
|
+
overlay.close();
|
|
4865
|
+
fallbackPrompt.show(
|
|
4866
|
+
lastError,
|
|
4867
|
+
() => {
|
|
4868
|
+
attempts = 0;
|
|
4869
|
+
overlay.show("preparing");
|
|
4870
|
+
setTimeout(() => tryLogin(), 500);
|
|
4871
|
+
},
|
|
4872
|
+
() => {
|
|
4873
|
+
if (onFallbackRequest) {
|
|
4874
|
+
onFallbackRequest();
|
|
4875
|
+
} else {
|
|
4876
|
+
onError(
|
|
4877
|
+
new NeoFaceError(
|
|
4878
|
+
"Por favor, use email e senha para fazer login",
|
|
4879
|
+
ErrorType.LOGIN_FAILED
|
|
4880
|
+
)
|
|
4881
|
+
);
|
|
4882
|
+
}
|
|
4883
|
+
},
|
|
4884
|
+
onCancel
|
|
4885
|
+
);
|
|
4886
|
+
}, 1e3);
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
};
|
|
4890
|
+
setTimeout(() => {
|
|
4891
|
+
tryLogin();
|
|
4892
|
+
}, 300);
|
|
4893
|
+
}
|
|
3783
4894
|
async function startFaceLogin(options) {
|
|
3784
4895
|
try {
|
|
3785
4896
|
const isValidToken = await validateToken(options.applicationToken);
|
|
@@ -3787,25 +4898,23 @@ async function startFaceLogin(options) {
|
|
|
3787
4898
|
options.onError(new NeoFaceError("Token de aplicação inválido", ErrorType.INVALID_TOKEN));
|
|
3788
4899
|
return;
|
|
3789
4900
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
onSuccess:
|
|
3793
|
-
try {
|
|
3794
|
-
const imageBlob = await fetch(imageData).then((res) => res.blob());
|
|
3795
|
-
const result = await loginWithBiometric(imageBlob, options.applicationToken);
|
|
3796
|
-
options.onSuccess(result);
|
|
3797
|
-
} catch (error) {
|
|
3798
|
-
options.onError(error);
|
|
3799
|
-
}
|
|
3800
|
-
},
|
|
4901
|
+
await executeBiometricLoginFlow({
|
|
4902
|
+
applicationToken: options.applicationToken,
|
|
4903
|
+
onSuccess: options.onSuccess,
|
|
3801
4904
|
onError: options.onError,
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
4905
|
+
onFallbackRequest: options.onFallbackRequest || (() => {
|
|
4906
|
+
const fallbackOptions = {
|
|
4907
|
+
applicationToken: options.applicationToken,
|
|
4908
|
+
onSuccess: options.onSuccess,
|
|
4909
|
+
onError: options.onError,
|
|
4910
|
+
title: "Login Alternativo",
|
|
4911
|
+
subtitle: "Biometria não reconhecida. Por favor, informe email e senha."
|
|
4912
|
+
};
|
|
4913
|
+
const modal = new EmailPasswordModal(fallbackOptions);
|
|
4914
|
+
modal.open();
|
|
4915
|
+
}),
|
|
4916
|
+
onCancel: options.onCancel
|
|
4917
|
+
});
|
|
3809
4918
|
} catch (error) {
|
|
3810
4919
|
options.onError(
|
|
3811
4920
|
new NeoFaceError(
|
|
@@ -4109,8 +5218,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
4109
5218
|
initializeBiometricDetection,
|
|
4110
5219
|
isAdvancedDetectionAvailable
|
|
4111
5220
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
4112
|
-
const VERSION = "1.
|
|
4113
|
-
const RELEASE_DATE = "2025-11-
|
|
5221
|
+
const VERSION = "1.4.2";
|
|
5222
|
+
const RELEASE_DATE = "2025-11-23";
|
|
4114
5223
|
class OnboardingCaptureModal {
|
|
4115
5224
|
constructor(options) {
|
|
4116
5225
|
__publicField(this, "overlay", null);
|
|
@@ -4376,8 +5485,8 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
|
|
|
4376
5485
|
const modalElement = require$$0$1.createElement(BiometricRegistrationModal, {
|
|
4377
5486
|
personData,
|
|
4378
5487
|
applicationToken,
|
|
4379
|
-
useRealApi: (options == null ? void 0 : options.useRealApi)
|
|
4380
|
-
//
|
|
5488
|
+
useRealApi: (options == null ? void 0 : options.useRealApi) ?? true,
|
|
5489
|
+
// Sempre usa API real em produção
|
|
4381
5490
|
onClose: cleanup,
|
|
4382
5491
|
onSuccess: (result) => {
|
|
4383
5492
|
cleanup();
|
|
@@ -4396,20 +5505,25 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
|
|
|
4396
5505
|
});
|
|
4397
5506
|
}
|
|
4398
5507
|
export {
|
|
4399
|
-
BiometricCaptureModal,
|
|
5508
|
+
BiometricCaptureModal$1 as BiometricCaptureModal,
|
|
4400
5509
|
BiometricRegistrationModal,
|
|
5510
|
+
BiometricStatusOverlay,
|
|
5511
|
+
EmailPasswordModal,
|
|
4401
5512
|
ErrorType,
|
|
4402
5513
|
FaceCaptureModal,
|
|
5514
|
+
FallbackPrompt,
|
|
4403
5515
|
NeoFaceError,
|
|
4404
5516
|
RELEASE_DATE,
|
|
4405
5517
|
VERSION,
|
|
4406
5518
|
biometricLogin,
|
|
4407
5519
|
biometricLoginWithFallback,
|
|
5520
|
+
completeOnboarding,
|
|
5521
|
+
completeOnboardingWithData,
|
|
4408
5522
|
detectBiometricType,
|
|
4409
5523
|
identifyPerson,
|
|
4410
5524
|
initializeBiometricDetection,
|
|
4411
5525
|
isAdvancedDetectionAvailable,
|
|
4412
|
-
loginWithBiometric,
|
|
5526
|
+
loginWithBiometric$1 as loginWithBiometric,
|
|
4413
5527
|
recognize,
|
|
4414
5528
|
recognizeBiometric,
|
|
4415
5529
|
recognizeByPurpose,
|
|
@@ -4422,6 +5536,7 @@ export {
|
|
|
4422
5536
|
startFaceLogin,
|
|
4423
5537
|
startHandLogin,
|
|
4424
5538
|
startOnboarding,
|
|
5539
|
+
validateOnboardingToken,
|
|
4425
5540
|
validateToken
|
|
4426
5541
|
};
|
|
4427
5542
|
//# sourceMappingURL=neoface-id-sdk.es.js.map
|