databridge-server
v0.1.5
Published
HTTP gateway com governança de SQL para consultas read-only em MySQL. Backend do DataBridge.
Downloads
647
Maintainers
Readme
databridge-server
HTTP gateway com governança de SQL para consultas somente leitura em MySQL. Backend do DataBridge.
Expõe /v1/* em Fastify, guarda organizações e usuários num arquivo SQLite local (via better-sqlite3) e cifra as credenciais MySQL em repouso com AES-256-GCM.
Instalação e uso
# Rodar sem instalar (precisa Node >= 18.17)
npx databridge-server
# Ou instalar globalmente
npm i -g databridge-server
databridge-serverServidor sobe em http://0.0.0.0:8787. Dados ficam em ./databridge/app.db (organizações, usuários, políticas por org). Os segredos (DATABRIDGE_JWT_SECRET, DATABRIDGE_MYSQL_SECRETS_KEY) não são persistidos no .db — você precisa defini-los no .env do projeto consumidor. Backup: .db + seu .env (sem a chave, as credenciais MySQL cifradas são irrecuperáveis).
Opções
databridge-server [opções]
--port <n> Porta HTTP (default 8787, env DATABRIDGE_PORT ou PORT)
--sqlite <caminho> Arquivo SQLite (default ./databridge/app.db, env DATABRIDGE_SQLITE_PATH ou SQLITE_PATH)
-v, --version
-h, --helpVariáveis de ambiente
Todas as vars do server usam o prefixo DATABRIDGE_ pra não colidir com outras libs no mesmo .env. As vars de config aceitam os nomes curtos como fallback (útil em PaaS que injetam PORT automaticamente).
| Var | Obrigatória? | Default | Uso |
|---|---|---|---|
| DATABRIDGE_JWT_SECRET | sim | — | Assinatura dos JWT |
| DATABRIDGE_MYSQL_SECRETS_KEY | sim | — | Cifragem AES-256-GCM das credenciais MySQL |
| DATABRIDGE_PORT (fallback PORT) | não | 8787 | Porta HTTP |
| DATABRIDGE_SQLITE_PATH (fallback SQLITE_PATH) | não | ./databridge/app.db | Caminho do arquivo SQLite |
Exemplo de .env no projeto consumidor:
DATABRIDGE_JWT_SECRET=<48 bytes base64url>
DATABRIDGE_MYSQL_SECRETS_KEY=<48 bytes base64url>Gere chaves fortes com:
node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))".gitignore do projeto consumidor
O server cria ./databridge/ no cwd (ou em SQLITE_PATH se definido) e você usa .env para os segredos. Nenhum dos dois deve ir pro git. Adicione ao seu .gitignore:
# DataBridge
databridge/
.envdatabridge/— arquivo SQLite (app.db,app.db-wal,app.db-shm) com credenciais MySQL cifradas e usuários bcrypt..env— ondeJWT_SECRETeMYSQL_SECRETS_KEYmoram. Se caírem no git, comprometem tudo.
Se você já commitou esses arquivos antes de ignorá-los, remova do índice sem apagar do disco:
git rm --cached -r databridge/ .env
git commit -m "stop tracking databridge state"Para verificar que o ignore pegou:
git check-ignore -v databridge/app.db
# → .gitignore:N:databridge/ databridge/app.dbPrimeiro uso (admin)
Criar a organização + admin (testa a conexão MySQL antes de persistir):
curl -X POST http://localhost:8787/v1/orgs \
-H 'Content-Type: application/json' \
-d '{
"slug": "minha-empresa",
"name": "Minha Empresa",
"mysql": {
"host": "replica.mysql.minha-empresa.com",
"port": 3306,
"database": "producao",
"username": "readonly",
"password": "..."
},
"admin": { "login": "admin", "password": "segredo123" }
}'Retorna { token, login, displayName, isAdmin }. Use o token como Authorization: Bearer <token> nas demais rotas.
Rotas (prefixo /v1)
| Método | Rota | Auth | Uso |
|---|---|---|---|
| POST | /orgs | — | Cria org + admin |
| POST | /auth/login | — | { orgSlug, login, password } → JWT |
| GET | /schema | Bearer | information_schema filtrado pela política |
| POST | /query | Bearer | { sql } (só SELECT) |
| GET/PUT | /policy | Bearer admin | Lê/sobrescreve política da org |
| GET/POST/DELETE | /trusted-users[/:id] | Bearer admin | CRUD de trusted users |
Storage pluggable (avançado)
Por padrão o server usa SQLite local — bom pra dev e deploys single-node com disco persistente. Para Cloud SQL, Postgres gerenciado, DynamoDB, ou qualquer backend próprio, você importa buildApp como biblioteca e injeta uma implementação da interface AppStorage:
import {
buildApp,
createSqliteStorage,
type AppStorage,
type AppDeps,
} from "databridge-server";
// segredos, política e criptografia continuam vindo dos helpers padrão:
import { loadEmbeddedPolicy, createMysqlFieldCrypto } from "databridge-server";
import Fastify from "fastify";
const storage: AppStorage = createSqliteStorage(); // ou a sua implementação
const deps: AppDeps = {
storage,
jwtSecret: process.env.DATABRIDGE_JWT_SECRET!,
templatePolicy: loadEmbeddedPolicy(),
mysqlFieldCrypto: createMysqlFieldCrypto(process.env.DATABRIDGE_MYSQL_SECRETS_KEY!),
};
const app = await buildApp(deps);
await app.listen({ port: 8787, host: "0.0.0.0" });A interface está em server-node/src/storage/types.ts. Métodos:
- orgs:
createOrg,getOrgById,getOrgBySlug,updateOrgPolicyJson - users:
createUser,getUserByLogin,getAdminLogin,listTrustedUsers,deleteTrustedUser - lifecycle:
close
Obrigações da implementação:
- Retornar
nullem lookups não encontrados (nunca lançar). - Lançar
StorageUniqueError(field)emcreateOrg/createUserquando houver violação de unicidade (slug duplicado, login já existente). O server converte em HTTP 409. - Ser seguro para chamadas concorrentes (o Fastify processa rotas em paralelo).
Esboço de Postgres:
import { Pool } from "pg";
import { StorageUniqueError, type AppStorage } from "databridge-server";
export function createPostgresStorage(connString: string): AppStorage {
const pool = new Pool({ connectionString: connString });
return {
async createOrg(input) {
try {
await pool.query(
`INSERT INTO organizations (id, slug, name, mysql_host, mysql_port, mysql_database, mysql_username, mysql_password, policy_json, created_at)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULL,$9)`,
[input.id, input.slug, input.name, input.mysql.host, input.mysql.port, input.mysql.database, input.mysql.usernameCiphertext, input.mysql.passwordCiphertext, input.createdAt],
);
} catch (e: any) {
if (e?.code === "23505") throw new StorageUniqueError("slug");
throw e;
}
},
// ... demais métodos análogos ao createSqliteStorage
async close() { await pool.end(); },
};
}O schema SQL equivalente às migrations do SQLite está em server-node/src/sqlite.ts (adapte os tipos para o dialeto alvo — TEXT → VARCHAR/TEXT, etc.).
Segurança
- Guard de SQL rejeita qualquer coisa que não seja
SELECT(paridade comdatabridge-coreem Rust). - Credenciais MySQL são cifradas com
DATABRIDGE_MYSQL_SECRETS_KEYantes de irem pro SQLite. A chave nunca é persistida — fica só no.envdo projeto consumidor. - Senhas de usuários do app são
bcrypt(rounds=12). DATABRIDGE_MYSQL_SECRETS_KEYeDATABRIDGE_JWT_SECRETsão chaves independentes — rotacione uma sem afetar a outra. RotacionarDATABRIDGE_MYSQL_SECRETS_KEYinvalida credenciais MySQL já cifradas.
Licença
ISC
