- Added support for Google OAuth with separate client IDs for Android and iOS. - Updated `verify_google_id_token` to validate `aud` against both client IDs and check `email_verified`. - Modified `google_oauth_handler` to accept and process the new client IDs. - Enhanced security by enforcing explicit JWT algorithm validation. - Updated mobile app to handle Google OAuth flow using `expo-auth-session`. - Fixed API request to send `id_token` in snake_case as expected by the backend. - Added necessary environment variables for Google client IDs in mobile app. - Implemented intent filter for Google OAuth redirect in AndroidManifest.xml.
574 lines
26 KiB
Markdown
574 lines
26 KiB
Markdown
# MeowSpool — Documento de Referência do Projeto
|
||
|
||
**Canvas de Design:** https://app.paper.design/file/01KKM0HA6NBV44VCQDFQHC2MRY
|
||
|
||
---
|
||
|
||
## 1. Visão Geral
|
||
|
||
MeowSpool é uma solução para entusiastas e profissionais de impressão 3D gerenciarem seu estoque de filamentos. O diferencial está na precisão do cálculo de peso líquido (descontando o peso do carretel via presets), na identidade visual inspirada em gatos siameses, e na integração com etiquetas físicas exportáveis (SVG/PDF/QR Code/NFC).
|
||
|
||
**Foco do desenvolvimento atual:** App Mobile (React Native + Expo) + Backend em Rust. A interface Web é planejada para uma fase posterior, após a estabilização do mobile e da API.
|
||
|
||
---
|
||
|
||
## 2. Objetivos
|
||
|
||
- **Precisão:** Eliminar o "chute" de quanto filamento resta no rolo.
|
||
- **Agilidade:** Facilitar o cadastro e a consulta via dispositivos móveis, mesmo offline.
|
||
- **Organização:** Centralizar parâmetros técnicos (temperatura/fluxo) para consulta rápida.
|
||
|
||
---
|
||
|
||
## 3. Público-Alvo
|
||
|
||
- Hobbistas de impressão 3D.
|
||
- Donos de "Print Farms" (fazendas de impressão).
|
||
- Projetistas que trabalham com diversos materiais (PLA, ABS, PETG, etc).
|
||
|
||
---
|
||
|
||
## 4. Identidade Visual
|
||
|
||
### 4.1 Nome e Conceito
|
||
|
||
**MeowSpool** — fusão de "Meow" (gato) e "Spool" (carretel). A identidade visual é inspirada no gato siamês: tons quentes e escuros com detalhes em azul-aço que remetem aos olhos característicos da raça.
|
||
|
||
App é **dark mode exclusivo** — nenhum suporte a light mode.
|
||
|
||
### 4.2 Paleta de Cores
|
||
|
||
| Token | Hex | Função na Interface |
|
||
|-----------------|-------------|-------------------------------------------------|
|
||
| `bgBase` | `#1E1B18` | Fundo geral |
|
||
| `bgSurface` | `#2A2622` | Cards, contêineres, widgets |
|
||
| `bgHover` | `#332F2B` | Estados de hover em cards e itens de lista |
|
||
| `textPrimary` | `#F5EEDC` | Títulos, peso líquido (texto importante) |
|
||
| `textSecondary` | `#C9C1B0` | Descrições, labels (material, marca) |
|
||
| `accent` | `#38BCC2` | Botões de ação, elementos ativos |
|
||
| `accentMuted` | `#38BCC226` | Backgrounds de badges e status (10% opacidade) |
|
||
| `stockLow` | `#FF6B6B` | ≤ 15% — vermelho |
|
||
| `stockMedium` | `#FF9F43` | ≤ 35% — laranja |
|
||
| `stockOk` | `#38BCC2` | > 35% — accent |
|
||
| `border` | `#3D3830` | Bordas sutis |
|
||
| `error` | `#FF6B6B` | Mensagens de erro |
|
||
|
||
### 4.3 Tipografia
|
||
|
||
- UI geral: **Inter**
|
||
- Slugs, hex, URLs, código: **JetBrains Mono**
|
||
|
||
---
|
||
|
||
## 5. Requisitos Funcionais (MVP)
|
||
|
||
### 5.1 Autenticação
|
||
|
||
- **Métodos suportados:** Email/senha e OAuth via Google.
|
||
- **Verificação de e-mail obrigatória** antes do primeiro login.
|
||
- **Sessão:** Token JWT com refresh token. O app mobile persiste a sessão localmente para acesso offline.
|
||
- **Fluxos:** Login, Cadastro, Verificar E-mail, Reenviar Verificação, Esqueci a Senha, Redefinir Senha.
|
||
|
||
### 5.2 Gestão de Inventário
|
||
|
||
- **Cadastro de Filamento:** Material, Marca, Modelo, Cor (hex), Temperatura Hotend, Temperatura Mesa, Fator de Fluxo/Extrusão, Notas.
|
||
- **Calculadora de Peso Líquido:** `Peso_Líquido = Peso_Total - Peso_Carretel` (via preset selecionado).
|
||
- **Filtros:** Material, marca, busca textual, nível de estoque (`low`/`medium`/`ok`).
|
||
- **Ordenação:** Peso líquido asc/desc, criado em desc (padrão).
|
||
|
||
### 5.3 Presets de Carretéis
|
||
|
||
**Presets do Sistema (built-in):**
|
||
|
||
| Nome do Preset | Peso do Carretel |
|
||
|------------------------|-----------------|
|
||
| Bambu Lab (Plástico) | 250g |
|
||
| Elegoo (Papelão) | 200g |
|
||
| Creality (Plástico) | 230g |
|
||
| Prusament (Plástico) | 201g |
|
||
| Sunlu (Papelão) | 200g |
|
||
| Polymaker (Plástico) | 220g |
|
||
| Genérico Papelão 1kg | 200g |
|
||
| Genérico Plástico 1kg | 250g |
|
||
|
||
**Presets Customizados:** O usuário pode criar, editar e excluir seus próprios presets. Presets do sistema são somente leitura.
|
||
|
||
### 5.4 Identificação e Etiquetas
|
||
|
||
- **QR Code:** Cada filamento gera QR Code com deep link `meowspool://filament/<id>`.
|
||
- **Etiqueta SVG:** `GET /filaments/:id/label.svg` — parâmetros `width_mm`, `height_mm`, `fields`, `dpi`.
|
||
- **Etiqueta PDF:** `GET /filaments/:id/label.pdf` — mesmos parâmetros.
|
||
- **Impressoras suportadas:** Padrão (50×30mm), Niimbot D11/D110 (22×14mm, dpi=203), Brother/Dymo (dpi=300).
|
||
- **Campos selecionáveis:** `color`, `name`, `material_brand`, `net_weight`, `print_temp`, `qrcode`.
|
||
- **NFC:** Leitura e gravação de tags NTAG215 com URI `meowspool://filament/<id>`.
|
||
|
||
### 5.5 Dashboard
|
||
|
||
- Total de estoque em kg.
|
||
- Contagem de filamentos com estoque baixo.
|
||
- Resumo por material (contagem + peso total).
|
||
- Filamentos com estoque baixo (≤15%).
|
||
- Filamentos adicionados recentemente.
|
||
|
||
---
|
||
|
||
## 6. Arquitetura do Sistema
|
||
|
||
Ambos os lados seguem **Arquitetura Hexagonal (Ports & Adapters)**:
|
||
|
||
```
|
||
adapters/inbound (HTTP / Tela)
|
||
|
|
||
v
|
||
application (Use Cases / Services)
|
||
|
|
||
v
|
||
domain (Entidades puras)
|
||
|
|
||
^
|
||
ports (Interfaces/traits)
|
||
|
|
||
^
|
||
adapters/outbound (PostgreSQL / SQLite / NFC / HTTP)
|
||
```
|
||
|
||
**Regra fundamental:** O `domain` e os `ports` nunca importam de `adapters` ou `application`.
|
||
|
||
---
|
||
|
||
## 7. Backend (Rust)
|
||
|
||
### 7.1 Stack
|
||
|
||
| Componente | Biblioteca |
|
||
|------------------|----------------------------------------|
|
||
| HTTP Framework | `axum` 0.7 |
|
||
| Async Runtime | `tokio` (full) |
|
||
| ORM / Query | `sqlx` (postgres, uuid, time, macros) |
|
||
| Autenticação JWT | `jsonwebtoken` 9.x |
|
||
| Hash de senha | `argon2` 0.5 (Argon2id) |
|
||
| OAuth Google | `oauth2` 4.x |
|
||
| Serialização | `serde`, `serde_json` |
|
||
| Erros | `thiserror` |
|
||
| Env vars | `dotenvy` |
|
||
| Logging | `tracing`, `tracing-subscriber` |
|
||
| Validação | `validator` |
|
||
| HTTP Client | `reqwest` |
|
||
| Email (SMTP) | `lettre` (STARTTLS) |
|
||
| Geração QR Code | `qrcode` + `image` |
|
||
| Geração PDF | `printpdf` 0.7 |
|
||
|
||
### 7.2 Estrutura de Pastas
|
||
|
||
```
|
||
backend/
|
||
├── Cargo.toml
|
||
├── migrations/
|
||
│ ├── 20240101000001_create_users.sql
|
||
│ ├── 20240101000002_create_spool_presets.sql
|
||
│ ├── 20240101000003_create_filaments.sql
|
||
│ └── 20240101000004_create_token_tables.sql ← password_reset_tokens + email_verification_tokens
|
||
└── src/
|
||
├── main.rs ← entry point: config, DB, router, servidor
|
||
├── config.rs ← struct Config lida de variáveis de ambiente
|
||
├── error.rs ← AppError unificado com IntoResponse
|
||
├── router.rs ← composição de todas as rotas Axum
|
||
├── domain/ ← entidades puras (User, Filament, SpoolPreset)
|
||
├── ports/ ← traits: UserRepository, FilamentRepository, SpoolPresetRepository
|
||
├── application/
|
||
│ ├── auth_service.rs ← login, register, OAuth, refresh, logout
|
||
│ ├── email_token_service.rs← forgot_password, verify_email, reset_password
|
||
│ ├── filament_service.rs ← CRUD, peso líquido, QR Code, SVG, PDF
|
||
│ └── spool_preset_service.rs
|
||
├── infrastructure/
|
||
│ └── email_service.rs ← SMTP via lettre
|
||
└── adapters/
|
||
├── inbound/ ← handlers Axum + middleware JWT
|
||
└── outbound/ ← implementações PostgreSQL dos ports
|
||
```
|
||
|
||
### 7.3 Rotas da API
|
||
|
||
Base: `/api/v1`
|
||
|
||
**Auth — `/api/v1/auth`**
|
||
|
||
| Método | Rota | Acesso |
|
||
|--------|-------------------------|--------------------------------|
|
||
| POST | `/register` | Público — retorna 201 sem token|
|
||
| POST | `/login` | Público |
|
||
| POST | `/oauth/google` | Público |
|
||
| POST | `/refresh` | Público (requer refresh token) |
|
||
| POST | `/logout` | Autenticado |
|
||
| POST | `/resend-verification` | Público — sempre retorna 200 |
|
||
| POST | `/forgot-password` | Público |
|
||
| POST | `/verify-email` | Público |
|
||
| POST | `/reset-password` | Público (requer token) |
|
||
|
||
**Redirects para Deep Links**
|
||
|
||
| Método | Rota | Descrição |
|
||
|--------|-------------------|------------------------------------------------------------|
|
||
| GET | `/verify-email` | Redireciona 302 → `{APP_SCHEME}://verify-email?token=xxx` |
|
||
| GET | `/reset-password` | Redireciona 302 → `{APP_SCHEME}://reset-password?token=xxx`|
|
||
|
||
**Filaments — `/api/v1/filaments`**
|
||
|
||
| Método | Rota | Acesso |
|
||
|--------|------------------|-------------|
|
||
| GET | `/` | Autenticado |
|
||
| POST | `/` | Autenticado |
|
||
| GET | `/:id` | Autenticado |
|
||
| PUT | `/:id` | Autenticado |
|
||
| DELETE | `/:id` | Autenticado |
|
||
| GET | `/:id/qrcode` | Autenticado |
|
||
| GET | `/:id/label.svg` | Autenticado |
|
||
| GET | `/:id/label.pdf` | Autenticado |
|
||
|
||
**Spool Presets — `/api/v1/spool-presets`**
|
||
|
||
| Método | Rota | Acesso |
|
||
|--------|--------|--------------------------------------|
|
||
| GET | `/` | Autenticado |
|
||
| POST | `/` | Autenticado |
|
||
| PUT | `/:id` | Autenticado (apenas presets do user) |
|
||
| DELETE | `/:id` | Autenticado (apenas presets do user) |
|
||
|
||
**Outros**
|
||
|
||
| Método | Rota | Acesso |
|
||
|--------|--------------|-------------|
|
||
| GET | `/dashboard` | Autenticado |
|
||
| GET | `/users/me` | Autenticado |
|
||
| PUT | `/users/me` | Autenticado |
|
||
|
||
### 7.4 Padrão de Erros
|
||
|
||
```json
|
||
{ "error": "not found", "code": "NOT_FOUND" }
|
||
```
|
||
|
||
| Variante AppError | HTTP | Code |
|
||
|------------------------|------|------------------------|
|
||
| `NotFound` | 404 | `NOT_FOUND` |
|
||
| `Unauthorized` | 401 | `UNAUTHORIZED` |
|
||
| `Forbidden` | 403 | `FORBIDDEN` |
|
||
| `EmailNotVerified` | 403 | `EMAIL_NOT_VERIFIED` |
|
||
| `Conflict` | 409 | `CONFLICT` |
|
||
| `BadRequest` | 400 | `BAD_REQUEST` |
|
||
| `Validation` | 422 | `VALIDATION_ERROR` |
|
||
| `Internal` | 500 | `INTERNAL_ERROR` |
|
||
|
||
### 7.5 Variáveis de Ambiente
|
||
|
||
| Variável | Obrigatória | Padrão |
|
||
|---------------------------|-------------|-------------------------|
|
||
| `DATABASE_URL` | ✅ | — |
|
||
| `JWT_SECRET` | ✅ | — |
|
||
| `JWT_EXPIRY_SECS` | ❌ | `3600` |
|
||
| `JWT_REFRESH_EXPIRY_SECS` | ❌ | `2592000` |
|
||
| `GOOGLE_CLIENT_ID` | ❌ | — |
|
||
| `GOOGLE_CLIENT_SECRET` | ❌ | — |
|
||
| `HOST` / `PORT` | ❌ | `0.0.0.0` / `8080` |
|
||
| `SMTP_HOST` / `SMTP_PORT` | ❌ | `smtp.gmail.com` / `587`|
|
||
| `SMTP_USER` / `SMTP_PASS` | ❌ | — |
|
||
| `EMAIL_FROM` | ❌ | `noreply@meowspool.app` |
|
||
| `APP_BASE_URL` | ❌ | `http://localhost:8080` |
|
||
| `APP_SCHEME` | ❌ | `meowspool` |
|
||
|
||
> Em produção: `APP_BASE_URL=https://meowspool.felipecncloud.com/api/v1`
|
||
|
||
### 7.6 Como Rodar Localmente
|
||
|
||
```bash
|
||
cd backend/
|
||
cp .env.example .env
|
||
|
||
# PostgreSQL via Docker
|
||
docker run -d --name meowspool-db \
|
||
-e POSTGRES_USER=meowspool \
|
||
-e POSTGRES_PASSWORD=meowspool \
|
||
-e POSTGRES_DB=meowspool \
|
||
-p 5432:5432 postgres:16-alpine
|
||
|
||
sqlx migrate run
|
||
cargo watch -x run
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Mobile (React Native + Expo)
|
||
|
||
### 8.1 Stack
|
||
|
||
| Componente | Biblioteca |
|
||
|-------------------|-------------------------------------|
|
||
| Framework | `expo` ~54 (Bare Workflow) |
|
||
| Navigation | `expo-router` ~4 (file-system) |
|
||
| Linguagem | TypeScript 5.x (strict) |
|
||
| Node Version | 22 LTS (via `mise`) |
|
||
| Java Version | 17 (obrigatório — Java 24 quebra CMake) |
|
||
| Forms | `react-hook-form` + `zod` |
|
||
| Estado global | `zustand` |
|
||
| HTTP | `axios` (interceptor Bearer + refresh) |
|
||
| DB local | `expo-sqlite` + SQLCipher |
|
||
| Auth persistência | `expo-secure-store` |
|
||
| UI | React Native + Ionicons |
|
||
| QR Code render | `react-native-qrcode-svg` |
|
||
| SVG inline | `react-native-svg` (SvgXml) |
|
||
| Câmera / Scanner | `expo-camera` ~17 (ML Kit) |
|
||
| NFC | `react-native-nfc-manager` |
|
||
| File system | `expo-file-system/legacy` |
|
||
| Compartilhamento | `expo-sharing` (iOS) |
|
||
| Abrir arquivo | `expo-intent-launcher` (Android) |
|
||
| Animações | `react-native-reanimated` |
|
||
|
||
> **Bare Workflow:** o diretório `android/` é comitado. Qualquer pacote com módulo nativo exige `npx expo prebuild --platform android` + rebuild do APK.
|
||
|
||
### 8.2 Estrutura de Pastas
|
||
|
||
```
|
||
mobile/
|
||
├── .mise.toml ← node 22 LTS, java 17
|
||
├── app.json ← Expo config, scheme "meowspool"
|
||
├── app/ ← expo-router (file-system routes)
|
||
│ ├── _layout.tsx ← Root layout (auth guard + sessão)
|
||
│ ├── (auth)/
|
||
│ │ ├── login.tsx ← G3-0
|
||
│ │ ├── register.tsx ← IX-0
|
||
│ │ ├── forgot-password.tsx ← L2-0
|
||
│ │ ├── check-email.tsx ← RU-0 (pós-cadastro / e-mail não verificado)
|
||
│ │ ├── reset-password.tsx ← 117-0
|
||
│ │ └── password-reset-done.tsx ← 135-0
|
||
│ ├── filament/[id].tsx ← Deep link: meowspool://filament/<id> → /(app)/inventory/<id>
|
||
│ ├── verify-email.tsx ← Deep link: meowspool://verify-email?token=xxx
|
||
│ └── (app)/
|
||
│ ├── (tabs)/
|
||
│ │ ├── home.tsx ← 1-0 Dashboard
|
||
│ │ ├── inventory.tsx ← CW-0 Inventário
|
||
│ │ ├── add.tsx ← FAB → /inventory/new
|
||
│ │ ├── config.tsx ← M4-0 Presets de Carretéis
|
||
│ │ └── profile.tsx ← 1LQ-0 Perfil
|
||
│ ├── inventory/
|
||
│ │ ├── new.tsx ← 2X-0 Cadastro
|
||
│ │ ├── filters.tsx ← 17H-0 Filtros (bottom sheet)
|
||
│ │ ├── [id].tsx ← 6L-0 Detalhe
|
||
│ │ └── [id]/edit.tsx ← 13O-0 Editar
|
||
│ ├── config/presets/
|
||
│ │ ├── new.tsx ← QM-0 Novo Preset
|
||
│ │ └── [id]/edit.tsx ← 1KD-0 Editar Preset
|
||
│ ├── scanner.tsx ← Scanner QR Code (ML Kit)
|
||
│ ├── nfc-reader.tsx ← 1RY-0 Leitura NFC (bottom sheet modal)
|
||
│ └── filaments/[id]/
|
||
│ ├── qrcode.tsx ← Ver QR Code
|
||
│ ├── label.tsx ← Exportar Etiqueta (PDF/SVG)
|
||
│ └── write-nfc.tsx ← Gravar tag NTAG215
|
||
└── src/
|
||
├── domain/ ← Entidades puras (Filament, SpoolPreset, User, Dashboard)
|
||
├── ports/ ← Interfaces (IFilamentRepository, ISpoolPresetRepository, IAuthRepository, INFCRepository)
|
||
├── application/ ← Use Cases (filament/, preset/, auth/, nfc/)
|
||
├── adapters/
|
||
│ ├── remote/ ← Axios → API Rust (ApiFilamentRepository, etc.)
|
||
│ ├── local/ ← SQLite (LocalFilamentRepository, LocalSpoolPresetRepository)
|
||
│ └── nfc/ ← react-native-nfc-manager (NFCManagerRepository)
|
||
├── store/ ← Zustand: authStore, filamentStore, presetStore
|
||
├── shared/ ← theme.ts, constants.ts, utils/
|
||
└── presentation/components/
|
||
├── ui/ ← Button, Input, Card, Badge
|
||
├── layout/ ← Screen, Header
|
||
└── filament/ ← StockBar, ColorSwatch, FilamentCard
|
||
```
|
||
|
||
### 8.3 Navegação e Deep Links
|
||
|
||
- Scheme: `meowspool://` (configurado em `app.json`)
|
||
- Grupos: `(auth)` — sem sessão | `(app)` — autenticado | `(app)/(tabs)` — bottom tabs
|
||
|
||
| Deep Link | Handler | Ação |
|
||
|----------------------------------------|--------------------------------|---------------------------------------------|
|
||
| `meowspool://filament/<id>` | `app/filament/[id].tsx` | Redireciona para `/(app)/inventory/<id>` |
|
||
| `meowspool://verify-email?token=<tok>` | `app/verify-email.tsx` | Verifica e-mail via POST /auth/verify-email |
|
||
| `meowspool://reset-password?token=<tok>`| `app/(auth)/reset-password.tsx`| Exibe formulário de nova senha |
|
||
|
||
> **Links de e-mail:** o backend gera `https://meowspool.felipecncloud.com/api/v1/verify-email?token=xxx` (aceito por clientes de e-mail). O backend redireciona 302 → `meowspool://...`. O OS abre o app.
|
||
|
||
### 8.4 Estratégia Offline-First
|
||
|
||
1. **Escrita:** toda mutação persiste primeiro no SQLite (`synced = 0`) e enfileira em `sync_queue`.
|
||
2. **Leitura:** sempre do SQLite; a API é usada apenas para sync.
|
||
3. **Sync:** background job consome `sync_queue` e envia para o backend.
|
||
4. **Conflito:** last-write-wins via `updatedAt`. O backend é a fonte de verdade.
|
||
5. **Auth offline:** token no `expo-secure-store`; refresh automático via interceptor Axios.
|
||
|
||
**Schema SQLite:**
|
||
|
||
```sql
|
||
-- filaments
|
||
id TEXT PRIMARY KEY, user_id TEXT, material TEXT, brand TEXT, model TEXT,
|
||
color_hex TEXT, spool_preset_id TEXT, total_weight_g REAL, net_weight_g REAL,
|
||
temp_hotend_c REAL, temp_bed_c REAL, flow_factor_pct REAL, notes TEXT,
|
||
synced INTEGER DEFAULT 0, updated_at TEXT, created_at TEXT
|
||
|
||
-- spool_presets
|
||
id TEXT PRIMARY KEY, user_id TEXT, name TEXT, spool_weight_g REAL,
|
||
is_system INTEGER DEFAULT 0, synced INTEGER DEFAULT 0, created_at TEXT
|
||
|
||
-- sync_queue
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT, entity_type TEXT, entity_id TEXT,
|
||
operation TEXT, payload TEXT, created_at TEXT
|
||
```
|
||
|
||
### 8.5 Como Rodar Localmente
|
||
|
||
```bash
|
||
cd mobile/
|
||
mise install # garante Node 22 e Java 17
|
||
npm install
|
||
|
||
# Criar .env
|
||
echo "EXPO_PUBLIC_API_URL=http://localhost:3000/api/v1" > .env
|
||
|
||
# Dev (Expo Go — sem câmera ML Kit / NFC)
|
||
npx expo start
|
||
|
||
# Build nativo Android (necessário para câmera ML Kit e NFC)
|
||
mise exec -- npx expo run:android
|
||
```
|
||
|
||
> **Atenção ao build Android:** o projeto usa `namespace "com.meowspool"` no `build.gradle`, mas o código fonte fica em `com.meowspool.app`. Use sempre nomes totalmente qualificados no `AndroidManifest.xml`.
|
||
|
||
---
|
||
|
||
## 9. Estrutura de Dados (Entidades Principais)
|
||
|
||
### `users`
|
||
| Campo | Tipo | Descrição |
|
||
|------------------|-----------|-------------------------------------|
|
||
| id | UUID | Identificador único |
|
||
| email | string | Email do usuário |
|
||
| password_hash | string? | Hash Argon2id (nulo se OAuth) |
|
||
| google_id | string? | ID OAuth Google (opcional) |
|
||
| email_verified | boolean | Obrigatório para login |
|
||
| created_at | timestamp | |
|
||
|
||
### `spool_presets`
|
||
| Campo | Tipo | Descrição |
|
||
|----------------|-----------|-------------------------------------------------|
|
||
| id | UUID | Identificador único |
|
||
| name | string | Nome do preset |
|
||
| spool_weight_g | integer | Peso do carretel vazio em gramas |
|
||
| is_system | boolean | `true` = built-in (read-only para usuários) |
|
||
| user_id | UUID? | Nulo para presets do sistema |
|
||
| created_at | timestamp | |
|
||
|
||
### `filaments`
|
||
| Campo | Tipo | Descrição |
|
||
|-----------------|-----------|-----------------------------------------|
|
||
| id | UUID | Identificador único |
|
||
| user_id | UUID | Dono do filamento |
|
||
| material | string | PLA, ABS, PETG, TPU, ASA, PA, PC… |
|
||
| brand | string | Marca |
|
||
| model | string? | Modelo/linha |
|
||
| color_hex | string | Cor em hex (`#FF5733`) |
|
||
| spool_preset_id | UUID | Preset de carretel usado |
|
||
| total_weight_g | real | Peso total medido na balança (gramas) |
|
||
| net_weight_g | real | Calculado: total − peso do carretel |
|
||
| temp_hotend_c | real? | Temperatura do hotend (°C) |
|
||
| temp_bed_c | real? | Temperatura da mesa (°C) |
|
||
| flow_factor_pct | real? | Fator de fluxo (%) |
|
||
| notes | text? | Observações livres |
|
||
| updated_at | timestamp | Usado para resolução de conflitos sync |
|
||
| created_at | timestamp | |
|
||
|
||
### `password_reset_tokens` / `email_verification_tokens`
|
||
| Campo | Tipo | Descrição |
|
||
|------------|-------------|--------------------------------------|
|
||
| id | UUID | PK |
|
||
| token | TEXT UNIQUE | Token único (indexado) |
|
||
| user_id | UUID | FK para users |
|
||
| expires_at | TIMESTAMPTZ | TTL: 1h (reset) / 24h (verificação) |
|
||
| used_at | TIMESTAMPTZ | NULL = não usado; preenchido ao usar |
|
||
|
||
---
|
||
|
||
## 10. Fluxo do Usuário
|
||
|
||
### Cadastro e Login
|
||
1. Usuário se cadastra → backend envia e-mail de verificação (background).
|
||
2. Usuário clica no link `https://...` → backend redireciona 302 → deep link → app verifica.
|
||
3. Login com e-mail não verificado → `403 EMAIL_NOT_VERIFIED` → app sugere reenvio.
|
||
4. Login com e-mail verificado → tokens JWT emitidos → sessão persistida no SecureStore.
|
||
|
||
### Cadastro de Filamento
|
||
1. Usuário abre o App (autenticado) → "Novo Filamento".
|
||
2. Preenche marca, material, modelo, seleciona cor hex.
|
||
3. Seleciona preset de carretel ou cria um novo.
|
||
4. Digita o peso total (ex: 850g) → app calcula: **600g disponível**.
|
||
5. Salva localmente (offline-first) e sincroniza quando online.
|
||
6. QR Code e etiqueta ficam disponíveis imediatamente.
|
||
|
||
### Identificação via QR Code / NFC
|
||
1. Usuário aponta câmera para QR Code **ou** aproxima tag NFC do carretel.
|
||
2. App lê `meowspool://filament/<id>` → navega direto para o detalhe do filamento.
|
||
|
||
---
|
||
|
||
## 11. Artboards de Design
|
||
|
||
| ID | Tela | Seção |
|
||
|---------|-----------------------------|-------|
|
||
| `G3-0` | Login | 5.1 |
|
||
| `IX-0` | Cadastro | 5.1 |
|
||
| `L2-0` | Recuperação de Senha | 5.1 |
|
||
| `RU-0` | Verificação de Email | 5.1 |
|
||
| `117-0` | Redefinir Senha | 5.1 |
|
||
| `135-0` | Senha Redefinida | 5.1 |
|
||
| `1-0` | Home / Dashboard | 5.5 |
|
||
| `CW-0` | Inventário | 5.2 |
|
||
| `17H-0` | Filtros (bottom sheet) | 5.2 |
|
||
| `2X-0` | Cadastro de Filamento | 5.2 |
|
||
| `13O-0` | Editar Filamento | 5.2 |
|
||
| `6L-0` | Detalhe do Filamento | 5.2 |
|
||
| `M4-0` | Presets de Carretéis | 5.3 |
|
||
| `QM-0` | Novo Preset | 5.3 |
|
||
| `1KD-0` | Editar Preset | 5.3 |
|
||
| `1CF-0` | Ver QR Code | 5.4 |
|
||
| `1FZ-0` | Exportar Etiqueta | 5.4 |
|
||
| `1RY-0` | Leitura NFC (modal) | 5.4 |
|
||
| `1LQ-0` | Perfil / Conta | 5.1 |
|
||
|
||
---
|
||
|
||
## 12. Roadmap
|
||
|
||
### Pendente — Mobile
|
||
- [ ] Sync background com `sync_queue` SQLite → API
|
||
- [ ] Google OAuth com `expo-auth-session`
|
||
- [ ] NFC suporte iOS (entitlement `com.apple.developer.nfc.readwrite`)
|
||
- [ ] Expo Notifications para alertas de estoque baixo
|
||
- [ ] Testes de integração com Jest + Testing Library
|
||
|
||
### Pendente — Infraestrutura
|
||
- [ ] DI container formal (instanciar e injetar repositórios concretos)
|
||
|
||
### Fase 2 — Web (planejado)
|
||
- [ ] Front-end Web com Next.js (dashboard + inventário)
|
||
- [ ] Cache com Redis / HTTP Cache
|
||
- [ ] Histórico de uso por projeto/impressão
|
||
- [ ] Custo por grama
|
||
- [ ] Importação em lote (CSV/JSON)
|
||
- [ ] Pool público de presets compartilhados pela comunidade
|
||
|
||
### Concluído
|
||
- [x] CRUD de filamentos (mobile + API)
|
||
- [x] CRUD de presets (mobile + API)
|
||
- [x] Dashboard (mobile + API)
|
||
- [x] QR Code geração e scanner (ML Kit)
|
||
- [x] Exportação de etiqueta SVG e PDF
|
||
- [x] Suporte Niimbot D11/D110 (22×14mm) com layout adaptativo
|
||
- [x] NFC leitura e gravação NTAG215 (Android)
|
||
- [x] Deep links (filamento, verificação de e-mail, reset de senha)
|
||
- [x] Verificação de e-mail obrigatória + reenvio
|
||
- [x] Fluxo completo de reset de senha
|
||
- [x] Redirect HTTP → deep link (compatibilidade com clientes de e-mail)
|