FlotillasGPS - Sistema completo de monitoreo de flotillas GPS

Sistema completo para monitoreo y gestion de flotas de vehiculos con:
- Backend FastAPI con PostgreSQL/TimescaleDB
- Frontend React con TypeScript y TailwindCSS
- App movil React Native con Expo
- Soporte para dispositivos GPS, Meshtastic y celulares
- Video streaming en vivo con MediaMTX
- Geocercas, alertas, viajes y reportes
- Autenticacion JWT y WebSockets en tiempo real

Documentacion completa y guias de usuario incluidas.
This commit is contained in:
FlotillasGPS Developer
2026-01-21 08:18:00 +00:00
commit 51d78bacf4
248 changed files with 50171 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
/**
* Servicio de almacenamiento local usando AsyncStorage
* Wrapper con tipado y manejo de errores
*/
import AsyncStorage from '@react-native-async-storage/async-storage';
// Claves de almacenamiento
export const STORAGE_KEYS = {
AUTH_TOKEN: '@adan/auth_token',
CONDUCTOR: '@adan/conductor',
DISPOSITIVO: '@adan/dispositivo',
UBICACIONES_OFFLINE: '@adan/ubicaciones_offline',
VIAJE_ACTIVO: '@adan/viaje_activo',
CONFIGURACION: '@adan/configuracion',
ULTIMO_SYNC: '@adan/ultimo_sync',
MENSAJES_PENDIENTES: '@adan/mensajes_pendientes',
PARADAS_PENDIENTES: '@adan/paradas_pendientes',
COMBUSTIBLE_PENDIENTE: '@adan/combustible_pendiente',
} as const;
type StorageKey = typeof STORAGE_KEYS[keyof typeof STORAGE_KEYS];
class StorageService {
/**
* Guarda un valor en el almacenamiento
*/
async set<T>(key: StorageKey, value: T): Promise<void> {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
} catch (error) {
console.error(`Error guardando ${key}:`, error);
throw new Error(`No se pudo guardar en ${key}`);
}
}
/**
* Obtiene un valor del almacenamiento
*/
async get<T>(key: StorageKey): Promise<T | null> {
try {
const jsonValue = await AsyncStorage.getItem(key);
if (jsonValue === null) {
return null;
}
return JSON.parse(jsonValue) as T;
} catch (error) {
console.error(`Error leyendo ${key}:`, error);
return null;
}
}
/**
* Elimina un valor del almacenamiento
*/
async remove(key: StorageKey): Promise<void> {
try {
await AsyncStorage.removeItem(key);
} catch (error) {
console.error(`Error eliminando ${key}:`, error);
throw new Error(`No se pudo eliminar ${key}`);
}
}
/**
* Elimina múltiples valores
*/
async removeMultiple(keys: StorageKey[]): Promise<void> {
try {
await AsyncStorage.multiRemove(keys);
} catch (error) {
console.error('Error eliminando múltiples claves:', error);
throw new Error('No se pudieron eliminar las claves');
}
}
/**
* Obtiene múltiples valores
*/
async getMultiple<T extends Record<string, unknown>>(
keys: StorageKey[]
): Promise<Partial<T>> {
try {
const pairs = await AsyncStorage.multiGet(keys);
const result: Record<string, unknown> = {};
pairs.forEach(([key, value]) => {
if (value !== null) {
try {
result[key] = JSON.parse(value);
} catch {
result[key] = value;
}
}
});
return result as Partial<T>;
} catch (error) {
console.error('Error leyendo múltiples claves:', error);
return {};
}
}
/**
* Guarda múltiples valores
*/
async setMultiple(data: Array<[StorageKey, unknown]>): Promise<void> {
try {
const pairs: Array<[string, string]> = data.map(([key, value]) => [
key,
JSON.stringify(value),
]);
await AsyncStorage.multiSet(pairs);
} catch (error) {
console.error('Error guardando múltiples valores:', error);
throw new Error('No se pudieron guardar los valores');
}
}
/**
* Limpia todo el almacenamiento de la app
*/
async clearAll(): Promise<void> {
try {
const keys = await AsyncStorage.getAllKeys();
const adanKeys = keys.filter((key) => key.startsWith('@adan/'));
await AsyncStorage.multiRemove(adanKeys);
} catch (error) {
console.error('Error limpiando almacenamiento:', error);
throw new Error('No se pudo limpiar el almacenamiento');
}
}
/**
* Verifica si existe una clave
*/
async exists(key: StorageKey): Promise<boolean> {
const value = await this.get(key);
return value !== null;
}
/**
* Añade un elemento a un array existente
*/
async appendToArray<T>(key: StorageKey, item: T): Promise<void> {
const existing = await this.get<T[]>(key);
const array = existing || [];
array.push(item);
await this.set(key, array);
}
/**
* Elimina un elemento de un array por predicado
*/
async removeFromArray<T>(
key: StorageKey,
predicate: (item: T) => boolean
): Promise<void> {
const existing = await this.get<T[]>(key);
if (existing) {
const filtered = existing.filter((item) => !predicate(item));
await this.set(key, filtered);
}
}
/**
* Actualiza un elemento en un array
*/
async updateInArray<T extends { id: string }>(
key: StorageKey,
id: string,
updates: Partial<T>
): Promise<void> {
const existing = await this.get<T[]>(key);
if (existing) {
const updated = existing.map((item) =>
item.id === id ? { ...item, ...updates } : item
);
await this.set(key, updated);
}
}
/**
* Obtiene el tamaño aproximado del almacenamiento
*/
async getStorageSize(): Promise<number> {
try {
const keys = await AsyncStorage.getAllKeys();
const adanKeys = keys.filter((key) => key.startsWith('@adan/'));
const pairs = await AsyncStorage.multiGet(adanKeys);
let totalSize = 0;
pairs.forEach(([key, value]) => {
totalSize += key.length + (value?.length || 0);
});
return totalSize;
} catch (error) {
console.error('Error calculando tamaño:', error);
return 0;
}
}
}
export const storage = new StorageService();
export default storage;