Fix usuarios page #6

Merged
killer-cf merged 28 commits from fix-usuarios-page into master 2025-11-04 17:42:21 +00:00
68 changed files with 9663 additions and 7127 deletions
Showing only changes of commit f278ad4d17 - Show all commits

View File

@@ -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) {

View 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,
};
}

View File

@@ -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";
}