尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A surpresa fica. A complicação sai.
Apps (iPhone e Android, Codemagic):
docs/apps.md· ficha da versão:docs/lojas/novidades-1.0.md· medição:docs/dados/como-medimos.md.Marca e regras de uso em
docs/marca.md(guia completo e arquivos originais emdocs/marca/). O identificador técnico antigo (tirei) continua só em nomes internos, como o projeto Supabase e a pasta do repositório.
App do amigo oculto: o organizador cadastra os nomes, o app sorteia e gera um link para o grupo do WhatsApp. Cada participante abre o link, toca no próprio nome, descobre quem tirou e, na mesma tela, escolhe o próprio presente (com links de afiliado).
Este repositório tem um código só para web, iOS e Android (Expo + Expo Router). A versão web é a que está no ar para testes; os apps nativos saem do mesmo projeto pelo Codemagic (codemagic.yaml).
Referências: a especificação de produto é o arquivo tirei-spec-desenvolvimento.md (fora do repo); a identidade visual está em docs/marca.md.
src/app/ rotas (Expo Router)
index.tsx "/" web: landing page · app: tela 1 (Início)
app.tsx "/app" tela 1 no web (para testar o protótipo)
entrar.tsx "Tenho um convite": cola o link ou código
criar/nome.tsx tela 2 · nome do grupo (1/3)
criar/pessoas.tsx tela 3 · participantes (2/3)
criar/valor.tsx tela 4 · valor, data, pares, Sortear (3/3)
pronto/[codigo].tsx tela 5 · sorteado → WhatsApp / copiar link
painel/[codigo].tsx tela 6 · painel do organizador (status, lembrar, liberar, remover, adicionar)
g/[codigo].tsx link do convite → telas 7, 8 e 9
r/[id].tsx redirecionador de afiliado (registra o clique)
minha-conta.tsx sair e excluir a conta (exigência das lojas)
auth.tsx volta dos links de e-mail no app (amigoocultoja://auth)
src/features/ telas compostas (landing, start, participante, rascunho)
src/components/ UI (Screen, Button, Row, Sheet, ícones)
src/lib/ api (RPCs/Edge Functions), device key, share (WhatsApp), analytics, formatação
src/theme/tokens.ts cores, fontes (Figtree), medidas do sistema visual v2
supabase/migrations/ esquema, RLS e funções (0001_init.sql)
supabase/functions/ Edge Functions: draw_group (sorteio) e repair_group (remover/adicionar)
supabase/functions/_shared/draw.ts algoritmo de sorteio (puro, testado)
tests/draw.test.ts 10.000 sorteios por cenário, distribuição, casos impossíveis, reparo
scripts/postexport.mjs renomeia rotas dinâmicas do export estático para a Vercel
npm install
cp .env.example .env # já vem preenchido com o projeto de testes
npm run web # http://localhost:8081
npm test # testes do sorteio (vitest)
npm run typecheck
npm run build # export estático em dist/ (o que a Vercel publica)
iOS / Android (Expo Go ou development build):
npx expo start # escaneie o QR com o Expo Go
# lojas: Codemagic › workflow "Lojas (iPhone e Android)" (docs/apps.md)
Projeto tirei (região sa-east-1). Tudo que o cliente faz passa por funções RPC security definer ou pelas Edge Functions; nenhuma tabela é legível diretamente (RLS ligado sem policies, exceto products).
Identidade. Dois mecanismos convivem (migração 0002_auth.sql):
device key, 64 hex) guardada localmente; o banco só conhece o hash SHA-256. Assumir um nome vincula o nome à chave./conta); todo o preenchimento anterior fica no rascunho local. O grupo guarda owner_user_id e owner_key_hash; is_owner() aceita qualquer um dos dois, então o organizador vê o painel e "Seus grupos" em qualquer aparelho logado. As Edge Functions leem o JWT do usuário no Authorization.Contas e DNS: quem é dono de cada conta (Supabase, Vercel, Google Cloud, lojas, e-mail) e os registros de DNS ficam fora do repositório, com o dono do projeto.
Para ativar os logins no painel do Supabase (Authentication → Providers / URL Configuration):
Google: criar OAuth Client ID (Web) no Google Cloud, colar client ID/secret no provider Google e adicionar https://qqzlqnvreftablfyonux.supabase.co/auth/v1/callback como redirect no Google.
Apple: Services ID + chave .p8 no provider Apple (exige Apple Developer Program).
URL Configuration: Site URL https://amigoocultoja.com.br e Redirect URLs https://amigoocultoja.com.br/**, amigoocultoja://auth. Sem isso o retorno do Google/Apple é recusado.
E-mails com a marca: os templates (confirmação, redefinir senha, link mágico, troca de e-mail, convite) estão em supabase/templates/ e são gerados por scripts/email-templates.mjs. Para publicar no projeto com um comando (precisa de um token pessoal em supabase.com/dashboard/account/tokens):
O Supabase só aceita templates personalizados com SMTP próprio. O mesmo comando configura o remetente:
SUPABASE_ACCESS_TOKEN=sbp_... \
SMTP_HOST=smtp.resend.com SMTP_PORT=465 SMTP_USER=resend SMTP_PASS=re_... [email protected] SMTP_NAME="Amigo Oculto Já!" \
node scripts/email-templates.mjs --push --site-url=https://amigoocultoja.com.br
# acrescente --autoconfirm para desligar a confirmação de e-mail nos testes
Para testes, Gmail com senha de app (myaccount.google.com/apppasswords, exige verificação em 2 etapas) funciona na hora. Para o lançamento, use Resend (smtp.resend.com, porta 465, usuário resend, senha = API key) ou Brevo, com o domínio verificado, e o remetente [email protected].
Ou cole cada .html e o assunto (.subject.txt) em Authentication → Email Templates. As imagens da marca ficam em public/email/ e são servidas pelo site (/email/logo-dark.png).
E-mail: funciona sem configuração. "Confirm email" vem ligado: o usuário recebe um link e, ao clicar, volta direto para o sorteio. Para testes rápidos, desligue "Confirm email" em Authentication → Providers → Email (ou configure um SMTP próprio: o remetente padrão tem limite baixo de envios por hora).
Segredo do sorteio (P0-04). assignments só é lida por rpc_my_result, que exige a chave do próprio participante. O organizador não tem nenhum privilégio extra: rpc_panel devolve apenas status (não abriu / entrou / viu / lista pronta). O teste em SQL (seção abaixo) tenta ler como anon e como organizador e precisa falhar.
| Função | Quem chama | O que faz |
|---|---|---|
Edge draw_group | organizador | cria grupo + participantes + exclusões, sorteia e grava (service role) |
Edge repair_group | organizador | remove/adiciona pessoa com reparo mínimo (spec §9.3) |
rpc_get_group | qualquer um com o link | nome, valor, data, nomes e quem já entrou |
rpc_claim | participante | vincula o nome ao aparelho (trava) |
rpc_my_result | participante | quem eu tirei + lista dele + minha lista; marca revealed_at |
rpc_set_wish_items | participante | salva até 3 itens de 4 tipos (kind): sugestão do catálogo, busca na Amazon, produto escolhido na Amazon (só o ASIN; a URL é montada com o nosso ID) e texto fora da Amazon (sem link) |
Edge resolve_amazon_link | participante | abre links curtos da Amazon (a.co, amzn.to) seguindo só os redirecionamentos e devolve o ASIN |
rpc_suggestions | participante | 6 produtos dentro do valor, vitrine varia por pessoa |
rpc_resolve_link | qualquer um | registra outbound_clicks e devolve a URL de afiliado |
rpc_panel / rpc_release / rpc_my_groups | organizador | painel, liberar nome, meus grupos |
rpc_nudge | ambos | lembrete anônimo in-app (quem tirou → quem foi tirado; aparece no link da pessoa; 1 a cada 12 h) e registro do lembrete do organizador |
rpc_track | ambos | eventos de analytics (events) |
Afiliados: a tag e os templates de busca ficam em app_settings (affiliate_tag_amazon, search_url_amazon, default_store). ID de associado da Amazon: amigoocultoja-20 (migração 0003_affiliate_tag.sql). A Amazon encerra a conta se não houver 3 vendas qualificadas em 180 dias. O catálogo (products, migração 0005_catalogo_amazon.sql) tem 28 produtos reais da Amazon em 4 faixas (até R$ 50, 100, 200 e 400), com link /dp/ASIN?tag=amigoocultoja-20. O preço guardado é aproximado e serve só para filtrar pelo valor do grupo; a interface mostra a faixa, porque a Amazon não permite exibir preço fixo fora da API oficial. Revise a lista de tempos em tempos: produtos podem sair de estoque.
Os blocos do $$ ... $$ usados na validação estão em supabase/tests/ (RLS: nenhuma tabela legível por anon; ponta a ponta: claim, trava, segredo, lista, painel, sugestões, redirecionador, liberar).
O conector do Supabase trata as palavras delete/revoke como comandos destrutivos e aguarda confirmação. Por isso a função rpc_set_wish_items em produção executa o delete via execute de string e os revoke rodam dentro de do $$ ... $$. O arquivo 0001_init.sql é a versão legível e equivalente; para aplicar via CLI (supabase db push) ele funciona como está.
vercel.json já define build (npm run build), saída (dist/) e as rewrites das rotas dinâmicas (/g/:codigo etc.). O .env não é versionado. As variáveis públicas (EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_ANON_KEY, EXPO_PUBLIC_BASE_URL) estão em .env.example; o build (scripts/env.mjs) monta o .env a partir dele quando ele não existe, e variáveis definidas no painel da Vercel ou do Codemagic valem sobre ele. Os IDs do Google (EXPO_PUBLIC_GA4_ID, EXPO_PUBLIC_GOOGLE_ADS_ID, EXPO_PUBLIC_GOOGLE_ADS_CADASTRO) vão no painel da Vercel (ver docs/dados/marketing.md). EXPO_PUBLIC_BASE_URL fixa o domínio dos links de convite em https://amigoocultoja.com.br, mesmo quando o site é aberto em tirei.vercel.app ou numa preview.
Domínio amigoocultoja.com.br: configurado na Vercel (o www redireciona para a raiz). Nos apps, os Universal Links / App Links estão em app.json e os arquivos apple-app-site-association e assetlinks.json em public/.well-known/.
events com os nomes da spec §10).