Archive: System v4 - Estado al 2024-12-24
This commit is contained in:
32
v4-archive/packet/CHANGELOG.md
Normal file
32
v4-archive/packet/CHANGELOG.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## [1.7.4] - 2025-12-23
|
||||
|
||||
### Added
|
||||
- Permisos completos de Android en AndroidManifest.xml:
|
||||
- INTERNET, ACCESS_NETWORK_STATE
|
||||
- CAMERA, RECORD_AUDIO
|
||||
- ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
|
||||
- READ_MEDIA_IMAGES/VIDEO/AUDIO (Android 13+)
|
||||
- READ_EXTERNAL_STORAGE (Android ≤12)
|
||||
- Configuración `android:usesCleartextTraffic="true"` para HTTP
|
||||
|
||||
### Build Info
|
||||
- Flutter 3.38.5 (stable)
|
||||
- Dart 3.10.4
|
||||
- Java OpenJDK 17.0.17
|
||||
- APK Size: 49 MB
|
||||
- SHA-256: `7f57bee3edec7342d6ca78d789704fec3827e3bf85f11ac362dcbacc8371ef1a`
|
||||
|
||||
## [1.0.0] - 2025-12-21
|
||||
|
||||
### Added
|
||||
- Release inicial
|
||||
- 5 pantallas: Captura, Etiquetas, Packs, Pendientes, Config
|
||||
- Arquitectura Clean con BLoC
|
||||
- Soporte offline con cola de reintentos
|
||||
- Integración con backend via API REST
|
||||
- Sistema de etiquetas desde bibliotecas remotas
|
||||
- Captura multimedia (fotos, video, audio, archivos)
|
||||
- GPS location
|
||||
- Hash SHA-256 para contenedores
|
||||
171
v4-archive/packet/README.md
Normal file
171
v4-archive/packet/README.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Packet
|
||||
|
||||

