尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Sistema de cadastro e cobrança para várias empresas assinantes. Cada empresa vê só os próprios clientes, serviços e faturas.
O cliente é quem a empresa atende e pode ter várias unidades (filiais). A empresa monta o próprio catálogo, vende por cliente e cobra pelo Asaas: fatura avulsa, mensalidade recorrente e webhook. Nada de texto, vencimento ou encargos fica preso a uma empresa específica. A Cora não entra neste código.
A interface, as mensagens de validação e este texto estão em português do Brasil.
Cada empresa assinante vê só os próprios clientes e filiais. O CPF/CNPJ do cliente é único dentro da empresa, não no sistema inteiro.
/cadastro. A primeira pessoa vira dona da conta.POST /api/webhooks/asaas autentica o cabeçalho asaas-access-token de cada empresa e atualiza a situação da cobrança sem processar o mesmo evento duas vezes.É preciso Node.js 22 e um PostgreSQL 16. O caminho mais simples é o Docker.
cp .env.example .env
docker compose up -d
npm install
npx prisma migrate deploy
npm run db:seed
npm run dev
Abra http://localhost:3000.
O docker compose sobe o Postgres na porta 5432, cria o banco gestaocobranca e, na primeira inicialização do volume, também o banco gestaocobranca_test usado pelos testes.
Se você já tem um PostgreSQL, crie o usuário e os bancos e aponte o .env:
CREATE USER gestao WITH PASSWORD 'gestao';
CREATE DATABASE gestaocobranca OWNER gestao;
CREATE DATABASE gestaocobranca_test OWNER gestao;
O seed cria um acesso fictício, só para desenvolvimento:
[email protected]exemplo-1234Os clientes gravados pelo seed têm Exemplo no nome. CPF, CNPJ, telefones e e-mails são inventados. Não são empresas reais.
Quem esquece a senha pede um link em Esqueci minha senha. O e-mail sai pela Resend da plataforma (AUTH_RESEND_API_KEY), de Taguaro <[email protected]> (AUTH_EMAIL_FROM). Não usa a chave que cada empresa grava na régua. Sem a chave, o pedido falha com um aviso genérico e o link não aparece no log.
O boleto e a régua saem do endereço gravado em Régua, no campo E-mail do remetente. Se a empresa ainda não preencheu, o código usa [email protected]. O dono pode trocar esse endereço na mesma tela. A chave da Resend continua sendo a da empresa.
O seed recusa rodar quando NODE_ENV é production. Em produção o primeiro acesso da Collab já nasce na migration multiempresa. npm run criar-admin ainda serve para criar outro superadministrador nessa empresa, se o e-mail ainda não existir.
A migration 20261021235900_multiempresa cria a empresa Collab Software e liga nela os usuários e clientes que já existirem. O CNPJ gravado é 00000000000000. Esse número é um placeholder, não é um CNPJ válido, e precisa ser trocado na tela Empresa pelo dono. O e-mail [email protected] fica como dono dessa empresa e superadministrador da plataforma. Outros usuários que já existirem entram na mesma empresa como donos, sem o flag da plataforma.
npm test
O comando cria o banco gestaocobranca_test se ele ainda não existir (usando o usuário do .env), aplica as migrations nele e roda:
Os testes apagam empresas, usuários, clientes, filiais, serviços, vínculos, faturas, a configuração do Asaas, a régua, os lembretes, os eventos de webhook e o limite de cadastro desse banco de teste.
| Variável | Para que serve |
|---|---|
DATABASE_URL | Conexão da aplicação. Na Vercel, o pooler de transação do Supabase (porta 6543) com ?pgbouncer=true. |
DIRECT_URL | Conexão do Prisma Migrate. Direta ou pooler em modo sessão, porta 5432. No computador local, igual à DATABASE_URL. |
AUTH_SECRET | Segredo do cookie de sessão. Use uma string longa e aleatória (openssl rand -base64 32). |
SEED_OWNER_NOME | Nome do acesso fictício do seed local. |
SEED_OWNER_EMAIL | E-mail desse acesso. |
SEED_OWNER_SENHA | Senha do seed local. Só é gravada se o e-mail ainda não existir. |
SEED_EXEMPLOS | true grava os clientes fictícios no seed local. false grava só o acesso de exemplo. |
ADMIN_NOME | Nome do primeiro acesso real. Só para npm run criar-admin. |
ADMIN_EMAIL | E-mail desse acesso. |
ADMIN_SENHA | Senha em texto. O script grava o hash e não altera a senha se o e-mail já existir. |
ASAAS_ENCRYPTION_KEY | 32 bytes em base64 (openssl rand -base64 32). Cifra, no banco, a chave da API do Asaas, o token do webhook, a chave da Resend e o auth token da subconta Twilio de cada empresa. Não é a chave da API e não pode ir para o repositório. Sem ela, a tela Asaas e a régua não gravam nem leem esses segredos. Não troque essa chave se já houver segredo gravado. |
TWILIO_ACCOUNT_SID | Account SID da conta pai da Twilio (começa com AC). Não é digitado na tela. |
TWILIO_AUTH_TOKEN | Auth token da conta pai. Só no servidor. A subconta de cada empresa é criada pela API e o token dela fica cifrado. |
TWILIO_WEBHOOK_URL | URL pública exata de /api/webhooks/twilio. Em produção, https. A Twilio assina esse endereço. |
TWILIO_WHATSAPP_TESTE | Número que recebe o WhatsApp enquanto o modo teste da régua está ligado. Sem ele, o modo teste não envia. |
O banco de produção já está no Supabase, região sa-east-1, projeto ziqjcworupopkcnfddjr. As tabelas iniciais e o RLS foram aplicados à mão, com o SQL de prisma/migrations/20261020225413_init e o de 20261021180000_enable_rls. A aplicação sobe na Vercel.
A migration 20261021235900_multiempresa não é aplicada por este repositório no banco de produção. Depois do merge, na sua máquina, com DIRECT_URL apontando para esse projeto:
npx prisma migrate deploy
Esse comando aplica só o que ainda falta. Se _prisma_migrations ainda não registrar as duas primeiras, marque-as antes, senão o deploy tenta criar de novo tipos que já existem:
npx prisma migrate resolve --applied 20261020225413_init
npx prisma migrate resolve --applied 20261021180000_enable_rls
npx prisma migrate deploy
O deploy da multiempresa cria a Collab Software, liga o usuário existente e liga o RLS em empresas e limites_cadastro. Em seguida, entre como Guilherme e troque o CNPJ placeholder na tela Empresa. Sessões antigas deixam de valer: o cookie passou a carregar a empresa e o papel, então é preciso entrar de novo.
O Prisma 7 não aceita url nem directUrl dentro de schema.prisma. O papel do antigo directUrl está em prisma.config.ts: as migrations leem DIRECT_URL. O servidor Next lê DATABASE_URL.
No projeto, abra Connect (ou Project Settings → Database). Escolha o formato URI e copie duas URLs.
pooler.supabase.com e a região sa-east-1. Essa é a DATABASE_URL. Confira se a URL termina com ?pgbouncer=true. Se o painel não incluir esse parâmetro, acrescente. O adapter não usa prepared statements nomeados, que é o que o pooler de transação exige; o driver pg ignora o parâmetro pgbouncer em si.db.<ref>.supabase.co, porta 5432). Essa é a DIRECT_URL. Não use a porta 6543 aqui: o prisma migrate não atravessa o pooler de transação.Se a senha do banco tiver @, # ou espaço, troque esses caracteres pelo código percentual na URL, ou gere outra senha no painel.
O RLS fica ligado nas tabelas, sem política. O papel postgres, usado por essas duas URLs, ignora o RLS. A API REST pública do Supabase continua sem acesso. A migration multiempresa liga o RLS também em empresas e limites_cadastro.
Em Project Settings → Environment Variables, no ambiente Production (e no Preview, se ele apontar para um banco):
| Variável | Valor |
|---|---|
DATABASE_URL | Pooler de transação, porta 6543, com ?pgbouncer=true |
DIRECT_URL | Conexão direta ou pooler em modo sessão, porta 5432 |
AUTH_SECRET | Saída de openssl rand -base64 32 |
ASAAS_ENCRYPTION_KEY | Outra saída de openssl rand -base64 32, diferente do AUTH_SECRET e diferente da chave da API do Asaas. A mesma chave cifra a Resend e o token da subconta Twilio de cada empresa. Não rotacione se já houver segredo gravado. |
TWILIO_ACCOUNT_SID | Account SID da conta pai. |
TWILIO_AUTH_TOKEN | Auth token da conta pai. |
TWILIO_WEBHOOK_URL | https://<seu-dominio>/api/webhooks/twilio |
TWILIO_WHATSAPP_TESTE | Número de teste do dono, em formato nacional ou E.164. |
CRON_SECRET | Saída de openssl rand -base64 32. A Vercel Cron envia esse valor em Authorization: Bearer para /api/cron/regua. |
Não defina ASAAS_MOCK nem TWILIO_MOCK na Vercel. Em produção a integração chama https://api.asaas.com/v3 ou https://api-sandbox.asaas.com/v3, conforme o ambiente salvo pelo dono, e envia a chave no cabeçalho access_token. A chave real ainda não entra neste repositório.
Essas variáveis precisam existir no build e em runtime. O postinstall e o script build rodam prisma generate, e o arquivo prisma.config.ts lê DIRECT_URL ao carregar. A aplicação lê DATABASE_URL e AUTH_SECRET quando atende um pedido. O cookie de sessão sai com secure quando NODE_ENV é production.
Não defina ADMIN_SENHA na Vercel e não rode npm run db:seed contra o banco de produção. O seed de exemplo está bloqueado quando NODE_ENV é production.
O build da Vercel não aplica migration.
Num banco vazio, no futuro, o caminho normal continua sendo npx prisma migrate deploy, que aplica as migrations em ordem. A pasta database/ é uma cópia legível desses SQLs; a fonte da verdade continua em prisma/migrations.
Com DATABASE_URL (ou, se só ela existir, DIRECT_URL) apontando para o mesmo banco:
ADMIN_NOME="Seu Nome" \
ADMIN_EMAIL="[email protected]" \
ADMIN_SENHA="uma-senha-longa" \
npm run criar-admin
O script grava o hash da senha do mesmo jeito que o login confere, na empresa Collab Software, como dono e superadministrador. Se o e-mail já existir, ele para e não troca a senha. Não use este comando para o Guilherme depois da migration multiempresa: esse acesso já fica marcado.
src/lib — CPF/CNPJ, máscaras, rótulos e os esquemas de validação usados tanto na tela quanto no servidor.src/server/services — regras de empresa, usuário, cliente, filial, serviço, resultado, Asaas e fatura. Cliente, fatura e configuração sempre recebem a empresa da sessão, nunca um id vindo do navegador. O arquivo src/server/services/index.ts é o ponto de entrada estável.src/app/api — rotas HTTP finas em cima desses serviços. As telas chamam essas rotas para criar, editar e excluir. POST /api/webhooks/asaas não usa o cookie: autentica o token da empresa.prisma/schema.prisma — Empresa 1-N Usuario, Cliente e Servico. Cliente 1-N Filial, ClienteServico e Fatura. ClienteServico guarda o valor combinado daquele cliente. ConfiguracaoAsaas e PreferenciaCobranca são uma por empresa. O campo Empresa.plano continua sendo a assinatura da empresa na plataforma, separado desse catálogo e separado da cobrança no Asaas. As migrations em prisma/migrations são a fonte da verdade do banco.database/ — os mesmos SQLs, com comentário curto, para quem não usa o Prisma.Um servidor MCP futuro (buscar cliente, listar filiais, e as ações de escrita) deve importar os serviços, não o Prisma. Os próximos módulos entram como tabela, serviço e rota próprios:
Dono e financeiro ativam a cobrança recorrente nas vendas MENSAL ativas. Forma, dia de vencimento, texto da descrição, multa, juros, desconto e a opção de atualizar cobranças em aberto começam pelas preferências da empresa, gravadas pelo dono em Asaas. Cada venda e o lote podem trocar esses valores. Sem percentual informado, a cobrança não envia multa, juros nem desconto, e o Asaas conserva o padrão da conta. O prazo de gerar a cobrança antes do vencimento não vai na assinatura: o Asaas usa 7, 14 ou 40 dias na configuração da conta, e o número escolhido fica só na preferência. A assinatura usa ciclo mensal. Dá para editar, pausar ou cancelar. O lote mostra a prévia antes de criar. As cobranças da assinatura entram em Faturas pelo webhook (o pagamento traz o campo subscription) e pelo botão Importar faturas, sem duplicar. Suporte não vê faturas nem recorrência. A chave da API continua só com o dono.
A régua de cobrança fica em Régua, só para o dono alterar e para o financeiro consultar. A empresa liga a régua, monta várias réguas nomeadas (uma é a padrão) e define, em cada uma, os passos, o horário e se o envio sai no fim de semana. O cliente usa a padrão, outra régua, nenhuma, ou fica pausado. O passo pode ser na emissão ou em dias antes, no vencimento ou em atraso, com assunto e texto usando {cliente}, {servico}, {valor}, {vencimento}, {link}, {dias_atraso} e {empresa}. O e-mail sai pela Resend da própria empresa; a chave fica cifrada com ASAAS_ENCRYPTION_KEY e a tela só diz se ela está configurada. O mesmo passo pode marcar WhatsApp. Aí a mensagem usa um template UTILITY da Twilio (boleto gerado, vence em X dias, venceu ou pago), com nome, valor, vencimento e link do Asaas. A empresa tem uma subconta e um Sender. O webhook POST /api/webhooks/twilio valida X-Twilio-Signature. Régua pausada, opt-out e o modo teste seguram o envio: no modo teste o WhatsApp só vai para TWILIO_WHATSAPP_TESTE. A rota /api/cron/regua roda todo dia, exige CRON_SECRET e também esvazia a fila. Fora de produção o envio não chama a Resend nem a Twilio.
A forma também pode ser cartão de crédito. A assinatura nasce no Asaas com billingType CREDIT_CARD e sem dados de cartão: o cliente informa o cartão na página da primeira cobrança, e o Asaas passa a cobrar sozinho. A venda mostra “Aguardando cadastro do cartão” até o webhook confirmar o pagamento, “Cartão cadastrado” (só bandeira e quatro últimos dígitos, quando o Asaas os devolve) ou “Cartão recusado”. O número completo e o CVV não entram no banco nem no log. Qualquer cobrança com link pode ser copiada, aberta no WhatsApp de quem está usando o sistema ou no e-mail, com o texto gravado nas preferências da empresa ({cliente}, {servico}, {valor}, {vencimento}, {link}). Nada disso envia a mensagem sozinho.
A Cora não é alterada por estes módulos.
O cliente aceita CPF ou CNPJ. A filial aceita CNPJ. A validação usa o cálculo dos dígitos verificadores. No CNPJ, a raiz pode ter letras (formato alfanumérico): o valor de cada caractere é o código ASCII menos 48, e os dois dígitos verificadores continuam numéricos. O documento é guardado sem pontuação e mostrado com máscara.
TWILIO_MOCK | Só fora de produção. true em produção é recusado. Fora de produção o envio já fica no mock e não chama a Twilio. |
CRON_SECRET | Segredo da rota diária /api/cron/regua. A Vercel Cron envia Authorization: Bearer. Sem ele a rota responde 500 e não envia. Não é a chave da Resend. |
ASAAS_MOCK | Só no computador de desenvolvimento. true usa um Asaas em memória, sem rede e sem chave real, para a tela funcionar antes de existir a chave. Se NODE_ENV for production, a aplicação recusa esse modo. Não defina na Vercel. |