replace duckdb-ui with ttyd shell: add /query HTTP endpoint, fix utf-8/locale, region config
- swap DuckDB UI for ttyd web terminal (--writable, -readonly db) - add POST /query endpoint with X-Password auth for curl-based SQL execution - fix UTF-8 rendering: set LANG/LC_ALL=C.UTF-8 in container - pass BUCKET_REGION env var for correct S3 signing region - simplify start.sh: drop Xvfb, views.duckdb generation, blocking duckdb -ui - add less, ncurses-bin to Dockerfile for proper pager/terminal support - update Caddyfile: single route to ttyd with flush_interval -1 for websocket - update README to reflect current architecture and document /query usage - remove duckdb-ui.service, schemas.json, file_tree.md (generated artifacts)
This commit is contained in:
156
README.md
156
README.md
@@ -1,26 +1,13 @@
|
||||
# baseldosdados
|
||||
|
||||
Mirror completo das tabelas públicas do projeto [Base dos Dados](https://basedosdados.org/) — 533 tabelas, ~675 GB em Parquet+zstd — hospedado no Hetzner Object Storage e acessível via DuckDB.
|
||||
Mirror completo das tabelas públicas do projeto [Base dos Dados](https://basedosdados.org/) — 533 tabelas, ~675 GB em Parquet+zstd — hospedado no Hetzner Object Storage e acessível via DuckDB no browser.
|
||||
|
||||
## O que tem aqui
|
||||
## Scripts
|
||||
|
||||
| Script | Função |
|
||||
|---|---|
|
||||
| `roda.sh` | Exporta BigQuery → GCS → Hetzner S3 (pipeline principal) |
|
||||
| `prepara_gui.py` | Cria views DuckDB sobre os parquets do S3 para exploração local |
|
||||
| `gera_schemas.py` | Gera `schemas.json` e `file_tree.md` com metadados de todos os parquets |
|
||||
|
||||
**Arquivos gerados:**
|
||||
|
||||
| Arquivo | Descrição |
|
||||
|---|---|
|
||||
| `schemas.json` | Schema completo de todas as 533 tabelas (colunas, tipos, tamanhos) |
|
||||
| `file_tree.md` | Árvore do bucket S3 com tamanhos e contagem de arquivos |
|
||||
| `basedosdados.duckdb` | Banco DuckDB com views para todas as tabelas (gerado por `prepara_gui.py`) |
|
||||
| `all_tables.txt` | Lista completa de tabelas descobertas |
|
||||
| `done_tables.txt` | Tabelas exportadas com sucesso para o GCS |
|
||||
| `done_transfers.txt` | Datasets transferidos com sucesso para o S3 |
|
||||
| `failed_tables.txt` | Tabelas que falharam após 3 tentativas |
|
||||
| `prepara_db.py` | Cria `basedosdados.duckdb` com views sobre os parquets do S3 |
|
||||
|
||||
## Fluxo de exportação
|
||||
|
||||
@@ -29,12 +16,11 @@ BigQuery (basedosdados) → GCS (Parquet + zstd) → Hetzner Object Storage (rcl
|
||||
```
|
||||
|
||||
1. Descobre automaticamente todos os datasets e tabelas via API do BigQuery
|
||||
2. Exporta todas as tabelas em paralelo no formato Parquet com compressão zstd
|
||||
2. Exporta em paralelo no formato Parquet com compressão zstd
|
||||
3. Transfere GCS → Hetzner Object Storage via rclone (streaming direto, sem disco local)
|
||||
4. Verifica a contagem de arquivos entre GCS e S3
|
||||
5. Oferece opção de deletar o bucket GCS ao final
|
||||
|
||||
O script suporta **resume automático**: se interrompido, basta rodar novamente — tabelas e transfers já concluídos são pulados.
|
||||
Resume automático: se interrompido, basta rodar novamente — tabelas e transfers já concluídos são pulados.
|
||||
|
||||
## Estrutura dos dados no S3
|
||||
|
||||
@@ -45,36 +31,15 @@ s3://<HETZNER_S3_BUCKET>/
|
||||
└── *.parquet
|
||||
```
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
**Exportação (`roda.sh`) — execução local:**
|
||||
- `google-cloud-sdk` (`bq`, `gcloud`, `gsutil`)
|
||||
- `parallel` (GNU parallel)
|
||||
- `rclone`
|
||||
- `flock`
|
||||
|
||||
**Execução via VM (`--gcloud-run`):** apenas `gcloud` localmente — dependências instaladas automaticamente na VM.
|
||||
|
||||
**Scripts Python** (`prepara_gui.py`, `gera_schemas.py`):
|
||||
- `duckdb`, `pyarrow`, `boto3`, `s3fs`, `python-dotenv`
|
||||
|
||||
Autenticação GCP (uma vez antes da exportação):
|
||||
|
||||
```bash
|
||||
gcloud auth login
|
||||
gcloud auth application-default login
|
||||
gcloud config set project SEU_PROJECT_ID
|
||||
```
|
||||
|
||||
## Configuração
|
||||
|
||||
Crie um arquivo `.env` na raiz:
|
||||
Crie um arquivo `.env`:
|
||||
|
||||
| Variável | Descrição |
|
||||
|---|---|
|
||||
| `YOUR_PROJECT` | ID do seu projeto GCP (para faturamento) |
|
||||
| `BUCKET_NAME` | Nome do bucket GCS intermediário |
|
||||
| `BUCKET_REGION` | Região do bucket — deve ser `US` |
|
||||
| `BUCKET_REGION` | Região do bucket S3 (ex: `eu-central`) |
|
||||
| `SOURCE_PROJECT` | Projeto fonte (`basedosdados`) |
|
||||
| `PARALLEL_EXPORTS` | Jobs paralelos de exportação BigQuery (padrão: 8) |
|
||||
| `HETZNER_S3_BUCKET` | Nome do bucket no Hetzner Object Storage |
|
||||
@@ -83,74 +48,75 @@ Crie um arquivo `.env` na raiz:
|
||||
| `PARALLEL_UPLOADS` | Datasets enviados em paralelo (padrão: 4) |
|
||||
| `AWS_ACCESS_KEY_ID` | Access key do Hetzner Object Storage |
|
||||
| `AWS_SECRET_ACCESS_KEY` | Secret key do Hetzner Object Storage |
|
||||
| `BASIC_AUTH_PASSWORD` | Senha de acesso ao shell web e endpoint `/query` |
|
||||
|
||||
## Uso
|
||||
## Uso — exportação
|
||||
|
||||
```bash
|
||||
# Exportação
|
||||
chmod +x roda.sh
|
||||
./roda.sh --dry-run # estima tamanho e custo antes de rodar
|
||||
./roda.sh --dry-run # estima tamanho e custo
|
||||
./roda.sh # execução local
|
||||
./roda.sh --gcloud-run # cria VM no GCP, roda lá e deleta a VM ao final
|
||||
|
||||
# Exploração via DuckDB
|
||||
python prepara_gui.py # cria basedosdados.duckdb com views para todas as tabelas
|
||||
duckdb --ui basedosdados.duckdb
|
||||
|
||||
# Dump de schemas
|
||||
python gera_schemas.py # gera schemas.json e file_tree.md (~21 MB de egress)
|
||||
./roda.sh --gcloud-run # cria VM no GCP, roda lá e deleta ao final
|
||||
```
|
||||
|
||||
## Servidor com UI protegida por senha
|
||||
|
||||
Para expor o DuckDB UI num servidor com HTTPS e autenticação básica, use o [Caddy](https://caddyserver.com/) como reverse proxy.
|
||||
|
||||
**Pré-requisitos no servidor:** `caddy`, `htpasswd` (pacote `apache2-utils`), `duckdb`
|
||||
|
||||
**1. Instalar o serviço DuckDB UI**
|
||||
|
||||
Edite `duckdb-ui.service` com o usuário e caminho corretos e copie para o systemd:
|
||||
Autenticação GCP necessária antes da primeira exportação:
|
||||
|
||||
```bash
|
||||
# edite User= e WorkingDirectory= e EnvironmentFile= no arquivo
|
||||
cp duckdb-ui.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now duckdb-ui
|
||||
gcloud auth login
|
||||
gcloud auth application-default login
|
||||
gcloud config set project SEU_PROJECT_ID
|
||||
```
|
||||
|
||||
**2. Configurar o Caddy**
|
||||
|
||||
Edite `Caddyfile` substituindo `your.domain.com` pelo domínio real, depois:
|
||||
|
||||
```bash
|
||||
cp Caddyfile /etc/caddy/Caddyfile
|
||||
systemctl reload caddy
|
||||
```
|
||||
|
||||
O Caddy obtém o certificado TLS via Let's Encrypt automaticamente (portas 80 e 443 abertas no firewall).
|
||||
|
||||
**Trocar a senha:**
|
||||
|
||||
```bash
|
||||
htpasswd -nbB -C 10 admin NOVA_SENHA | cut -d: -f2 | base64
|
||||
# cole o resultado no Caddyfile no lugar do hash atual, depois:
|
||||
systemctl reload caddy
|
||||
```
|
||||
|
||||
**Arquivos relevantes:**
|
||||
|
||||
| Arquivo | Função |
|
||||
|---|---|
|
||||
| `Caddyfile` | Config do Caddy: HTTPS + basicauth → proxy para localhost:4213 |
|
||||
| `duckdb-ui.service` | Serviço systemd que sobe o DuckDB UI em background |
|
||||
|
||||
---
|
||||
|
||||
### `--gcloud-run`
|
||||
|
||||
Cria uma VM `e2-standard-4` Debian 12 em `us-central1-a`, copia o script e o `.env`, instala as dependências e executa via SSH. Variáveis opcionais:
|
||||
Cria uma VM `e2-standard-4` Debian 12 em `us-central1-a`, copia o script e o `.env`, instala dependências e executa via SSH.
|
||||
|
||||
| Variável | Padrão | Descrição |
|
||||
|---|---|---|
|
||||
| `GCP_VM_NAME` | `bd-export-vm` | Nome da instância |
|
||||
| `GCP_VM_ZONE` | `us-central1-a` | Zona do Compute Engine |
|
||||
|
||||
## Uso — exploração local
|
||||
|
||||
```bash
|
||||
python prepara_db.py # cria basedosdados.duckdb com views para todas as tabelas
|
||||
duckdb basedosdados.duckdb
|
||||
```
|
||||
|
||||
## Acesso web — https://db.xn--2dk.xyz
|
||||
|
||||
Container Docker com Caddy + ttyd expondo um shell DuckDB no browser, protegido por senha.
|
||||
|
||||
### Shell interativo
|
||||
|
||||
Acesse https://db.xn--2dk.xyz → autentique com a senha → shell DuckDB direto no browser.
|
||||
|
||||
### API HTTP (curl)
|
||||
|
||||
```bash
|
||||
# Query simples
|
||||
curl -X POST https://db.xn--2dk.xyz/query \
|
||||
-H "X-Password: <senha>" \
|
||||
--data-binary "SELECT count(*) FROM br_anatel_banda_larga_fixa.densidade_brasil"
|
||||
|
||||
# De um arquivo SQL
|
||||
curl -X POST https://db.xn--2dk.xyz/query \
|
||||
-H "X-Password: <senha>" \
|
||||
--data-binary @query.sql >> result.csv
|
||||
|
||||
# Inline com heredoc
|
||||
curl -X POST https://db.xn--2dk.xyz/query \
|
||||
-H "X-Password: <senha>" \
|
||||
--data-binary @- << 'EOF'
|
||||
SELECT sigla_uf, sum(densidade) AS total
|
||||
FROM br_anatel_banda_larga_fixa.densidade_uf
|
||||
WHERE ano = 2023
|
||||
GROUP BY 1 ORDER BY 2 DESC
|
||||
EOF
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
```bash
|
||||
haloy deploy
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user