14 KiB
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/sessionse/users/activity - Banco: tabelas
email_verification_codes,auth_sessions,session_feature_access,auth_events - Migração é aditiva e preserva
admin,teresaenatalie
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-jsonrelativo
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_amountdo 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.
Atualização 2026-09-19 noite (007.2026)
- Money privacy (olho €) app-wide —
SPEC-MONEY-PRIVACY.md - Profile status audit (Temporary / Deceased / Custody / Treatment) —
SPEC-RESIDENT-PROFILE-STATUS.md - SMTP auth mail validado —
AUTH-USERS-SPEC.md - Prod + test em VM500 alinhados para estas features
Checklist agente
- Hop só no big, nunca big1 para esta VM.
- Nunca alterar
/etc/network/interfacesdo Proxmox sem três confirmações explícitas. - Gerir a aplicação com
systemctl status|restart rentcharge-backend rentcharge-frontend. - Não iniciar processos manuais nas portas 3000 ou 8000.
- Não tocar em
cloudflared.service/config-mycloud.yml(myvexx). Só o unitcloudflared-itecnologys. - Rebuild do frontend: confirmar que
.env.localestá disabled;NEXT_PUBLIC_API_URLvazio. - DNS público do rentcharge = CNAME proxied para o túnel
39cffaba; acesso Tailscale pelo hostnamerentcharge-ts. - Aplicação agora está na internet com senha curta; Cloudflare Access na borda ainda não está ligado.
- Antes de alterar lista de residentes / rooms / payments: ler RN-01..RN-03 e RN-PAY em
PROJECT-RENTCHARGE.md. - Reimport SAGE: usar consolidados em
E:\Reports Week\...//home/mini/rentcharge/sage-payments/reimport/; dedupe portxn_no. - Antes de alterar Account Receivables / Credit Book / ROI Finance: ler
SPEC-ACCOUNT-RECEIVABLES.md(over-settlement, remit, R-CB*).
Comandos rápidos
# 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>"}'