Widget SVG dinâmico da sua música atual (ou favorita) do Spotify. Cola no README do GitHub, no seu site ou em qualquer lugar que aceite <img>.
| Recurso | Descrição |
|---|---|
| Now Playing | mostra a música que você está ouvindo, em tempo real |
| Track fixa | ou fixa uma música específica pra exibir sempre |
| Aparência sob demanda | tema, fundo, cor do texto e escala, tudo via query param, sem precisar salvar |
| Privacidade | um toggle esconde os dados públicos a qualquer momento |
| Multi-usuário | RBAC completo (admin / user / viewer), pra hospedar pra mais gente |
| Login flexível | senha, GitHub OAuth ou modo público, dá pra combinar |
| Deploy zero-config | docker compose up --build -d e pronto |
flowchart LR
subgraph client["Quem consome o widget"]
gh["README do GitHub<br/>ou site pessoal"]
end
subgraph app["spotify-widget (1 container)"]
admin["Admin SPA<br/>React + Vite"]
api["Backend<br/>Fastify"]
db[("SQLite<br/>via Prisma")]
end
browser["Você, no navegador"]
spotify["Spotify Web API"]
github["GitHub OAuth"]
gh -- "GET /widget<br/>GET /user/api/:user" --> api
browser -- "/admin/*" --> admin
admin -- "fetch /api/*" --> api
api --> db
api -- "now playing / faixa" --> spotify
api -- "login" --> github
O backend serve duas coisas: a rota pública /widget (o SVG) e o painel /admin (a SPA de configuração). Cada usuário guarda as próprias credenciais do Spotify no banco. Não existe client ID/secret "global" no .env.
A forma mais rápida:
git clone https://github.com/KaduKessler/Spotify-Widget.git
cd Spotify-Widget
docker compose up --build -dSem .env, sem segredo pra gerar na mão. Na primeira execução, o container cria e imprime uma senha de admin sozinho. Detalhes na seção "Docker" mais abaixo.
Ou direto na máquina, sem Docker
- Node.js 22+
- pnpm
- Conta Spotify Developer (opcional, só pro modo Now Playing)
git clone https://github.com/KaduKessler/Spotify-Widget.git
cd Spotify-Widget
pnpm install
cp backend/.env.example backend/.env
# edite backend/.env com suas variáveis (veja "Variáveis de Ambiente" abaixo)
cd backend && pnpm exec prisma migrate dev && cd ..
pnpm dev # backend (porta 3000) + admin (porta 5173) juntosAcesse http://127.0.0.1:5173 pro painel em dev, ou http://127.0.0.1:3000/widget pra ver o SVG puro.
Credenciais do Spotify (Client ID/Secret) não vão no
.env. Cada usuário cadastra as suas próprias na aba Configuração do painel, depois de logar. O painel mostra a Redirect URI ({APP_URL}/auth/spotify/callback) pronta pra copiar e colar no app criado em developer.spotify.com/dashboard.
<img src="https://seu-dominio.com/widget?user=seu_username" alt="Spotify Widget" />O jeito mais fácil de montar essa URL é copiar direto do painel (aba Configuração → Embed): ele já monta com a aparência que você escolheu.
Query params disponíveis
| Param | Valores | Efeito |
|---|---|---|
user |
username | qual usuário exibir (obrigatório fora do painel) |
theme |
dark | light |
sobrescreve o tema salvo |
bg |
hex sem #, ou transparent |
cor de fundo customizada |
color |
hex sem # |
cor do texto customizada |
scale |
0.5 a 3 |
escala do widget (1 = tamanho original 495×160) |
spin |
1 | true |
anima a capa do álbum girando |
rainbow |
1 | true |
equalizer com cores em arco-íris |
scan |
1 | true |
mostra o scan code do Spotify (abre a faixa no app) |
<!-- tema light -->

