Painel
Em https://<project-domain>/admin/. Tem login próprio, separado dos usuários finais: o e-mail e a senha gerados pelo primeiro nelcota init (a senha é exibida apenas uma vez).
O painel está disponível em português e inglês. Na primeira visita, segue o idioma do navegador (qualquer variante de português recebe português; os demais idiomas recebem inglês); a escolha no menu da conta (Idioma) é lembrada nesse navegador. Mensagens do servidor e textos de erro são em inglês; o painel mostra os erros no idioma escolhido pelo código do erro.
Pré-requisitos
Use um projeto inicializado com credenciais do painel e acesso à origem da API. O administrador do projeto pode executar SQL privilegiado; essa identidade não é um token de usuário nem um login de operador de atualizações autorizado. Esta referência descreve a fonte atual.
Vários projetos
Cada projeto tem seu próprio painel, em seu domínio, mostrando apenas seus dados. No canto superior esquerdo, junto da mascote, o seletor de projetos (nome do projeto atual) lista os outros projetos do host e leva a "Todos os projetos": nome, domínio, estado (ativo / parado), versão e um botão para abrir cada painel.
Com login único (padrão), trocar de projeto no seletor não pede senha. Com login por projeto (nelcota panel-login per-project), o seletor apenas leva ao outro painel, que pede seu próprio login. A tela de login sempre mostra a qual projeto o painel pertence.
Como funciona o login único
Cookies não atravessam domínios diferentes (api.shop.com → api.blog.com), então o painel de origem faz uma transferência:
- O administrador, já autenticado no painel da loja, escolhe "blog" no seletor.
- O painel da loja emite um token para o blog: um JWT HS256 assinado com o segredo compartilhado do host,
aud= blog, válido por 60 s, comjtiúnico. - O navegador abre
https://<blog>/admin/#sso=<token>. O token viaja no fragmento da URL, que não aparece nos logs do servidor nem noReferer. - O painel do blog valida assinatura, destinatário, validade e e-mail do administrador, recusa tokens já usados e cria sua própria sessão. O token sai da URL imediatamente.
Com login por projeto não há segredo compartilhado: a transferência não existe, e cada painel aceita apenas suas próprias credenciais.
Uma SPA em Svelte 5 + shadcn-svelte + Tailwind v4, com CodeMirror 6 no editor SQL, Manrope para a interface e IBM Plex Mono para código. A compilação é embutida no binário: sem CDN e sem Node em produção (inclusive as fontes, por causa da CSP font-src 'self'). Tema escuro por padrão; tema e idioma ficam no menu da conta, no canto superior direito.
As páginas são botões arredondados no topo (em telas estreitas, em uma linha própria; em celulares, no menu). Banco de dados agrupa Tabelas, SQL e Migrações, com abas entre elas dentro de cada página; Login de usuários é acessado pela página Usuários. O indicador de projeto no canto superior esquerdo mostra o projeto, se executa localmente ou em qual host e um ponto que informa se o servidor responde (verificado a cada minuto e quando a janela recupera o foco). A busca (Ctrl K) fica ao lado do indicador da conta. A visão geral começa pelo tráfego da API: requisições nas últimas 24 horas, proporção recusada e proporção com falha, depois cartões para tabelas (quantas estão protegidas), usuários (quantos entraram pelo aplicativo) e chamadas recusadas, ao lado de um gráfico de requisições e recusas da última hora ou dia, com visualização em tabela. As contagens são mantidas em memória por minuto durante 24 horas: reiniciar o servidor as reinicia.
O estado da RLS aparece como uma etiqueta colorida: verde (RLS com políticas), âmbar (RLS ativada sem políticas) e vermelho (tabela exposta sem RLS).
| Recurso | Como usar |
|---|---|
| Editar uma célula | clique duplo; Enter salva, Esc cancela, botão NULL para valores nulos |
| Editar ou inserir uma linha inteira | botão de expandir ao lado da caixa de seleção da linha / "Inserir linha" (painel lateral). Fechar um formulário alterado pergunta se deseja continuar editando ou descartar |
| Excluir várias linhas | marque as caixas e "Excluir N" (uma única transação) |
| Ordenar | clique no cabeçalho da coluna (crescente → decrescente → sem ordenação) |
| Filtrar | "Filtrar" na grade: igual, diferente, contém, maior/menor, é/não é NULL. Filtros ficam na URL no formato da API REST (/admin/tables/orders?status=eq.paid). Página, tamanho da página e ordenação usam _page, _size, _sort e _desc; a visão completa persiste ao alternar entre Dados e Estrutura, recarregar e navegar pelo navegador |
| Exportar uma tabela | "Exportar" → CSV ou JSON, com a ordenação e os filtros da grade. O servidor transmite com buffers limitados e sem limite de linhas de coleção; exportações têm duração máxima de dois minutos e podem falhar por timeout/cancelamento. CSV inclui BOM para acentos no Excel |
| Seguir uma chave estrangeira | a seta em uma célula FK abre a linha referenciada na outra tabela; o cabeçalho mostra → table |
| Executar SQL | Ctrl+Enter; preenchimento automático de tabelas e colunas; modelos e histórico |
| Salvar consultas | Ctrl+S ou "Salvar"; aparecem na barra lateral do editor e na paleta. Cada consulta tem seu próprio rascunho; abrir outra consulta, modelo ou nova consulta mantém o trabalho anterior. Consultas salvas e rascunhos são armazenados neste navegador, por projeto |
| Ler valores longos | "Ver conteúdo completo" em células longas da grade e dos resultados SQL; copie pelo diálogo de detalhes |
| Redimensionar resultados SQL | arraste o separador entre editor e resultados ou coloque o foco nele e use as setas. A divisão é lembrada no navegador |
| Exportar um resultado SQL | "Exportar" acima de cada resultado → CSV ou JSON |
| Paleta de comandos | Ctrl+K (⌘K no Mac) ou botão de busca no canto superior direito: páginas, tabelas, consultas salvas, modelos e ações |
| Criar uma tabela | "Nova tabela" na lista de tabelas: colunas (tipo, padrão, PK, identidade, UNIQUE, chave estrangeira), RLS ativada por padrão e matriz de GRANTs. Mostra o SQL antes de criar |
| Editar a estrutura | aba Estrutura da tabela (/admin/tables/<name>/structure): adicionar, editar e excluir colunas; renomear a tabela; descrição; ativar/desativar RLS; GRANTs por papel; excluir a tabela (digitando seu nome) |
| Políticas | busque tabelas e filtre seu estado de RLS; "Nova política" em cada tabela, com modelos (leitura pública, leitura por usuários autenticados, proprietário lê/cria/atualiza/exclui); editar e excluir; "Ativar RLS" em tabelas sem ela. Expressões longas expandem sob demanda |
| Token service_role | página API, seção expansível de token após os exemplos: emite um token com a validade escolhida; exibido uma vez e nunca armazenado |
| Criar usuário / redefinir senha | página Usuários: "Novo usuário" e "Redefinir senha…" no menu de cada usuário, com gerador de senhas. Mesmas regras do cadastro público. Contas criadas aqui são consideradas confirmadas e o painel não envia convites; usuários finais podem recuperar a própria senha por e-mail quando o projeto tem SMTP configurado (O esquema auth) |
| Gerar migração | página Migrações: alterações de esquema feitas no painel (tabelas, colunas, políticas) que ainda não estão em arquivo, e migrações do projeto (aplicadas ou apenas na pasta). "Gerar migração" baixa V<n>__<name>.sql e a registra como aplicada neste banco; o arquivo vai para migrations/ (veja implantação) |
| Foto de perfil | menu da conta, no canto superior direito: "Adicionar foto…". A imagem é recortada no centro e redimensionada para 256px no navegador; o servidor aceita PNG, JPEG ou WebP até 256 KB. Fica no banco do projeto (nelcota.admin_avatar, fora do alcance dos papéis da API), então funciona em qualquer navegador, mas é por projeto |
| Datas na grade | timestamptz aparece no fuso horário e idioma de quem visualiza; timestamp e date, como armazenados. O valor exato está na dica, no modo de edição e nas exportações |
Exportações CSV protegem importações em planilhas: células e cabeçalhos que parecem fórmulas recebem um prefixo de tabulação entre aspas, inclusive fórmulas escondidas atrás de espaços ou caracteres de controle. Literais numéricos negativos mantêm seus dígitos e formato numérico. Use exportação JSON quando precisar do texto original sem essa neutralização.
| Página | O que contém |
|---|---|
| Visão geral | o que precisa de ação (tabelas desprotegidas ou bloqueadas), próximos passos marcados conforme o estado do projeto (primeira tabela, dados protegidos, um usuário, primeiro login pelo aplicativo), resumo de uma linha e lista de tabelas com quem pode ler e escrever. Páginas com regras de acesso usam linguagem simples; "Detalhes técnicos" mostra RLS, papéis, grants e SQL |
| Tabelas | todas as tabelas do esquema exposto, estimativa de linhas, GRANTs de anon/authenticated e estado da RLS. Listar, inserir, editar e excluir linhas (tabelas com chave primária); criar tabelas e editar a estrutura |
| Editor SQL | executa como proprietário do banco (ignora RLS, mostrado no aviso "Acesso total"), ou como visitante ou usuário autenticado escolhido para testar as regras de acesso (D94); Ctrl+Enter executa a consulta ou apenas a seleção; uma instrução com falha é sublinhada no editor; barra de status com posição do cursor e limites de execução; resultados ficam recolhidos até a primeira execução; rascunhos recebem nomes conforme o que fazem ("Consulta em orders"); consultas salvas são renomeadas clicando no título |
| Migrações | alterações do painel ainda fora de uma migração e migrações do projeto, com "Gerar migração" |
| Armazenamento | buckets com contagem de arquivos, tamanho, limite e tipos aceitos (criar, editar, excluir quando vazio); navegador de arquivos por pasta: upload por seleção ou arraste, progresso por arquivo (pergunta antes de substituir), prévia, download, copiar URL pública, excluir. Formatos passivos de imagem e PDFs até 20 MB têm prévia; texto simples, CSV e JSON até 1 MB. PDFs abrem no visualizador do navegador. Arquivos passam pelas mesmas verificações da API (Armazenamento) |
| Usuários | busca por e-mail, como cada conta entra (senha, Google, GitHub ou apenas links de e-mail), último login, sessões ativas, e-mails não confirmados; criar usuário, confirmar e-mail, redefinir senha (encerra as sessões), encerrar sessões ou excluir usuário |
| Políticas | políticas de cada tabela (USING/WITH CHECK): criar, editar e excluir; tabelas sem RLS; funções que anon pode executar |
| API | navegação por seções, endereço do projeto, como cada papel chama a API, exemplos curl e JavaScript gerados a partir das colunas de cada tabela, token service_role. Tema, linguagem e tabela podem ser compartilhados pela URL |
Conectar um aplicativo e entender o acesso
- API → Conectar com o SDK: comando de instalação, botão Baixar database.ts (o mesmo arquivo escrito por
nelcota types) e linhas decreateClientpara este projeto. Os exemplos alternam entre o SDK TypeScript (padrão), curl efetchsimples, gerados a partir das colunas da tabela escolhida. - Login de usuários: quais formas de entrada estão ativas (cadastro, confirmação de e-mail, recuperação de senha, link mágico, Google, GitHub) e, para cada uma desativada, as variáveis de ambiente que a ativam. Com um provedor ativo, mostra o endereço de callback para cadastrar no Google ou GitHub e as páginas do aplicativo às quais um login pode retornar. Apenas leitura: essas configurações ficam no
.envdo servidor. - Tabelas → Ver como: lê a grade como visitante ou usuário escolhido, com o mesmo papel, claims, grants e RLS aplicados pela API, em uma transação que sofre rollback. É apenas leitura; um papel sem GRANT recebe uma explicação simples em vez de linhas.
- Armazenamento → um bucket → Quem acessa os arquivos: as políticas de
storage.objectsaplicáveis ao bucket, em linguagem simples, e modelos para adicionar mais (qualquer pessoa ou usuários autenticados baixam, usuários autenticados enviam, cada usuário apenas sua própria pasta ou arquivos). São criadas como qualquer alteração do painel, então "Gerar migração" as inclui. Um bucket privado sem regras é sinalizado: apenas o painel eservice_roleo acessam. - Políticas → Bloqueios recentes: as últimas 100 chamadas à API respondidas com 401, 403 ou 429, quem as fez e por quê, em linguagem simples. Ficam apenas em memória (somem ao reiniciar), com o caminho, mas nunca a query string ou o token. Leituras filtradas pela RLS não são recusas (retornam menos linhas) e não aparecem aqui.
Guias e referências
O ícone de livro na barra superior abre Guias e referências em /admin/guides. O leitor inclui os guias registrados do projeto, referências JavaScript/TypeScript e Rust, exemplos SQL e receitas de servidor. Referências exclusivas do repositório e relatórios históricos não são adicionados automaticamente. A busca consulta o texto completo, blocos de código têm botão de cópia e um índice leva a cada seção. No celular, um seletor substitui a barra lateral. Guias e referências seguem o idioma do painel, com traduções dos textos ingleses para português. Exemplos, código inline, URLs e identificadores de seção permanecem iguais. As traduções ficam em docs/i18n/pt-BR/; npm run guides:check verifica cada fragmento contra sua fonte e falha se faltar tradução ou ela estiver desatualizada. Alterações apenas no código são incorporadas diretamente, sem atualizar a tradução.
A ajuda do SDK na página da API e o guia S3 na página de armazenamento abrem neste leitor. Links entre documentos registrados ficam no painel, incluindo seções compartilháveis que sobrevivem a recarregamentos. Fontes e imagens registradas são incorporadas e não exigem acesso ao GitHub. O guia completo de staging Notes abre no GitHub. Alguns links para documentos não registrados não são suportados pelo leitor atual; consulte-os no repositório.
API → Documentação OpenAPI, também disponível na paleta de comandos, abre a referência atual dentro do painel. Busca /rest/v1/ como anon, mostra rotas disponíveis, parâmetros, corpos de requisição, respostas e modelos do esquema e permite baixar o JSON completo. A sessão administrativa do painel não concede privilégios extras na API. Um catálogo vazio indica a tela de acesso da tabela; uma requisição com falha pode ser repetida.
Avisos de RLS
- sem RLS: uma tabela comum com GRANT para
anonouauthenticatede RLS desativada. Quem tiver o GRANT vê e altera todas as linhas. Mostrado em destaque no topo do painel e na página de políticas. - RLS sem políticas: RLS ativada e nenhuma política. Apenas
service_role(e o proprietário) tem acesso: é seguro, mas talvez não seja o que você quer.
Alterar a senha do administrador
nelcota admin-password # single login: new host password, applied to every project
nelcota -p shop admin-password # per-project login: new password for the shop onlyVocê também pode usar um hash argon2id (formato PHC) gerado por qualquer ferramenta: NELCOTA_ADMIN_PASSWORD_HASH='$argon2id$...' no .env (entre aspas simples por causa do $), seguido de docker compose up -d --force-recreate app.
Segurança
- Cookie de sessão
HttpOnly,SameSite=StricteSecureatrás do Caddy; sessões em memória válidas por 12 h (reiniciar o servidor desconecta todos). - Tentativas de login limitadas a 10 por minuto.
- A página em si não contém dados: tudo vem de
/admin/api/*, que exige sessão. Toda requisição que altera estado deve vir da mesma origem. - CSP sem scripts inline (
script-src 'self'),X-Frame-Options: DENY. - Svelte escapa o texto da interface. O leitor renderiza Markdown escapando HTML bruto e rejeitando links inseguros; exemplos de código são renderizados como texto.
- DDL feito pelo painel (tabelas, colunas, políticas): a interface envia uma especificação tipada, e o servidor constrói o SQL com identificadores sempre entre aspas e tipos de uma lista fechada. Expressões (
DEFAULT,USING,WITH CHECK) são SQL por natureza: cada instrução passa pelo protocolo estendido, que recusa uma segunda instrução escondida na expressão, e tudo executa em uma transação (ou nada é aplicado). O SQL é mostrado antes de executar. - Chaves estrangeiras do formulário apontam apenas para o esquema exposto (para
auth.users, use o editor SQL). - O editor SQL usa uma nova conexão por execução e o texto SQL não vai para o log.
- Tokens
service_roleemitidos pelo painel não são armazenados nem registrados em logs (apenas o fato e a validade). - Sem
NELCOTA_ADMIN_EMAILeNELCOTA_ADMIN_PASSWORD_HASH, o painel fica desativado (as rotas/adminnem existem).
Desenvolvimento do painel
cd crates/admin/ui
npm ci
npm run dev # http://127.0.0.1:5173/admin/; proxy: http://127.0.0.1:8000
npm run check # types (svelte-check)
npm run build # generates dist/ (versioned and embedded in the binary)Após alterar guias incorporados/UI, execute npm run build e recompile o binário (cargo build --locked -p nelcota-server). Faça commit do dist/ gerado em um commit de build próprio. A CI verifica divergências. Defina NELCOTA_API_URL para o Vite usar outro backend.
Textos da interface ficam em src/lib/i18n/messages/<area>.ts, com um objeto em português (pt-BR) e outro em inglês (en); o inglês é tipado a partir do português, então uma chave ausente faz npm run check falhar. Nunca escreva diretamente uma string visível ao usuário em um componente.
Guias relacionados
Veja deploy, autenticação, armazenamento e solução de problemas.
Projetos do host no controlador
O controlador de host ainda não lançado adiciona criação de projetos, backups pareados, restauração e remoção com revisão visual, confirmação digitada, nova autenticação do operador e recuperação persistente. Ele usa uma origem HTTPS separada e autoridade de host concedida por SSH. A página Projetos oferece acesso a esse controlador quando cadastrado. A implantação, os layouts aceitos e a ativação estão descritos em docs/host-management.md.