BikeControl (SwiftControl)
Use Zwift Click, Zwift Play, Shimano Di2, or any Bluetooth controller with MyWhoosh, Rouvy, Zwift, and more. Free to try on iOS, Android, Windows, and macOS.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
Projeto destinado ao meu controle de treinos
翻訳待ち
Loading the full README.
Read the full README ↗Aplicativo web para gerenciar treinos de musculação com foco em hipertrofia: rotina semanal editável, execução de treino ao vivo com registro de séries, progressão dupla de carga, estatísticas, histórico, recuperação estimada por grupo muscular e biblioteca de exercícios.
Requisitos: Node.js 18.18+ (recomendado 20 ou 22) e npm.
npm install
npm run dev
Abra http://localhost:3000. Sem configurar nada, o app roda em modo local: os dados ficam no localStorage do navegador e não são sincronizados.
| Script | O que faz |
|---|---|
npm run dev | Servidor de desenvolvimento |
npm run build | Build de produção (inclui checagem de tipos) |
npm run start | Serve o build de produção |
npm run typecheck | tsc --noEmit |
npm run lint | ESLint (regras next/core-web-vitals + next/typescript) |
npm run test | Testes unitários com Vitest (cálculos, persistência, backup) |
npm run check | typecheck + lint + test + build |
npm run vapid | Gera o par de chaves VAPID para as notificações push (uma vez por projeto) |
Copie .env.example para .env.local. Nenhuma variável é obrigatória.
| Variável | Obrigatória | Descrição |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | não | URL do projeto Supabase. Ativa o modo nuvem quando presente junto da chave. |
NEXT_PUBLIC_SUPABASE_ANON_KEY | não | Chave pública anon. Nunca use a service_role no frontend. |
NEXT_PUBLIC_ALLOW_LOCAL_MODE | não | Com o Supabase configurado a conta é obrigatória. true volta a oferecer "Usar modo local neste dispositivo" na tela de login. |
NEXT_PUBLIC_VAPID_PUBLIC_KEY | não | Chave pública VAPID (Web Push). Ativa a notificação de fim de descanso com a tela bloqueada. Ver seção 11. |
VAPID_PRIVATE_KEY | não | Chave privada VAPID. Só no servidor (sem prefixo NEXT_PUBLIC_): é usada pela rota /api/rest-timer para assinar os pushes. |
VAPID_SUBJECT | não | Contato do responsável exigido pelo protocolo (mailto:... ou https://...). |
Chaves privadas nunca chegam ao navegador: a service_role do Supabase não é usada em lugar nenhum e a VAPID_PRIVATE_KEY só é lida pela rota de API. Toda a segurança de dados no Supabase vem das políticas RLS (ver migrations).
| Modo local | Supabase | |
|---|---|---|
| Quando | Variáveis ausentes (ou NEXT_PUBLIC_ALLOW_LOCAL_MODE=true e o usuário escolheu "Usar modo local") | Variáveis presentes; login obrigatório |
| Onde salva | localStorage com prefixo ironlog:v1: | Tabela public.user_documents (uma linha por chave) |
| Sincroniza entre dispositivos | Não (o app deixa isso explícito na interface) | Sim |
| Backup | Exportar/importar JSON em Perfil | Idem, além da nuvem |
A camada de persistência é isolada (lib/storage/): ambos os modos salvam os mesmos seis documentos JSON (settings, exercises, routine, sessions, activeSession, restLogs). Migrar do local para a nuvem é exportar o backup e importar após entrar na conta.
supabase/migrations/0001_init.sql. Ele cria a tabela user_documents, habilita RLS e cria políticas para que cada usuário leia/escreva somente as próprias linhas. Alternativamente, com a CLI: supabase db push..env.local:
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
npm run dev. A tela inicial passa a pedir login (ou permite continuar em modo local).vercel.json já define npm run build).NEXT_PUBLIC_SUPABASE_URL e NEXT_PUBLIC_SUPABASE_ANON_KEY para os ambientes Production/Preview. Para notificações com a tela bloqueada, adicione também NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY e VAPID_SUBJECT (seção 11).npm i -g vercel
vercel login
vercel # primeiro deploy (preview)
vercel --prod # produção
Para variáveis: vercel env add NEXT_PUBLIC_SUPABASE_URL production (repita para a chave e para preview).
Em Authentication → URL Configuration no Supabase:
This is the beginning of the README. Read the rest at the original. Read the full README ↗