Fix usuarios page #6
@@ -10,6 +10,7 @@
|
||||
import NotificationBell from "$lib/components/chat/NotificationBell.svelte";
|
||||
import ChatWidget from "$lib/components/chat/ChatWidget.svelte";
|
||||
import PresenceManager from "$lib/components/chat/PresenceManager.svelte";
|
||||
import { getBrowserInfo } from "$lib/utils/browserInfo";
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
|
||||
@@ -100,9 +101,14 @@
|
||||
carregandoLogin = true;
|
||||
|
||||
try {
|
||||
// Capturar informações do navegador
|
||||
const browserInfo = await getBrowserInfo();
|
||||
|
||||
const resultado = await convex.mutation(api.autenticacao.login, {
|
||||
matriculaOuEmail: matricula.trim(),
|
||||
senha: senha,
|
||||
userAgent: browserInfo.userAgent || undefined,
|
||||
ipAddress: browserInfo.ipAddress,
|
||||
});
|
||||
|
||||
if (resultado.sucesso) {
|
||||
|
||||
98
apps/web/src/lib/utils/browserInfo.ts
Normal file
98
apps/web/src/lib/utils/browserInfo.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Função utilitária para obter informações do navegador
|
||||
* Sem usar APIs externas
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtém o User-Agent do navegador
|
||||
*/
|
||||
export function getUserAgent(): string {
|
||||
if (typeof window === 'undefined' || !window.navigator) {
|
||||
return '';
|
||||
}
|
||||
return window.navigator.userAgent || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenta obter o IP local usando WebRTC
|
||||
* Esta função não usa API externa, mas pode falhar em alguns navegadores
|
||||
* Retorna undefined se não conseguir obter
|
||||
*/
|
||||
export async function getLocalIP(): Promise<string | undefined> {
|
||||
return new Promise((resolve) => {
|
||||
// Verificar se está em ambiente browser
|
||||
if (typeof window === 'undefined' || typeof RTCPeerConnection === 'undefined') {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: []
|
||||
});
|
||||
|
||||
let resolved = false;
|
||||
const timeout = setTimeout(() => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
pc.close();
|
||||
resolve(undefined);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
pc.onicecandidate = (event) => {
|
||||
if (event.candidate && !resolved) {
|
||||
const candidate = event.candidate.candidate;
|
||||
// Regex para extrair IP local (IPv4)
|
||||
const ipMatch = candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3})/);
|
||||
if (ipMatch && ipMatch[1]) {
|
||||
const ip = ipMatch[1];
|
||||
// Verificar se não é IP localhost (127.0.0.1 ou ::1)
|
||||
if (!ip.startsWith('127.') && !ip.startsWith('::1')) {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
resolve(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Criar um data channel para forçar a criação de candidatos
|
||||
pc.createDataChannel('');
|
||||
pc.createOffer()
|
||||
.then((offer) => pc.setLocalDescription(offer))
|
||||
.catch(() => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém informações completas do navegador
|
||||
*/
|
||||
export interface BrowserInfo {
|
||||
userAgent: string;
|
||||
ipAddress?: string;
|
||||
}
|
||||
|
||||
export async function getBrowserInfo(): Promise<BrowserInfo> {
|
||||
const userAgent = getUserAgent();
|
||||
const ipAddress = await getLocalIP();
|
||||
|
||||
return {
|
||||
userAgent,
|
||||
ipAddress,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,26 +37,182 @@ export async function registrarLogin(
|
||||
|
||||
// Helpers para extrair informações do userAgent
|
||||
function extrairDevice(userAgent: string): string {
|
||||
if (/mobile/i.test(userAgent)) return "Mobile";
|
||||
if (/tablet/i.test(userAgent)) return "Tablet";
|
||||
const ua = userAgent.toLowerCase();
|
||||
|
||||
// Detectar dispositivos móveis primeiro
|
||||
if (/mobile|android|iphone|ipod|blackberry|opera mini|iemobile|wpdesktop/i.test(ua)) {
|
||||
// Verificar se é tablet
|
||||
if (/ipad|tablet|playbook|silk|(android(?!.*mobile))/i.test(ua)) {
|
||||
return "Tablet";
|
||||
}
|
||||
return "Mobile";
|
||||
}
|
||||
|
||||
// Detectar outros dispositivos
|
||||
if (/smart-tv|smarttv|googletv|appletv|roku|chromecast/i.test(ua)) {
|
||||
return "Smart TV";
|
||||
}
|
||||
|
||||
if (/watch|wear/i.test(ua)) {
|
||||
return "Smart Watch";
|
||||
}
|
||||
|
||||
// Padrão: Desktop
|
||||
return "Desktop";
|
||||
}
|
||||
|
||||
function extrairBrowser(userAgent: string): string {
|
||||
if (/edg/i.test(userAgent)) return "Edge";
|
||||
if (/chrome/i.test(userAgent)) return "Chrome";
|
||||
if (/firefox/i.test(userAgent)) return "Firefox";
|
||||
if (/safari/i.test(userAgent)) return "Safari";
|
||||
if (/opera/i.test(userAgent)) return "Opera";
|
||||
const ua = userAgent.toLowerCase();
|
||||
|
||||
// Ordem de detecção é importante (Edge deve vir antes de Chrome)
|
||||
if (/edgios/i.test(ua)) {
|
||||
return "Edge iOS";
|
||||
}
|
||||
|
||||
if (/edg/i.test(ua)) {
|
||||
// Extrair versão do Edge
|
||||
const match = ua.match(/edg[e\/]([\d.]+)/i);
|
||||
return match ? `Edge ${match[1]}` : "Edge";
|
||||
}
|
||||
|
||||
if (/opr|opera/i.test(ua)) {
|
||||
const match = ua.match(/(?:opr|opera)[\/\s]([\d.]+)/i);
|
||||
return match ? `Opera ${match[1]}` : "Opera";
|
||||
}
|
||||
|
||||
if (/chrome|crios/i.test(ua) && !/edg|opr|opera/i.test(ua)) {
|
||||
const match = ua.match(/chrome[/\s]([\d.]+)/i);
|
||||
return match ? `Chrome ${match[1]}` : "Chrome";
|
||||
}
|
||||
|
||||
if (/firefox|fxios/i.test(ua)) {
|
||||
const match = ua.match(/firefox[/\s]([\d.]+)/i);
|
||||
return match ? `Firefox ${match[1]}` : "Firefox";
|
||||
}
|
||||
|
||||
if (/safari/i.test(ua) && !/chrome|crios|android/i.test(ua)) {
|
||||
const match = ua.match(/version[/\s]([\d.]+)/i);
|
||||
return match ? `Safari ${match[1]}` : "Safari";
|
||||
}
|
||||
|
||||
if (/msie|trident/i.test(ua)) {
|
||||
const match = ua.match(/(?:msie |rv:)([\d.]+)/i);
|
||||
return match ? `Internet Explorer ${match[1]}` : "Internet Explorer";
|
||||
}
|
||||
|
||||
if (/samsungbrowser/i.test(ua)) {
|
||||
return "Samsung Internet";
|
||||
}
|
||||
|
||||
if (/ucbrowser/i.test(ua)) {
|
||||
return "UC Browser";
|
||||
}
|
||||
|
||||
if (/micromessenger/i.test(ua)) {
|
||||
return "WeChat";
|
||||
}
|
||||
|
||||
if (/baiduboxapp/i.test(ua)) {
|
||||
return "Baidu Browser";
|
||||
}
|
||||
|
||||
return "Desconhecido";
|
||||
}
|
||||
|
||||
function extrairSistema(userAgent: string): string {
|
||||
if (/windows/i.test(userAgent)) return "Windows";
|
||||
if (/mac/i.test(userAgent)) return "MacOS";
|
||||
if (/linux/i.test(userAgent)) return "Linux";
|
||||
if (/android/i.test(userAgent)) return "Android";
|
||||
if (/ios/i.test(userAgent)) return "iOS";
|
||||
const ua = userAgent.toLowerCase();
|
||||
|
||||
// Windows
|
||||
if (/windows nt 10.0/i.test(ua)) {
|
||||
return "Windows 10/11";
|
||||
}
|
||||
if (/windows nt 6.3/i.test(ua)) {
|
||||
return "Windows 8.1";
|
||||
}
|
||||
if (/windows nt 6.2/i.test(ua)) {
|
||||
return "Windows 8";
|
||||
}
|
||||
if (/windows nt 6.1/i.test(ua)) {
|
||||
return "Windows 7";
|
||||
}
|
||||
if (/windows nt 6.0/i.test(ua)) {
|
||||
return "Windows Vista";
|
||||
}
|
||||
if (/windows nt 5.1/i.test(ua)) {
|
||||
return "Windows XP";
|
||||
}
|
||||
if (/windows/i.test(ua)) {
|
||||
return "Windows";
|
||||
}
|
||||
|
||||
// macOS
|
||||
if (/macintosh|mac os x/i.test(ua)) {
|
||||
const match = ua.match(/mac os x ([\d_]+)/i);
|
||||
if (match) {
|
||||
const version = match[1].replace(/_/g, '.');
|
||||
return `macOS ${version}`;
|
||||
}
|
||||
return "macOS";
|
||||
}
|
||||
|
||||
// iOS
|
||||
if (/iphone|ipad|ipod/i.test(ua)) {
|
||||
const match = ua.match(/os ([\d_]+)/i);
|
||||
if (match) {
|
||||
const version = match[1].replace(/_/g, '.');
|
||||
return `iOS ${version}`;
|
||||
}
|
||||
return "iOS";
|
||||
}
|
||||
|
||||
// Android
|
||||
if (/android/i.test(ua)) {
|
||||
const match = ua.match(/android ([\d.]+)/i);
|
||||
if (match) {
|
||||
return `Android ${match[1]}`;
|
||||
}
|
||||
return "Android";
|
||||
}
|
||||
|
||||
// Linux
|
||||
if (/linux/i.test(ua)) {
|
||||
// Tentar identificar distribuição
|
||||
if (/ubuntu/i.test(ua)) {
|
||||
return "Ubuntu";
|
||||
}
|
||||
if (/debian/i.test(ua)) {
|
||||
return "Debian";
|
||||
}
|
||||
if (/fedora/i.test(ua)) {
|
||||
return "Fedora";
|
||||
}
|
||||
if (/centos/i.test(ua)) {
|
||||
return "CentOS";
|
||||
}
|
||||
if (/redhat/i.test(ua)) {
|
||||
return "Red Hat";
|
||||
}
|
||||
if (/suse/i.test(ua)) {
|
||||
return "SUSE";
|
||||
}
|
||||
return "Linux";
|
||||
}
|
||||
|
||||
// Chrome OS
|
||||
if (/cros/i.test(ua)) {
|
||||
return "Chrome OS";
|
||||
}
|
||||
|
||||
// BlackBerry
|
||||
if (/blackberry/i.test(ua)) {
|
||||
return "BlackBerry OS";
|
||||
}
|
||||
|
||||
// Windows Phone
|
||||
if (/windows phone/i.test(ua)) {
|
||||
return "Windows Phone";
|
||||
}
|
||||
|
||||
return "Desconhecido";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user