<!-- fundo transparente, texto branco, 150% do tamanho -->
Três abas, depois de logar em /admin:
- Configuração: editor único, preview e controles lado a lado. Modo (Now Playing/Track fixa), tema, aparência (fundo/cor/tamanho), toggle de privacidade, embed pronto pra copiar, credenciais do Spotify.
- Usuários (admin): lista, cria, edita role e reseta senha de qualquer usuário.
- Whitelist GitHub (admin, só com
REGISTRATION_POLICY=github_whitelist): controla quem pode criar conta via GitHub OAuth, um por um ou em lote.
Sistema de autenticação e RBAC (detalhes)
Password
ENABLE_PASSWORD_AUTH=true
ADMIN_USERNAME=seu_usuario
ADMIN_PASSWORD=sua_senhaContas locais com senha hasheada (bcrypt), armazenadas no banco. O admin inicial vem do env como fallback.
GitHub OAuth
ENABLE_GITHUB_AUTH=true
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...Cria conta automaticamente no primeiro login, respeitando a política de registro. O callback ({APP_URL}/auth/github/callback) é montado a partir de APP_URL, não existe variável separada pra ele.
None (público)
ENABLE_NONE_AUTH=trueSem autenticação nenhuma. Útil pra uso pessoal ou demo.
| Role | Permissões |
|---|---|
admin |
acesso total: gestão de usuários, config global |
user |
edita a própria config do widget e credenciais |
viewer |
só visualiza |
REGISTRATION_POLICY=open # open | github_whitelist | invite_only | closedopen: qualquer pessoa cria contagithub_whitelist: só usuários GitHub na whitelistinvite_only: bloqueia todo registro novo (só quem já tem conta loga); sistema de token de convite foi avaliado e descartado, não existe hoje nem tá planejadoclosed: nenhum registro novo
GITHUB_WHITELIST=user1,user2,user3Essa lista do .env é importada pro banco automaticamente na primeira execução, e complementa (não substitui) a whitelist gerenciada pelo painel. Lá dá pra adicionar em lote, validar contra a API do GitHub, rastrear quem adicionou e remover com auditoria (soft-delete, dá pra reativar depois).
ADMIN_USERS=admin,johndoe,janedoe # username do GitHub vira role admin no login OAuth (não afeta password auth)
ALLOW_PASSWORD_SIGNUP=true # false = só o admin inicial do env logaAPI Endpoints (detalhes)
Públicos
GET /widget?user=username- SVG do widgetGET /user/api/:username- JSON com a track atual (respeita a flag de privacidade)GET /api/whitelist/:username- checa se um username tá na whitelist GitHubGET /health,GET /ready- health/readiness check
Autenticação
POST /auth/login,POST /auth/logoutGET /auth/github,GET /auth/github/callbackGET /auth/spotify,GET /auth/spotify/callbackGET /api/auth-config- providers e política de registro ativos
Autenticados
GET /api/me- usuário logado (com role)GET /api/config,POST /api/config- config do widgetGET /api/spotify-config,POST /api/spotify-config,DELETE /api/spotify-configGET /api/spotify/status,GET /api/spotify/now-playingPOST /api/spotify/disconnect- remove tokens OAuth do Spotify da conta
Admin only
GET /api/admin/users,POST /api/admin/usersPUT /api/admin/users/:username/role,PUT /api/admin/users/:username/passwordGET /api/admin/whitelist,POST /api/admin/whitelist,POST /api/admin/whitelist/batch,DELETE /api/admin/whitelist/:username
O toggle "Expor dados no JSON público" (modal Flags) controla o endpoint /user/api/:username: ligado, retorna os dados da track; desligado, responde 204 No Content e esconde tudo. Bom pra pausar a exibição sem desconfigurar o widget.
docker compose up --build -d
docker compose logs -f app # ver a senha gerada na 1ª execução================================================================
Nenhum ADMIN_PASSWORD definido. Senha gerada pra você:
usuário: owner
senha: aMrsZxNJL_3i11RY
Salva em /app/data/.admin_password. Troque depois de logar.
================================================================
Acesse http://localhost:3000/admin/ com essas credenciais.
Fixar suas próprias credenciais
Crie um .env na raiz (mesma pasta do docker-compose.yml), o Compose lê automaticamente:
SESSION_SECRET=gere_com_openssl_rand_hex_32
ADMIN_USERNAME=seu_usuario
ADMIN_PASSWORD=sua_senha_forte_min_8_chars
ENABLE_GITHUB_AUTH=false
APP_URL=http://localhost:3000
ADMIN_URL=http://localhost:3000/admin
ADMIN_USERNAME=adminsozinho é rejeitado de propósito (checagem de segurança). Use qualquer outro valor.
Acesse sempre pelo mesmo host configurado em
APP_URL/ADMIN_URL.localhoste127.0.0.1são origens diferentes pra cookie de sessão, mesmo apontando pro mesmo lugar: configurou comlocalhost, acesse comlocalhost(não127.0.0.1), senão o login quebra.
Comandos úteis
docker compose down # parar
docker compose down -v # parar e apagar volumes (inclui o banco!)
docker compose build --no-cache # forçar rebuild ignorando cacheNotas: o compose mapeia ./data:/app/data pra persistir o banco e os segredos gerados. Healthcheck em /health a cada 30s.
Só rodar, sem baixar código nem buildar nada:
docker run -d --name spotify-widget \
-p 3000:3000 \
-v ./data:/app/data \
ghcr.io/kadukessler/spotify-widget:latestMesmas variáveis de ambiente da seção acima (-e SESSION_SECRET=... -e ADMIN_USERNAME=... etc, ou um --env-file .env). latest segue a versão estável mais recente (tag vX.Y.Z); imagem também publicada com tags de versão fixa pra quem quiser pinar (:v1.0.0, :v1, :v1.0).
Spotify-Widget/
├── Dockerfile # Build multi-stage (admin + backend num container)
├── docker-compose.yml # Deploy em 1 comando
├── docker-entrypoint.sh # Migrations + geração de secrets na 1ª execução
├── backend/ # Servidor Fastify + Prisma
│ ├── .env # Variáveis de ambiente (lido daqui, não da raiz)
│ ├── src/
│ │ ├── routes/ # Endpoints
│ │ ├── lib/ # DB, config, auth helpers
│ │ └── plugins/ # Auth plugin
│ └── prisma/ # Schema e migrations
├── admin/ # Frontend React + Vite
│ └── src/
│ ├── components/ # WidgetEditorCard, UsersPanel, etc
│ └── App.tsx # Orquestra estado + composição das telas
└── TODO.md
pnpm dev # backend + admin juntos, a partir da raiz
pnpm test # suite do backend (Vitest, banco de teste isolado)
pnpm typecheck # tsc --noEmit nos dois pacotesPre-commit hook (husky) roda biome check + typecheck automaticamente antes de cada commit.
Comandos separados
cd backend
pnpm dev # hot reload (tsx watch), porta 3000
pnpm build # compila pra dist/
pnpm test # Vitest
pnpm exec prisma studio # GUI do bancocd admin
pnpm dev # dev server com proxy pro backend, porta 5173
pnpm build # build de produçãoCI: todo push/PR roda lint, typecheck, testes, build dos dois pacotes e valida que o Dockerfile builda (.github/workflows/ci.yml). Nada é publicado nesses pushes — só builda. Pra publicar uma versão de verdade no GHCR:
git tag v1.0.0
git push origin v1.0.0Isso builda e publica ghcr.io/kadukessler/spotify-widget com as tags latest, v1.0.0, v1.0 e v1.
Lidas de backend/.env (ou injetadas direto como env var, no caso do Docker). Não existe PORT/HOST configurável, o servidor sempre sobe em 0.0.0.0:3000, nem credencial global de Spotify: cada usuário guarda a sua própria no painel.
Lista completa
# === Auth Providers (habilite 1 ou mais) ===
ENABLE_PASSWORD_AUTH=true
ENABLE_GITHUB_AUTH=false
ENABLE_NONE_AUTH=false
# === Registration Policy ===
REGISTRATION_POLICY=open # open | github_whitelist | invite_only | closed
ALLOW_PASSWORD_SIGNUP=true
# === Admin Config ===
ADMIN_USERS=user1,user2 # username do GitHub vira role admin no login OAuth (não afeta password auth)
ADMIN_USERNAME=seu_usuario # não pode ser literalmente "admin" (bloqueado por segurança)
ADMIN_PASSWORD=senha_forte # mínimo 8 caracteres, não pode ser "admin"
# === GitHub OAuth (se ENABLE_GITHUB_AUTH=true) ===
GITHUB_CLIENT_ID=seu_github_client_id
GITHUB_CLIENT_SECRET=seu_github_secret
GITHUB_WHITELIST=user1,user2 # para REGISTRATION_POLICY=github_whitelist
# === URLs ===
# O callback do GitHub OAuth é montado como {APP_URL}/auth/github/callback
APP_URL=http://127.0.0.1:3000
ADMIN_URL=http://127.0.0.1:5173
# === Session ===
SESSION_SECRET=chave_aleatoria_segura_aqui # min. 32 chars em produção; gere com: openssl rand -hex 32
# === Criptografia ===
# Criptografa client secret e tokens OAuth do Spotify em repouso no banco.
ENCRYPTION_KEY=chave_hex_64_chars_aqui # gere com: openssl rand -hex 32
# === Security Headers ===
# Ative Helmet em produção para enviar headers de segurança.
# Se você usa um reverse proxy (Nginx Proxy Manager/Cloudflare) que já envia HSTS,
# desative apenas o HSTS do app pra evitar duplicação.
ENABLE_HELMET=true
HELMET_DISABLE_HSTS=true
# === Database ===
DATABASE_URL=file:./data/db.sqlite
GPL-3.0 License
Contribuições são bem-vindas, abra uma issue ou PR.
Construído com Fastify, Prisma, React e Vite.
