Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Web app (PWA) de agendamento e CRM para clínicas de estética, multi-tenant. Regras do projeto em CLAUDE.md; plano de entregas em ROADMAP.md. Pré-requisitos: Node 24, pnpm e Docker. O app usa o driver @neondatabase/serverless em todos os ambientes. Em dev, ele fala com o local-neon-http-proxy do Docker pelo host db.localtest.me, que resolve para 127.0.0.1. Esse host depende de DNS público, então em dev é preciso estar online.README 전문 보기 ↗
Web app (PWA) de agendamento e CRM para clínicas de estética, multi-tenant. Regras do projeto em CLAUDE.md; plano de entregas em ROADMAP.md.
Pré-requisitos: Node 24, pnpm e Docker.
cp .env.example .env.local
pnpm install
pnpm db:up # Postgres 17 + proxy do Neon (docker-compose.yml)
pnpm db:migrate
pnpm db:seed # clínica de exemplo "EasyGlowCare"
pnpm dev # http://localhost:3000/easyglowcare
O app usa o driver @neondatabase/serverless em todos os ambientes. Em dev, ele fala com o
local-neon-http-proxy do Docker pelo host db.localtest.me, que resolve para 127.0.0.1.
Esse host depende de DNS público, então em dev é preciso estar online.
pnpm test # Vitest; usa o banco easyglowcare_test (precisa do pnpm db:up)
pnpm test:e2e # Playwright; veja "Equipe e login"
pnpm test:e2e:prod # build + Playwright em modo produção (igual ao CI)
pnpm typecheck
pnpm lint
pnpm build
O painel da equipe fica em /admin (login em /admin/login). A tela /admin/equipe, onde o dono
cadastra e gerencia a equipe, só abre para o papel dono. A área do cliente (/minha-conta) ainda é
um espaço reservado.
O pnpm db:seed cria estes usuários de exemplo (só para desenvolvimento e staging):
| Nome | Papel | |
|---|---|---|
[email protected] | Marina Alves | dono |
[email protected] | Paulo Reis | recepção |
[email protected] | Ana Souza | profissional |
[email protected] | Beatriz Lima | profissional |
[email protected] | Dra. Carla Mendes | profissional |
A senha de todos é a variável SEED_STAFF_PASSWORD do .env.local, quando definida (de 10 a 128
caracteres). Sem ela, o seed gera uma senha e a imprime no terminal; anote-a, porque ela não é
mostrada de novo. Em produção o seed só pode ser usado uma vez, em banco vazio, com --force (veja
"Neon + Vercel"); depois que houver dados reais, nunca o rode em produção.
A variável SESSION_SECRET (mínimo de 32 caracteres) é obrigatória para o app e para o build: sem
ela o login e as sessões não funcionam. O script pnpm staff:create não a usa (verificado: com ela
vazia ele roda normalmente). Para gerar uma: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))".
pnpm test:e2e roda o Playwright com o Google Chrome instalado e precisa do pnpm db:up. Antes dos
testes, ele refaz o seed no banco local de desenvolvimento com uma senha fixa de teste e limpa a
tabela login_attempts; por isso, depois dele, a senha do seed deixa de ser a do seu .env.local
(rode pnpm db:seed de novo para voltar a ela). Ele se recusa a rodar se DATABASE_URL ou
DATABASE_URL_UNPOOLED não apontarem para o banco local.
O app pode ser instalado no celular, como se fosse um aplicativo. A cliente instala pelo link
"Instalar app" da página da clínica (/<slug>/instalar). A equipe instala pelo link "Instalar o
painel no celular" na tela inicial do painel (/admin/instalar).
pnpm build && pnpm start), não no pnpm dev.A cada PR para staging ou main, e a cada push em staging e main, o GitHub roda o workflow .github/workflows/ci.yml: lint,
checagem de tipos, testes, build e os testes de navegador em modo produção. Ele não usa nenhum
segredo: sobe um Postgres próprio e dados de teste.
Para exigir o CI antes do merge, no GitHub vá em Settings > Branches > Add branch ruleset (ou "Add
rule") e crie a regra para staging e para main. Marque "Require status checks to pass" e escolha
"Lint, tipos, testes, build e navegador". Em repositório privado, exigir o CI antes do merge pode
precisar de plano pago do GitHub.
Se o CI falhar nos testes de navegador, o relatório do Playwright fica em "Artifacts" na página da execução, por 7 dias.
Nenhuma mensagem é enviada na hora: o código grava em message_outbox e um cron envia. Em
desenvolvimento, o provedor console só imprime no terminal o canal, o modelo e o destinatário
mascarado. As ações de acesso e de equipe ficam registradas em audit_log (ainda sem tela;
consulte pelo pnpm db:studio).
| Rota | O que faz | Agendamento (vercel.json) |
|---|---|---|
/api/cron/outbox | Envia as mensagens vencidas (até 50 por execução, 5 tentativas) | por volta das 06:00 de Brasília, todo dia |
/api/cron/cleanup | Apaga tentativas de login e sessões com mais de 30 dias e mensagens enviadas há mais de 90 | por volta das 06:30 de Brasília, todo dia |
As duas rotas exigem o cabeçalho Authorization: Bearer <CRON_SECRET>; sem a variável
CRON_SECRET definida, respondem 401 e nada é processado. Para chamar localmente (com
pnpm dev rodando e CRON_SECRET no .env.local), no PowerShell:
$env:CRON_SECRET = 'o mesmo valor do .env.local'
Invoke-RestMethod http://localhost:3000/api/cron/outbox -Headers @{ Authorization = "Bearer $env:CRON_SECRET" }
Remove-Item Env:CRON_SECRET
Na Vercel: cadastre CRON_SECRET em Production e em Preview, com valores diferentes (gere
com node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"), antes do
merge e do deploy. A Vercel só entrega uma variável nova aos deploys feitos depois que ela foi
cadastrada. Se cadastrar depois, faça um redeploy; senão os crons respondem 401 todo dia, sem
nenhum aviso além do log. A Vercel envia o cabeçalho sozinha nas chamadas de cron.
Os crons da Vercel só rodam no deploy de produção (main). Em staging (Preview) eles não
disparam. Para testar lá, chame as rotas à mão, com o CRON_SECRET de Preview, como no exemplo
local (trocando a URL).
O staging tem a proteção de deploys da Vercel ligada. Por isso a chamada à mão precisa também do
cabeçalho x-vercel-protection-bypass, com a chave criada em Settings > Deployment Protection >
Protection Bypass for Automation:
$env:CRON_SECRET = 'o valor de Preview'
$env:VERCEL_BYPASS = 'a chave do Protection Bypass'
Invoke-RestMethod "https://SEU-ENDERECO-git-staging.vercel.app/api/cron/outbox" -Headers @{ Authorization = "Bearer $env:CRON_SECRET"; 'x-vercel-protection-bypass' = $env:VERCEL_BYPASS }
Remove-Item Env:CRON_SECRET
Remove-Item Env:VERCEL_BYPASS
A migration desta parte roda sozinha no deploy, como todas (veja "Neon + Vercel").
Plano Hobby x Pro: o plano Hobby só aceita cron uma vez por dia, por isso a fila roda às
06:00. No Hobby o horário não é exato: a chamada pode sair em qualquer momento dentro daquela
hora. Antes de ligar os lembretes (Etapa 4), passe para o plano Pro e troque o agendamento da
fila no vercel.json para */5 * * * *. No Hobby, um agendamento mais frequente faz o deploy
falhar.
aws-sa-east-1), um para
produção e outro para staging. A região não muda depois.DATABASE_URL pooled e DATABASE_URL_UNPOOLED direta)
apontam para o banco de produção, e as de Preview para o banco de staging.
Não ative "Create database branch for deployment" na integração do Neon: com ela, cada
Preview, inclusive o staging, ganharia um banco descartável, e as migrations automáticas de
staging iriam para ele.Antes de um deploy (é a lista de conferência de qualquer deploy), siga estes passos nesta ordem:
Cadastre SESSION_SECRET na Vercel (Settings > Environment Variables), em Production e em Preview, com valores diferentes. Gere cada valor com node -e "console.log(require('crypto').randomBytes(32).toString('base64'))".
As migrations rodam sozinhas no build da Vercel, em produção (main) e no staging; outros
branches não alteram nenhum banco. Confira que DATABASE_URL_UNPOOLED existe em Production
(banco de produção) e em Preview (banco de staging). Se uma migration falhar, o deploy falha e
a versão anterior continua no ar.
Toda migration precisa funcionar com a versão anterior do app ainda no ar: acrescentar tabela, coluna opcional ou índice pode; apagar ou renomear se faz em duas etapas, em dois deploys.
Só em emergência, para aplicar à mão em um banco remoto, use a URL direta. No PowerShell:
$env:DATABASE_URL_UNPOOLED = "postgres://…"
pnpm db:migrate
Remove-Item Env:DATABASE_URL_UNPOOLED
A variável de ambiente tem precedência sobre o .env.local (que aponta para o Docker) e continua
definida até você fechar a janela do terminal ou rodar o Remove-Item. Se esquecer, os próximos
comandos nessa janela vão atingir o banco remoto. Coloque a URL só na sua linha de comando; nunca
a grave em arquivo versionado.
Faça o merge e o deploy.
Garanta que a clínica existe (veja "A clínica" abaixo).
Crie o primeiro dono. O comando imprime o banco de destino (só o host) e a clínica antes de inserir, e depois uma senha provisória, uma única vez; a pessoa precisa trocá-la no primeiro login:
$env:DATABASE_URL_UNPOOLED = "postgres://…"
pnpm staff:create -- --tenant <slug> --name "<nome>" --email <e-mail> --role owner
Remove-Item Env:DATABASE_URL_UNPOOLED
--role aceita owner, reception ou professional. Os demais usuários podem ser criados
depois pelo dono, em /admin/equipe.
Fora de ordem: sem SESSION_SECRET ou sem DATABASE_URL_UNPOOLED (em produção ou no
staging), o build falha e o deploy anterior continua no ar. O mesmo vale se uma migration falhar.
Os Previews de PR usam o banco de staging, sem migrar. Um PR que traz migration nova fica com o
Preview quebrado até o merge em staging.
A clínica. staff:create --tenant <slug> exige que a clínica já exista; senão responde
"Clínica não encontrada: ". Hoje a única forma de criar uma clínica é o seed. Em produção ele
só pode ser usado uma vez, em banco vazio, com pnpm db:seed -- --force (URL direta definida
como acima): cria a clínica de exemplo easyglowcare e também os 5 usuários de exemplo, então
desative-os em /admin/equipe depois de criar o seu dono. Depois que houver dados reais, nunca
rode o seed em produção: ele apaga e recria a clínica easyglowcare.
Dono sem acesso. Não há "esqueci minha senha": se o dono perder o acesso, crie outro dono com
pnpm staff:create, usando outro e-mail.
Staging e Previews. Todos os Previews (o staging e os de PR) usam o mesmo banco de staging; não
existe banco por Preview. Não rode o seed em staging: ele apaga e recria a clínica
easyglowcare, junto com os usuários e registros dela. Sem --force, o seed recusa qualquer banco
que não seja local.
As Functions rodam em gru1 (São Paulo), definido no vercel.json.