Files
ATLAS/mobile/src/services/storage.ts
ATLAS Admin e59aa2a742 feat: Complete ATLAS system installation and API fixes
## Backend Changes
- Add new API endpoints: combustible, pois, mantenimiento, video, configuracion
- Fix vehiculos endpoint to return paginated response with items array
- Add /vehiculos/all endpoint for non-paginated list
- Add /geocercas/all endpoint
- Add /alertas/configuracion GET/PUT endpoints
- Add /viajes/activos and /viajes/iniciar endpoints
- Add /reportes/stats, /reportes/templates, /reportes/preview endpoints
- Add /conductores/all and /conductores/disponibles endpoints
- Update router.py to include all new modules

## Frontend Changes
- Fix authentication token handling (snake_case vs camelCase)
- Update vehiculosApi.listAll to use /vehiculos/all
- Fix FuelGauge component usage in Combustible page
- Fix chart component exports (named + default exports)
- Update API client for proper token refresh

## Infrastructure
- Rename services from ADAN to ATLAS
- Configure Cloudflare tunnel for atlas.consultoria-as.com
- Update systemd service files
- Configure PostgreSQL with TimescaleDB
- Configure Redis, Mosquitto, Traccar, MediaMTX

## Documentation
- Update installation guides
- Update API reference
- Rename all ADAN references to ATLAS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 03:04:23 +00:00

208 lines
5.4 KiB
TypeScript

/**
* 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: '@atlas/auth_token',
CONDUCTOR: '@atlas/conductor',
DISPOSITIVO: '@atlas/dispositivo',
UBICACIONES_OFFLINE: '@atlas/ubicaciones_offline',
VIAJE_ACTIVO: '@atlas/viaje_activo',
CONFIGURACION: '@atlas/configuracion',
ULTIMO_SYNC: '@atlas/ultimo_sync',
MENSAJES_PENDIENTES: '@atlas/mensajes_pendientes',
PARADAS_PENDIENTES: '@atlas/paradas_pendientes',
COMBUSTIBLE_PENDIENTE: '@atlas/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 atlasKeys = keys.filter((key) => key.startsWith('@atlas/'));
await AsyncStorage.multiRemove(atlasKeys);
} 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 atlasKeys = keys.filter((key) => key.startsWith('@atlas/'));
const pairs = await AsyncStorage.multiGet(atlasKeys);
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;