|
||||
|
||||
|
||||
App móvil multiplataforma (iOS/Android) para capturar contenido multimedia, etiquetarlo con hashes y enviarlo a backends configurables.
|
||||
|
||||
## Principios
|
||||
|
||||
| Principio | Descripción |
|
||||
|-----------|-------------|
|
||||
| Zero-retention | Los archivos nunca se almacenan en disco, solo en RAM hasta envío |
|
||||
| Hash-first | Todo contenedor tiene hash SHA-256 único generado localmente |
|
||||
| Contenedor cerrado | Una vez montado, no se inspecciona. Solo enviar o desmontar |
|
||||
| App tonta | No valida coherencia de etiquetas, solo formato. El backend decide |
|
||||
| Offline-capable | Captura sin conexión, cola de reintentos cuando hay red |
|
||||
|
||||
## Stack Tecnológico
|
||||
|
||||
| Componente | Tecnología |
|
||||
|------------|------------|
|
||||
| Framework | Flutter 3.x |
|
||||
| Lenguaje | Dart |
|
||||
| Estado | flutter_bloc |
|
||||
| DB Local | sqflite |
|
||||
| HTTP | dio |
|
||||
| Crypto | crypto (SHA-256) |
|
||||
| Media | image_picker, record |
|
||||
| Location | geolocator |
|
||||
| Permisos | permission_handler |
|
||||
|
||||
## Pantallas
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [Mi DECK ▼] [CORP] [+Ext] │ ← Destinos (superior)
|
||||
├─────────────────────────────────────────┤
|
||||
│ CONTENIDO PANTALLA │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 📷 🏷️ 📦 ⏳ ⚙️ │ ← Navegación (inferior)
|
||||
│ Captura Tags Packs Pend. Config │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Captura
|
||||
Montar contenedor con contenido multimedia: título, descripción, archivos, GPS, etiquetas.
|
||||
|
||||
### Etiquetas
|
||||
Seleccionar etiquetas de bibliotecas HST/DECK y añadir hashes externos.
|
||||
|
||||
### Packs
|
||||
Conjuntos predefinidos de etiquetas para aplicar con 1 tap.
|
||||
|
||||
### Pendientes
|
||||
Cola de contenedores fallidos (máx 20). App se bloquea si se llena.
|
||||
|
||||
### Config
|
||||
URLs, llaves de autenticación y bibliotecas de iconos.
|
||||
|
||||
Bibliotecas con indicador visual:
|
||||
- 🔓 Pública (sin PIN)
|
||||
- 🔒 Privada (requiere PIN)
|
||||
|
||||
## Estructura del Proyecto
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/
|
||||
│ ├── constants/ # Constantes de la app
|
||||
│ ├── errors/ # Excepciones personalizadas
|
||||
│ ├── utils/ # Utilidades (hash, retry)
|
||||
│ └── theme/ # Tema de la app
|
||||
├── data/
|
||||
│ ├── datasources/ # APIs y BD local
|
||||
│ └── repositories/ # Repositorios
|
||||
├── domain/
|
||||
│ └── entities/ # Modelos de dominio
|
||||
└── presentation/
|
||||
├── bloc/ # Cubits de estado
|
||||
├── pages/ # Pantallas
|
||||
└── widgets/ # Widgets reutilizables
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Instalar dependencias
|
||||
flutter pub get
|
||||
|
||||
# Build APK
|
||||
flutter build apk --release
|
||||
|
||||
# Build iOS (requiere Xcode)
|
||||
flutter build ios --release
|
||||
```
|
||||
|
||||
## APIs
|
||||
|
||||
### Backend (envío)
|
||||
```http
|
||||
POST {destino.url}/ingest
|
||||
X-Auth-Key: {destino.hash}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Bibliotecas (etiquetas)
|
||||
```http
|
||||
GET {biblioteca.url}/api/biblioteca
|
||||
X-HSU-PIN: {pin} # Solo para bibliotecas privadas
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"nombre": "Mi Biblioteca",
|
||||
"requiere_pin": true,
|
||||
"tags": [...]
|
||||
}
|
||||
```
|
||||
|
||||
## Base de Datos Local
|
||||
|
||||
SQLite v4 con las siguientes tablas principales:
|
||||
|
||||
**bibliotecas:**
|
||||
| Campo | Tipo | Descripción |
|
||||
|-------|------|-------------|
|
||||
| id | INTEGER | PK autoincrement |
|
||||
| nombre | TEXT | Nombre de la biblioteca |
|
||||
| url | TEXT | URL del servidor |
|
||||
| hash | TEXT | Hash de identificación |
|
||||
| pin | TEXT | PIN para bibliotecas privadas (nullable) |
|
||||
| requiere_pin | INTEGER | 0=pública, 1=privada |
|
||||
|
||||
## Lógica de Reintentos
|
||||
|
||||
20 intentos en 72 horas con backoff exponencial (1min → 8h).
|
||||
|
||||
## Referencias
|
||||
|
||||
- Biblioteca HST: https://tzrtech.org
|
||||
- Especificación completa: [docs/SPEC.md](docs/SPEC.md)
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.7.3 (2025-12-22)
|
||||
- Fix: Añadido permiso INTERNET en AndroidManifest.xml
|
||||
- Fix: Mejor manejo de errores de conexión
|
||||
|
||||
### v1.7.2 (2025-12-22)
|
||||
- Fix: Diálogo de PIN se cierra correctamente al cancelar
|
||||
- Fix: Error de conexión muestra mensaje amigable
|
||||
|
||||
### v1.7.1 (2025-12-22)
|
||||
- Fix: PIN se guarda correctamente en BD local
|
||||
|
||||
### v1.7.0 (2025-12-22)
|
||||
- Feat: Sistema de PIN para bibliotecas privadas
|
||||
- Feat: Header `X-HSU-PIN` para autenticación
|
||||
- Feat: Indicadores visuales 🔒/🔓 para tipo de biblioteca
|
||||
- Feat: Diálogo de PIN al conectar biblioteca privada
|
||||
- Change: Endpoint de bibliotecas ahora es `/api/biblioteca`
|
||||
|
||||
### v1.6.0 (2025-12-22)
|
||||
- Feat: Diálogo simplificado para añadir biblioteca (solo URL)
|
||||
- Change: Nombre y hash se obtienen automáticamente del servidor
|
||||
|
||||
### v1.0.0
|
||||
- Release inicial
|
||||
151
v4-archive/packet/docs/BUILD-v1.7.4.md
Normal file
151
v4-archive/packet/docs/BUILD-v1.7.4.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Packet v1.7.4 - Build Report
|
||||
|
||||
**Fecha:** 2025-12-23
|
||||
**Compilado por:** Claude Code
|
||||
**Plataforma:** macOS Darwin 25.1.0
|
||||
|
||||
## Artefacto
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| Archivo | `packet-v1.7.4.apk` |
|
||||
| Tamaño | 49 MB |
|
||||
| SHA-256 | `7f57bee3edec7342d6ca78d789704fec3827e3bf85f11ac362dcbacc8371ef1a` |
|
||||
| Firmado | Debug keys |
|
||||
|
||||
## Entorno de Compilación
|
||||
|
||||
### Flutter
|
||||
| Componente | Versión |
|
||||
|------------|---------|
|
||||
| Flutter | 3.38.5 (stable) |
|
||||
| Dart | 3.10.4 |
|
||||
| DevTools | 2.51.1 |
|
||||
| Engine | c108a94d7a |
|
||||
|
||||
### Android
|
||||
| Componente | Valor |
|
||||
|------------|-------|
|
||||
| Java | OpenJDK 17.0.17 |
|
||||
| Package Name | `me.tzzr.packet` |
|
||||
| Label | packet |
|
||||
|
||||
## Permisos Android
|
||||
|
||||
```xml
|
||||
<!-- Network -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<!-- Camera and Audio -->
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
|
||||
<!-- Location -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<!-- Media (Android 13+ / API 33+) -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
|
||||
|
||||
<!-- Storage (Android 12 and below) -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32"/>
|
||||
```
|
||||
|
||||
**Configuración adicional:**
|
||||
```xml
|
||||
<application android:usesCleartextTraffic="true">
|
||||
```
|
||||
|
||||
## Dependencias
|
||||
|
||||
### Runtime
|
||||
| Paquete | Versión | Propósito |
|
||||
|---------|---------|-----------|
|
||||
| flutter_bloc | ^9.0.0 | State management |
|
||||
| equatable | ^2.0.7 | Value equality |
|
||||
| sqflite | ^2.4.2 | SQLite local DB |
|
||||
| path_provider | ^2.1.5 | File paths |
|
||||
| dio | ^5.7.0 | HTTP client |
|
||||
| crypto | ^3.0.6 | SHA-256 hashing |
|
||||
| image_picker | ^1.1.2 | Captura fotos/video |
|
||||
| record | ^6.0.0 | Grabación audio |
|
||||
| file_picker | ^8.1.7 | Selección archivos |
|
||||
| geolocator | ^13.0.2 | GPS location |
|
||||
| permission_handler | ^11.4.0 | Runtime permissions |
|
||||
| uuid | ^4.5.1 | UUID generation |
|
||||
| intl | ^0.20.2 | Internacionalización |
|
||||
| cached_network_image | ^3.4.1 | Cache de imágenes |
|
||||
|
||||
### Dev
|
||||
| Paquete | Versión |
|
||||
|---------|---------|
|
||||
| flutter_test | SDK |
|
||||
| flutter_lints | ^6.0.0 |
|
||||
|
||||
## Estructura del Proyecto
|
||||
|
||||
```
|
||||
lib/ (3,505 líneas Dart)
|
||||
├── main.dart # Entry point
|
||||
├── core/
|
||||
│ ├── constants/ # AppConstants, RetryDelays
|
||||
│ ├── errors/ # Excepciones
|
||||
│ ├── utils/ # hash_utils, retry_utils
|
||||
│ └── theme/ # AppTheme
|
||||
├── data/
|
||||
│ ├── datasources/ # BackendApi, BibliotecaApi, LocalDatabase
|
||||
│ └── repositories/ # Config, Etiqueta, Contenedor repos
|
||||
├── domain/
|
||||
│ └── entities/ # Contenedor, Etiqueta, Pack, Destino
|
||||
└── presentation/
|
||||
├── app.dart # PacketApp, MainScreen
|
||||
├── bloc/ # Cubits
|
||||
├── pages/ # 5 pantallas
|
||||
└── widgets/ # Componentes
|
||||
```
|
||||
|
||||
## Pantallas
|
||||
|
||||
| # | Pantalla | Icono | Función |
|
||||
|---|----------|-------|---------|
|
||||
| 1 | Captura | 📷 | Montar contenedor multimedia |
|
||||
| 2 | Etiquetas | 🏷️ | Seleccionar tags |
|
||||
| 3 | Packs | 📦 | Conjuntos de etiquetas |
|
||||
| 4 | Pendientes | ⏳ | Cola de reintentos (máx 20) |
|
||||
| 5 | Config | ⚙️ | URLs, llaves, bibliotecas |
|
||||
|
||||
## Constantes
|
||||
|
||||
| Constante | Valor |
|
||||
|-----------|-------|
|
||||
| maxPendientes | 20 |
|
||||
| maxReintentos | 20 |
|
||||
| hashLength | 64 (SHA-256) |
|
||||
| chunkSize | 512 KB |
|
||||
| httpTimeout | 30s |
|
||||
| retryCheckInterval | 30s |
|
||||
|
||||
## Backoff de Reintentos (72h total)
|
||||
|
||||
| Rango | Espera |
|
||||
|-------|--------|
|
||||
| 1-3 | 1, 2, 5 min |
|
||||
| 4-6 | 10, 20, 30 min |
|
||||
| 7-10 | 1, 2, 3, 4 h |
|
||||
| 11-14 | 5, 6, 6, 6 h |
|
||||
| 15-19 | 8, 8, 8, 8, 6 h |
|
||||
| 20 | STOP |
|
||||
|
||||
## Comandos de Build
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
|
||||
cd /tmp/packet-app
|
||||
flutter clean
|
||||
flutter build apk --release
|
||||
cp build/app/outputs/flutter-apk/app-release.apk ~/Downloads/packet-v1.7.4.apk
|
||||
```
|
||||
565
v4-archive/packet/docs/SPEC.md
Normal file
565
v4-archive/packet/docs/SPEC.md
Normal file
@@ -0,0 +1,565 @@
|
||||
# Packet - Especificación Técnica Completa
|
||||
|
||||
## 1. Visión General
|
||||
|
||||
Packet permite:
|
||||
- Capturar contenido multimedia (fotos, audio, video, archivos)
|
||||
- Etiquetarlo con referencias de bibliotecas externas
|
||||
- Empaquetarlo en contenedores con hash único
|
||||
- Enviarlo a backends configurables
|
||||
|
||||
### Principios
|
||||
|
||||
| Principio | Descripción |
|
||||
|-----------|-------------|
|
||||
| Zero-retention | Los archivos nunca se almacenan en disco, solo en RAM hasta envío |
|
||||
| Hash-first | Todo contenedor tiene hash único generado localmente |
|
||||
| Contenedor cerrado | Una vez montado, no se inspecciona. Solo enviar o desmontar |
|
||||
| App tonta | No valida coherencia de etiquetas, solo formato. El backend decide |
|
||||
| Offline-capable | Captura sin conexión, cola de reintentos cuando hay red |
|
||||
|
||||
---
|
||||
|
||||
## 2. Stack Tecnológico
|
||||
|
||||
| Componente | Tecnología |
|
||||
|------------|------------|
|
||||
| Framework | Flutter 3.x |
|
||||
| Lenguaje | Dart |
|
||||
| Estado | flutter_bloc |
|
||||
| DB Local | sqflite |
|
||||
| HTTP | dio |
|
||||
| Crypto | crypto (SHA-256) |
|
||||
| Media | image_picker, record |
|
||||
| Location | geolocator |
|
||||
| Permisos | permission_handler |
|
||||
|
||||
---
|
||||
|
||||
## 3. Estructura de Carpetas
|
||||
|
||||
```
|
||||
packet/
|
||||
├── android/
|
||||
├── ios/
|
||||
├── lib/
|
||||
│ ├── core/
|
||||
│ │ ├── constants/
|
||||
│ │ │ └── app_constants.dart
|
||||
│ │ ├── errors/
|
||||
│ │ │ └── exceptions.dart
|
||||
│ │ ├── utils/
|
||||
│ │ │ ├── hash_utils.dart
|
||||
│ │ │ └── retry_utils.dart
|
||||
│ │ └── theme/
|
||||
│ │ └── app_theme.dart
|
||||
│ │
|
||||
│ ├── data/
|
||||
│ │ ├── datasources/
|
||||
│ │ │ ├── local_database.dart
|
||||
│ │ │ ├── backend_api.dart
|
||||
│ │ │ └── biblioteca_api.dart
|
||||
│ │ ├── models/
|
||||
│ │ │ ├── destino_model.dart
|
||||
│ │ │ ├── contenedor_model.dart
|
||||
│ │ │ ├── etiqueta_model.dart
|
||||
│ │ │ └── pack_model.dart
|
||||
│ │ └── repositories/
|
||||
│ │ ├── contenedor_repository.dart
|
||||
│ │ ├── etiqueta_repository.dart
|
||||
│ │ └── config_repository.dart
|
||||
│ │
|
||||
│ ├── domain/
|
||||
│ │ └── entities/
|
||||
│ │ ├── contenedor.dart
|
||||
│ │ ├── etiqueta.dart
|
||||
│ │ ├── pack.dart
|
||||
│ │ ├── destino.dart
|
||||
│ │ └── archivo_adjunto.dart
|
||||
│ │
|
||||
│ ├── presentation/
|
||||
│ │ ├── bloc/
|
||||
│ │ │ ├── captura/
|
||||
│ │ │ ├── etiquetas/
|
||||
│ │ │ ├── packs/
|
||||
│ │ │ ├── pendientes/
|
||||
│ │ │ └── config/
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── captura_page.dart
|
||||
│ │ │ ├── etiquetas_page.dart
|
||||
│ │ │ ├── packs_page.dart
|
||||
│ │ │ ├── pendientes_page.dart
|
||||
│ │ │ └── config_page.dart
|
||||
│ │ ├── widgets/
|
||||
│ │ │ ├── audio_recorder.dart
|
||||
│ │ │ ├── destino_selector.dart
|
||||
│ │ │ ├── etiqueta_grid.dart
|
||||
│ │ │ └── contenedor_card.dart
|
||||
│ │ └── app.dart
|
||||
│ │
|
||||
│ └── main.dart
|
||||
│
|
||||
├── test/
|
||||
├── pubspec.yaml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Pantallas
|
||||
|
||||
### Navegación
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [Mi DECK ▼] [CORP] [+Ext] │ ← Destinos (superior)
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ CONTENIDO PANTALLA │
|
||||
│ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 📷 🏷️ 📦 ⏳ ⚙️ │ ← Navegación (inferior)
|
||||
│ Captura Tags Packs Pend. Config │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.1 Captura
|
||||
|
||||
Montar contenedor con contenido multimedia.
|
||||
|
||||
Campos:
|
||||
- Título (string, opcional)
|
||||
- Descripción (string, opcional, se procesa en backend)
|
||||
- Identificador (string 64 chars, auto-generado SHA-256)
|
||||
- Archivos (array, opcional)
|
||||
- GPS (lat/long, opcional)
|
||||
- Etiquetas (array de hashes, seleccionadas en pestaña Etiquetas)
|
||||
|
||||
Botones de captura:
|
||||
- 📷 Foto (cámara)
|
||||
- 🎤 Audio (con contador de tiempo)
|
||||
- 🎥 Video
|
||||
- 📎 Archivo
|
||||
- 📍 GPS
|
||||
|
||||
El hash se genera al abrir la app o tras cada envío. Se puede enviar contenedor vacío (solo hash). Botones de copiar y regenerar hash visibles.
|
||||
|
||||
### 4.2 Etiquetas
|
||||
|
||||
Seleccionar etiquetas de bibliotecas y añadir hashes externos.
|
||||
|
||||
Secciones:
|
||||
1. Grid HST - Iconos de biblioteca HST (se oscurecen al seleccionar)
|
||||
2. Grid DECK - Iconos de biblioteca DECK
|
||||
3. Campo hash - Pegar URL o hash externo
|
||||
4. Seleccionadas - Lista de etiquetas actuales con botón eliminar
|
||||
|
||||
Validación de hash externo:
|
||||
1. Usuario pega texto
|
||||
2. App busca patrón: [a-f0-9]{64}
|
||||
3. Por cada match → check verde inmediato
|
||||
4. Background: consulta /api/tags para resolver nombre
|
||||
5. Si encuentra → muestra nombre
|
||||
6. Si no → muestra hash truncado
|
||||
|
||||
### 4.3 Packs
|
||||
|
||||
Conjuntos predefinidos de etiquetas creados localmente.
|
||||
|
||||
- 1 tap → aplica todas las etiquetas del pack
|
||||
- Creación: nombre + etiquetas actualmente seleccionadas
|
||||
- Almacenados en SQLite local
|
||||
|
||||
### 4.4 Pendientes
|
||||
|
||||
Cola de contenedores fallidos. Límite: 20 contenedores máximo. App se bloquea si se llena.
|
||||
|
||||
Por cada contenedor muestra:
|
||||
- Hash (truncado)
|
||||
- Título (o "sin título")
|
||||
- Número de archivos
|
||||
- Intentos realizados (máx 20)
|
||||
- Último intento (hora)
|
||||
- Estado (error / reintentando)
|
||||
|
||||
Acciones:
|
||||
- Reintentar → Intenta enviar de nuevo
|
||||
- Recuperar → Extrae archivos al almacenamiento del dispositivo y elimina contenedor
|
||||
|
||||
### 4.5 Config
|
||||
|
||||
URLs, llaves y bibliotecas.
|
||||
|
||||
Por cada destino:
|
||||
- Nombre (identificador visual)
|
||||
- URL Backend (endpoint de envío)
|
||||
- Llave (hash de 64 chars para autenticación)
|
||||
|
||||
Bibliotecas de iconos:
|
||||
- URL (dominio)
|
||||
- Endpoint (ruta del índice, ej: /api/tags)
|
||||
- Nombre (identificador visual)
|
||||
|
||||
---
|
||||
|
||||
## 5. Modelos de Datos
|
||||
|
||||
### Contenedor
|
||||
|
||||
```dart
|
||||
class Contenedor {
|
||||
String hash; // 64 chars, generado localmente
|
||||
String? titulo;
|
||||
String? descripcion;
|
||||
List<ArchivoAdjunto> archivos;
|
||||
GpsLocation? gps;
|
||||
List<String> etiquetas; // Lista de hashes
|
||||
DateTime createdAt;
|
||||
}
|
||||
```
|
||||
|
||||
### Etiqueta
|
||||
|
||||
```dart
|
||||
class Etiqueta {
|
||||
String hMaestro; // Hash único (64 chars)
|
||||
String hGlobal; // biblioteca + etiqueta (128 chars)
|
||||
String? mrf; // Media reference para imagen
|
||||
String? ref; // Código corto
|
||||
String? nombreEs;
|
||||
String? nombreEn;
|
||||
String grupo;
|
||||
bool activo;
|
||||
}
|
||||
```
|
||||
|
||||
### Destino
|
||||
|
||||
```dart
|
||||
class Destino {
|
||||
int id;
|
||||
String nombre;
|
||||
String url;
|
||||
String hash; // Llave de autenticación
|
||||
bool activo;
|
||||
}
|
||||
```
|
||||
|
||||
### Pack
|
||||
|
||||
```dart
|
||||
class Pack {
|
||||
int id;
|
||||
String nombre;
|
||||
String icono;
|
||||
List<String> tags; // Lista de hashes
|
||||
}
|
||||
```
|
||||
|
||||
### ArchivoAdjunto
|
||||
|
||||
```dart
|
||||
class ArchivoAdjunto {
|
||||
String nombre;
|
||||
String mimeType;
|
||||
Uint8List bytes; // En memoria, nunca en disco
|
||||
FileType tipo; // image, audio, video, document
|
||||
String? hash;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Base de Datos SQLite
|
||||
|
||||
```sql
|
||||
CREATE TABLE registro (
|
||||
hash TEXT PRIMARY KEY,
|
||||
titulo TEXT,
|
||||
hora_envio TEXT NOT NULL,
|
||||
primera_conf TEXT,
|
||||
ultima_conf TEXT,
|
||||
destino_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE destinos (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nombre TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
hash TEXT NOT NULL,
|
||||
activo INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE TABLE bibliotecas (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nombre TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
endpoint TEXT NOT NULL,
|
||||
h_biblioteca TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE etiquetas_cache (
|
||||
h_maestro TEXT PRIMARY KEY,
|
||||
h_global TEXT,
|
||||
mrf TEXT,
|
||||
ref TEXT,
|
||||
nombre_es TEXT,
|
||||
nombre_en TEXT,
|
||||
grupo TEXT,
|
||||
biblioteca_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE packs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nombre TEXT NOT NULL,
|
||||
icono TEXT DEFAULT 📦,
|
||||
tags TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE pendientes (
|
||||
hash TEXT PRIMARY KEY,
|
||||
titulo TEXT,
|
||||
contenido BLOB NOT NULL,
|
||||
intentos INTEGER DEFAULT 0,
|
||||
ultimo_intento TEXT,
|
||||
proximo_intento TEXT,
|
||||
destino_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE app_state (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. APIs Externas
|
||||
|
||||
### Backend (envío)
|
||||
|
||||
```http
|
||||
POST {destino.url}/ingest
|
||||
X-Auth-Key: {destino.hash}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"hash": "fc2fae65158ca7c1880dd3ff9b8ed9081107b50f9cdce08df30234fe166777ce",
|
||||
"titulo": "ferr-23",
|
||||
"descripcion": "Compra en ferretería",
|
||||
"etiquetas": ["a1b2c3d4...fc2fae65...", "a1b2c3d4...b2c3d4e5..."],
|
||||
"gps": {"lat": 43.3623, "long": -8.4115},
|
||||
"archivos": [{"nombre": "foto.jpg", "tipo": "image/jpeg", "contenido": "base64..."}]
|
||||
}
|
||||
|
||||
Response 200: { "ok": true, "received_at": "2024-12-20T10:00:00Z" }
|
||||
Response 409: { "error": "hash_exists" }
|
||||
```
|
||||
|
||||
### Envío por chunks (archivos grandes)
|
||||
|
||||
```http
|
||||
POST {destino.url}/upload/init
|
||||
{ "hash": "...", "total_chunks": 50, "file_name": "video.mp4" }
|
||||
→ { "upload_id": "..." }
|
||||
|
||||
POST {destino.url}/upload/chunk/{upload_id}/{chunk_number}
|
||||
Body: bytes (512KB - 1MB por chunk)
|
||||
→ { "ok": true }
|
||||
|
||||
POST {destino.url}/upload/complete/{upload_id}
|
||||
→ { "ok": true, "file_hash": "..." }
|
||||
```
|
||||
|
||||
### Bibliotecas (índice de etiquetas)
|
||||
|
||||
```http
|
||||
GET {biblioteca.url}/api/tags
|
||||
|
||||
Response:
|
||||
{
|
||||
"count": 973,
|
||||
"biblioteca": {
|
||||
"h_biblioteca": "b7149f9e2106c566032aeb29a26e4c6cdd5f5c16b4421025c58166ee345740d1",
|
||||
"nombre": "HST",
|
||||
"publica": true
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"h_maestro": "fc2fae65...77ce",
|
||||
"h_global": "b7149f9e...fc2fae65...77ce",
|
||||
"mrf": "502dc114...",
|
||||
"ref": "inv",
|
||||
"nombre_es": "factura",
|
||||
"nombre_en": "invoice",
|
||||
"grupo": "hst",
|
||||
"imagen_url": "https://tzrtech.org/502dc...png",
|
||||
"activo": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Imágenes: GET {biblioteca.url}/{mrf}.png
|
||||
|
||||
---
|
||||
|
||||
## 8. Formato de Referencias
|
||||
|
||||
Hash simple (etiqueta): 64 caracteres hex
|
||||
```
|
||||
fc2fae65158ca7c1880dd3ff9b8ed9081107b50f9cdce08df30234fe166777ce
|
||||
```
|
||||
|
||||
Referencia global (biblioteca + etiqueta): 128 caracteres
|
||||
```
|
||||
[h_biblioteca 64 chars][h_etiqueta 64 chars]
|
||||
```
|
||||
|
||||
Cadena jerárquica: múltiplo de 64, sin separador
|
||||
```
|
||||
[biblioteca][nivel1][nivel2][nivel3]...
|
||||
```
|
||||
|
||||
Múltiples cadenas: separadas por \\n
|
||||
|
||||
---
|
||||
|
||||
## 9. Lógica de Reintentos
|
||||
|
||||
20 intentos en 72 horas con backoff exponencial:
|
||||
|
||||
| Intento | Espera | Acumulado |
|
||||
|---------|--------|-----------|
|
||||
| 1 | 1 min | 0 |
|
||||
| 2 | 2 min | 1 min |
|
||||
| 3 | 5 min | 3 min |
|
||||
| 4 | 10 min | 8 min |
|
||||
| 5 | 20 min | 18 min |
|
||||
| 6 | 30 min | 38 min |
|
||||
| 7 | 1 h | 1 h |
|
||||
| 8 | 2 h | 2 h |
|
||||
| 9 | 3 h | 4 h |
|
||||
| 10 | 4 h | 7 h |
|
||||
| 11 | 5 h | 11 h |
|
||||
| 12 | 6 h | 16 h |
|
||||
| 13 | 6 h | 22 h |
|
||||
| 14 | 6 h | 28 h |
|
||||
| 15 | 8 h | 34 h |
|
||||
| 16 | 8 h | 42 h |
|
||||
| 17 | 8 h | 50 h |
|
||||
| 18 | 8 h | 58 h |
|
||||
| 19 | 6 h | 66 h |
|
||||
| 20 | STOP | 72 h |
|
||||
|
||||
---
|
||||
|
||||
## 10. Persistencia de Estado
|
||||
|
||||
La app restaura al abrir exactamente como se cerró:
|
||||
- Destino seleccionado
|
||||
- Pantalla activa
|
||||
- Contenedor en progreso
|
||||
- Etiquetas seleccionadas
|
||||
- Cola de pendientes
|
||||
- Configuración completa
|
||||
|
||||
---
|
||||
|
||||
## 11. Permisos
|
||||
|
||||
Android:
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
```
|
||||
|
||||
iOS:
|
||||
```xml
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Para capturar fotos y videos</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Para grabar notas de voz</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Para añadir ubicación a los contenedores</string>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. CI/CD
|
||||
|
||||
```yaml
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
- run: flutter pub get
|
||||
- run: flutter test
|
||||
|
||||
build-android:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
- run: flutter build apk --release
|
||||
|
||||
build-ios:
|
||||
needs: test
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
- run: flutter build ios --release --no-codesign
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Roadmap
|
||||
|
||||
### v1.0 (MVP)
|
||||
- 5 pantallas básicas
|
||||
- Captura foto/audio/video
|
||||
- Envío a backend único
|
||||
- Cola de pendientes
|
||||
- Packs locales
|
||||
|
||||
### v1.1
|
||||
- Múltiples destinos
|
||||
- Envío por chunks
|
||||
- Cache de bibliotecas
|
||||
|
||||
### v1.2
|
||||
- Notificaciones push
|
||||
- Widgets de acceso rápido
|
||||
|
||||
### v2.0
|
||||
- Credenciales para bibliotecas privadas
|
||||
- Cadenas jerárquicas visuales
|
||||
- Sincronización entre dispositivos
|
||||
|
||||
---
|
||||
|
||||
## 14. Referencias
|
||||
|
||||
- Biblioteca HST: https://tzrtech.org
|
||||
- API HST: https://tzrtech.org/api/tags
|
||||
- h_biblioteca HST: b7149f9e2106c566032aeb29a26e4c6cdd5f5c16b4421025c58166ee345740d1
|
||||
|
||||
---
|
||||
|
||||
Versión: 1.0
|
||||
Fecha: 2025-12-20
|
||||
Reference in New Issue
Block a user