diff --git a/ligbox-ops-platform/docs/vms/VM500.md b/ligbox-ops-platform/docs/vms/VM500.md index 4f260c4..ab4ef88 100644 --- a/ligbox-ops-platform/docs/vms/VM500.md +++ b/ligbox-ops-platform/docs/vms/VM500.md @@ -1,68 +1,253 @@ -# SPEC-VM500 ? Rent and Charge (Weekly and Arrears) - -**Fonte local:** `C:\\LAPTOP\\rentcharge\\docs` ? sync Obsidian CT130. - - -**Data:** 2026-09-05 -**Status:** infra de acesso + DNS + Traefik (placeholder HTTP). App ainda nao deployado. -**Host da VM:** BIG (`big-itecnologys-wardrobe`) ? NAO e big1. - -## Objetivo - -Ambiente na VM 500 PROJETOS para o produto **RenteCharge Weekly and Arrears** (controle de aluguel semanal / debitos). Pasta na VM: `/home/mini/RenteCharge Weekly&Arrears`. Codigo `rent-arrears-web` ainda no PC Windows. - -## Topologia (obrigatorio hop) - -Windows (Tailscale) -> **big** `100.90.196.58:22` -> LAN `10.10.10.100` (VM 500). -Este PC **nao** roteia `10.10.10.100` direto. BIG e BIG1 nao se conectam. - -## VM 500 - -| Campo | Valor | -|--------|--------| -| VMID | 500 | -| Hostname | PROJETOS | -| SO | Debian 12 | -| IP LAN | 10.10.10.100/24 gw 10.10.10.1 vmbr1 | -| CPU/RAM/disco | 2 vCPU / 4 GB / 25 GB | -| Users | `admin` (sudo NOPASSWD), `mini` (docker) | -| Senha SSH | 805353 (admin e mini) | -| Chave | `id_ed25519` PROXMOX-HERTZNER-3 em authorized_keys de admin e mini | -| SSH config Windows | `Host vm500` ProxyJump `big-proxmox` User admin IdentityFile id_ed25519 | - -## DNS Cloudflare (zona itecnologys.com) - -| Nome | Tipo | Destino | Proxy | -|------|------|---------|-------| -| rentcharge.itecnologys.com | A | 100.90.196.58 (Tailscale do big) | DNS only | -| rentecharge.itecnologys.com | CNAME | rentcharge.itecnologys.com | DNS only | - -Nao aponta para 10.10.10.100. Hostname nao aceita `&`. - -## Traefik (BIG CT120 web-traefik, 10.10.10.120) - -Caminho HTTP: - -`Host: rentcharge.itecnologys.com` (ou rentecharge) --> nginx host big `:80` (`myvexx-proxy` server_name) --> Traefik `http://10.10.10.120:80` --> `http://10.10.10.100:80` - -Arquivo: `/opt/traefik/dynamic/services.yml` (router `rentcharge` / service `rentcharge-svc`). -Backup: `services.yml.bak-rentcharge-20260905`. -Traefik so entrypoint `:80` (sem TLS neste CT). -Teste no big: HTTP 200 placeholder. - -## Placeholder na VM 500 - -`python3 -m http.server 80` em `/var/www/html` (apt nginx falhou: VM sem saida HTTPS para deb.debian.org). Pagina: "Rent and Charge / VM500 PROJETOS - Traefik OK". - -## Instabilidade SSH deste Windows - -Porta 22 Tailscale do big = `SSH-2.0-Tailscale`, nao OpenSSH. `ssh.exe` timeout no KEX; paramiko funciona. WAN 51.171.219.235:22/80/443 fechados. - -## Checklist agente - -1. Hop so no **big**, nunca big1 para esta VM. -2. Nao alterar `/etc/network/interfaces` do Proxmox sem 3 confirmacoes. -3. Proximo: deploy do app Rent Charge no lugar do http.server:80. +# SPEC-VM500 — Rent and Charge (Weekly and Arrears) + +**Fonte local:** `C:\LAPTOP\rentcharge\docs` — sync Obsidian CT130. + +**Data:** 2026-09-09 (rev. Account Receivables / Credit Book / Period dashboard — noite 08–09) +**Status:** app completo na VM500; HTTPS público via Cloudflare Tunnel; login validado; pagamentos SAGE importados; **AR Credit Book live**. +**Host da VM:** BIG (`big-itecnologys-wardrobe`) — **não** é big1. +**Produto / escopo funcional:** ver `PROJECT-RENTCHARGE.md` (ocupação NP + **RN-PAY** + **SPEC-ACCOUNT-RECEIVABLES.md**). + +## Objetivo + +Ambiente na VM 500 PROJETOS para o produto **RenteCharge Weekly and Arrears** (controle de aluguel semanal / débitos). **Única pasta do projeto:** `/home/mini/rentcharge` (código, venv, SAGE imports, config, archive e bare git). + +## Topologia (obrigatório hop) + +Windows (Tailscale) → **big** `100.90.196.58:22` → LAN `10.10.10.100` (VM 500). +Este PC **não** roteia `10.10.10.100` direto. BIG e BIG1 não se conectam. + +Caminho **público HTTPS** (2026-09-05): + +Internet → Cloudflare (TLS `*.itecnologys.com`) → tunnel `39cffaba-…` no BIG → Traefik CT120 `10.10.10.120:80` → VM500 (`:3000` UI, `:8000` `/api`). + +Caminho **público HTTPS myvexx / Rently** (em curso 2026-09-06): + +Internet → Cloudflare (TLS `*.myvexx.com`) → tunnel myvexx `7233d455-…` (`config-mycloud.yml` no BIG) → Traefik CT120 → VM500. +Hostname: `rently.myvexx.com`. Traefik + CORS já prontos; ingress DNS no BIG ainda pendente (SSH publickey). Ver `docs/ops/RENTLY-MYVEXX-TUNNEL.md`. + +Caminho **privado Tailscale** (preservado): + +`rentcharge-ts.itecnologys.com` → A `100.90.196.58` DNS-only → nginx BIG `:80` → Traefik CT120 → VM500. + +## VM 500 + +| Campo | Valor | +|--------|--------| +| VMID | 500 | +| Hostname | PROJETOS | +| SO | Debian 12 | +| IP LAN | 10.10.10.100/24 gw 10.10.10.1 vmbr1 | +| CPU/RAM/disco | 2 vCPU / 4 GB / 25 GB | +| Users | `admin` (sudo NOPASSWD), `mini` (docker) | +| Senha SSH | 805353 (admin e mini) | +| Chave | `id_ed25519` PROXMOX-HERTZNER-3 em authorized_keys de admin e mini | +| SSH config Windows | `Host vm500` ProxyJump `big-proxmox` User admin IdentityFile id_ed25519 | + +## DNS Cloudflare (zona itecnologys.com) + +Zona ID: `7aab8fcc135ed992f284b1f5fbf57065` +Conta da zona: `37fd43032acca512b575ebe20853bf6d` (Admin@itecnologys.com) + +| Nome | Tipo | Destino | Proxy | +|------|------|---------|-------| +| rentcharge.itecnologys.com | CNAME | `39cffaba-b611-4c50-8297-41a48d220870.cfargotunnel.com` | **Proxied** | +| rentcharge-ts.itecnologys.com | A | 100.90.196.58 (Tailscale do BIG) | DNS only | +| rentecharge.itecnologys.com | CNAME | rentcharge.itecnologys.com | DNS only | + +Backup pré-alteração DNS: `/root/cloudflare-backups/rentcharge-dns-20260905T185905Z.json` (no BIG). + +Não aponta para `10.10.10.100`. Hostname não aceita `&`. + +**Por que CNAME para o túnel (não A Tailscale):** o padrão dos serviços já SSL no BIG (`fleet.myvexx.com`, `projetos.myvexx.com`, `mycloud.myvexx.com`) é Cloudflare Tunnel. Um CNAME `*.cfargotunnel.com` só funciona se o túnel estiver na **mesma conta Cloudflare da zona**. A zona `itecnologys.com` usa o túnel `39cffaba`; o túnel myvexx `7233d455` é de outra conta e **não** pode publicar este domínio. + +## SSL / Cloudflare Tunnel (BIG) + +Dois `cloudflared` convivem no BIG. Não se cruzam com Tailscale (redes distintas). Único conflito possível: porta de métricas — já isolada. + +| Serviço | Túnel | Conta CF | Config | Métricas | Função | +|---------|-------|----------|--------|----------|--------| +| `cloudflared.service` | `7233d455-2e1f-45b3-bf3a-4bfad26b2f4a` | myvexx (`2d504d7a…`) | `/etc/cloudflared/config-mycloud.yml` | `127.0.0.1:20241` | **Não alterar.** fleet, projetos, mycloud, etc. | +| `cloudflared-itecnologys.service` | `39cffaba-b611-4c50-8297-41a48d220870` | itecnologys (`37fd4303…`) | `/etc/cloudflared/config-itecnologys.yml` | `127.0.0.1:20242` | Só `rentcharge.itecnologys.com` | + +Ingress do túnel itecnologys: + +``` +hostname: rentcharge.itecnologys.com +service: http://10.10.10.120:80 +originRequest.httpHostHeader: rentcharge.itecnologys.com +catch-all: http_status:404 +``` + +Credencial: `/root/.cloudflared/39cffaba-b611-4c50-8297-41a48d220870.json` +Unit: `/etc/systemd/system/cloudflared-itecnologys.service` (enabled, Restart=always) + +Certificado público observado: CN `itecnologys.com`, SAN `itecnologys.com` + `*.itecnologys.com`, emissor Google Trust Services WE1. + +Validação: `https://rentcharge.itecnologys.com/login` HTTP 200; `https://rentcharge.itecnologys.com/api/health` → `{"status":"ok","app":"Granby Rent Arrears"}`. + +Origin certs `/root/.cloudflared/cert.pem` e `cert-mycloud.pem` **não** autenticam a API de gestão (`tunnel list` → unauthorized). Operar o túnel com a credencial JSON continua válido. DNS do rentcharge foi aplicado pela API Cloudflare (token de zona), não por `cloudflared tunnel route dns`. + +## Traefik (BIG CT120 web-traefik, 10.10.10.120) + +Caminho HTTP interno (inalterado): + +`Host: rentcharge.itecnologys.com` (ou rentecharge) +→ nginx host BIG `:80` (`myvexx-proxy` server_name) **ou** Cloudflare Tunnel +→ Traefik `http://10.10.10.120:80` +→ UI `http://10.10.10.100:3000` e API `http://10.10.10.100:8000` (`/api`) + +Arquivo: `/opt/traefik/dynamic/services.yml` (router `rentcharge` / service `rentcharge-svc`). +Backup: `services.yml.bak-rentcharge-20260905`. +Traefik só entrypoint `:80` (TLS na borda Cloudflare, não neste CT). +`cloudflared` no CT120 está **inativo/disabled**; o túnel itecnologys corre no **host BIG**, não no CT120. + +Nginx BIG `:443` (`myvexx-proxy-https`) **não** inclui `rentcharge.itecnologys.com`; SSL público não depende desse vhost. + +## Aplicação na VM 500 + +| Componente | Porta | Serviço | +|------------|-------|---------| +| Next.js frontend | 3000 | `rentcharge-frontend.service` | +| FastAPI backend | 8000 | `rentcharge-backend.service` | + +Os dois usam `systemd`, boot automático, `Restart=always`. +Frontend WorkingDirectory: `/home/mini/rentcharge/frontend`. +Backend WorkingDirectory: `/home/mini/rentcharge/backend`. +Python: `/home/mini/rentcharge/venv/bin/python`. +Node: `/home/admin/node/bin/node`. + +O frontend usa API no **mesmo domínio** (`/api`); o Traefik separa para 3000/8000. +Banco live: `/home/mini/rentcharge/backend/data/arrears.db`. +Segredo JWT: `/home/mini/rentcharge/config/rentcharge.env` (`SECRET_KEY` apenas; symlink legado `/etc/rentcharge.env`), permissão `600`, não versionar. + +Deploy anterior arquivado em `/home/mini/rentcharge/archive/`. +Backup pré-deploy do banco: ver `archive/` / `backend/data/*.bak*`. +Backup pré-troca de senha: `/home/mini/rentcharge/backend/data/arrears.db.bak-20260905T190523Z`. +Backups pagamentos SAGE: `arrears.db.bak-clear-amounts-*`, `arrears.db.bak-sage-dedupe-*`, `arrears.db.bak-before-sage-*`. + +### Pagamentos SAGE (fonte única — 2026-09-05/06) + +| Item | Valor | +|------|--------| +| Import | `POST /api/import/sage-payments` · `backend/app/services/sage_import.py` | +| Formato | SAGE Customer Invoices Due (Amount negativo; Type SA = cash) | +| Pasta Windows | `E:\Reports Week\PAYMENTS-REPORTS-BY-WEEK\2026\` | +| Pasta VM | `/home/mini/rentcharge/sage-payments/reimport/` | +| Unicidade | `payments.txn_no` (SAGE No) + índice único | +| Default UI | só `category=cash`; filtro rentsupp/refund/all | +| Excel arrears | **não** escreve payments | + +Detalhe de regras: `PROJECT-RENTCHARGE.md` § RN-PAY-01..06. + +### Login (produção) + +| Campo | Valor | +|--------|--------| +| Utilizador | `admin` | +| Palavra-passe | `(rotated — see vault / password manager; never store in Git)` | +| Endpoint | `POST /api/auth/login-json` | + +A password de produção **não** deve constar nesta spec. O default em `backend/app/config.py` só aplica no primeiro arranque se o user ainda não existir; após bootstrap, gerir a password fora do Git. + +### Módulo de utilizadores (2026-09-06) + +Especificação funcional e de segurança: `AUTH-USERS-SPEC.md`. + +- Código isolado: `backend/app/modules/users/` +- Cadastro público: `/signup`, acessível por **Register new user** em `/login` +- Verificação: código de seis dígitos por e-mail; 10 minutos, uso único, máximo de cinco erros +- Recuperação: token aleatório longo, hash no SQLite e revogação das sessões após uso +- Sessões: cookie HttpOnly + registo server-side com IP, User-Agent, SO, browser, login, last seen e encerramento +- Auditoria Admin: `/users/sessions` e `/users/activity` +- Banco: tabelas `email_verification_codes`, `auth_sessions`, `session_feature_access`, `auth_events` +- Migração é aditiva e preserva `admin`, `teresa` e `natalie` + +**Deploy 2026-09-07:** backend e frontend reiniciados em conjunto; backup +`arrears.db.bak-before-auth-module-20260907-090632`; migração e cookie login +validados. SMTP autenticado **configurado e validado 2026-09-19** (`mail.myvexx.com:465`, `admin@myvexx.com`) em `/home/mini/rentcharge/config/rentcharge.env`. + +### Correção crítica: login no browser (2026-09-05) + +Causa: `.env.local` com `NEXT_PUBLIC_API_URL=http://127.0.0.1:8000` tinha precedência sobre `.env.production` (`NEXT_PUBLIC_API_URL=` vazio). O JS compilado pedia a API ao **localhost do visitante**, não ao servidor. Curl no BIG/VM devolvia 200; o formulário falhava. + +Correção: + +- `.env.local` → `/home/mini/rentcharge/frontend/.env.local.disabled-20260905T190745Z` +- Rebuild `npm run build` + `systemctl restart rentcharge-frontend` +- Build atual: **zero** referências a `127.0.0.1:8000`; login usa `/api/auth/login-json` relativo + +**Regra:** nunca deixar `.env.local` no frontend de produção. Rebuild só com `.env.production` (`NEXT_PUBLIC_API_URL` vazio). + +## Produto (construído nesta fase) + +### Charge bands + +Além das bandas de rent / full board, existem: + +- **Paid by bank account** (`KIND_BANK`) — `uses_resident_amount`: weekly due = `weekly_rent` / `imported_weekly_amount` do residente +- **Flexible full board** (`KIND_FLEXIBLE_FB`) — idem, com flag full board + +Lógica: `backend/app/services/charge_bands.py`, `accounting.get_cap`, schema `ChargeBandOut.uses_resident_amount`. + +### SAGE ID + +Import Excel normaliza SAGE ID (float/int/string; ignora VACANT). Backfill em residentes existentes por quarto/nome sem sobrescrever IDs já válidos. Unicidade em create/update. Visível na lista e na ficha do residente. + +### UI (padrão SAGE) + +- Header em dois níveis (`AppNav`): barra de company group + barra branca com logo e navegação +- **Residents** e **Reporting**: mesmo layout (command bar, views, filtros por coluna, pager, export) — sem cards KPI no Reporting +- Ficha residente **Payments**: grelha SAGE; colunas separadas ID / Ref / SAGE / Details / Status / Amount; search + filtros; Type default Cash (SA) +- Coluna `#`, Room estreita, badges NP / Name dup / Room conflict; botão Reconcile rooms + +### Regras de negócio — ocupação + pagamentos + +Detalhe canónico em `PROJECT-RENTCHARGE.md`. Resumo obrigatório para agentes: + +| ID | Regra | +|----|--------| +| **RN-01** | Um quarto físico = um residente. Dívida na pessoa (SAGE), não no quarto. | +| **RN-02** | Ocupante validado = último **cash SA** (> 0). RENTSUPP/refund **não** contam. | +| **RN-03A** | Saiu / mudou sem ocupar → **NP01…** + `last_room_no`. | +| **RN-03B** | Nome duplicado → badge; sem merge automático. | +| **RN-PAY-01..06** | SAGE = única fonte de payments; SA=cash; RENTSUPP arquivado; refund não subtrai; `txn_no` único; semana Mon–Sun. | + +Estado de implementação: **RN-01..03 + RN-PAY + UI Reporting/Payments aplicados na VM500** (2026-09-06). +**Account Receivables / Credit Book / Period dashboard / ROI Finance A4 pack:** ver `SPEC-ACCOUNT-RECEIVABLES.md` (2026-09-09) — R-CB01…R-CB05, ROI Finance remit/credit net, over-settlement, **Print ROI Finance pack (A4)** com logo SA + Brinks bags + 3 assinaturas. + +### Cartas impressas + +Templates HTML em `backend/app/templates/` (`letter_debt.html`, `letter_agreement.html`, `statement.html`). Layout visual das cartas de reconhecimento/demonstrativo ainda em evolução com os exemplos do operador. + +## Instabilidade SSH deste Windows + +Porta 22 Tailscale do big = `SSH-2.0-Tailscale`, não OpenSSH. `ssh.exe` timeout no KEX; paramiko funciona. WAN 51.171.219.235:22/80/443 fechados neste contexto. Acesso público do **app** é pelo túnel Cloudflare, não por abrir WAN. + +## Checklist agente + +1. Hop só no **big**, nunca big1 para esta VM. +2. **Nunca** alterar `/etc/network/interfaces` do Proxmox sem **três** confirmações explícitas. +3. Gerir a aplicação com `systemctl status|restart rentcharge-backend rentcharge-frontend`. +4. Não iniciar processos manuais nas portas 3000 ou 8000. +5. Não tocar em `cloudflared.service` / `config-mycloud.yml` (myvexx). Só o unit `cloudflared-itecnologys`. +6. Rebuild do frontend: confirmar que `.env.local` está disabled; `NEXT_PUBLIC_API_URL` vazio. +7. DNS público do rentcharge = CNAME proxied para o túnel `39cffaba`; acesso Tailscale pelo hostname `rentcharge-ts`. +8. Aplicação agora está na internet com senha curta; Cloudflare Access na borda ainda **não** está ligado. +9. Antes de alterar lista de residentes / rooms / payments: ler RN-01..RN-03 e **RN-PAY** em `PROJECT-RENTCHARGE.md`. +10. Reimport SAGE: usar consolidados em `E:\Reports Week\...` / `/home/mini/rentcharge/sage-payments/reimport/`; dedupe por `txn_no`. +11. Antes de alterar Account Receivables / Credit Book / ROI Finance: ler `SPEC-ACCOUNT-RECEIVABLES.md` (over-settlement, remit, R-CB*). + +## Comandos rápidos + +```bash +# BIG +systemctl status cloudflared cloudflared-itecnologys +ss -ltnp | grep -E '20241|20242' + +# VM500 (via ProxyJump) +systemctl status rentcharge-backend rentcharge-frontend +curl -sS http://127.0.0.1:8000/api/health +curl -sS -X POST http://127.0.0.1:8000/api/auth/login-json \ + -H 'Content-Type: application/json' \ + --data '{"username":"admin","password":""}' +```