253 lines
14 KiB
Markdown
253 lines
14 KiB
Markdown
# 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":"<REDACTED>"}'
|
||
```
|