Files
adaptador-planilhas/docker/entrypoint.sh
T
Ferraz Fagundes 2be97ea969
ci / lint, typecheck, testes e build (push) Has been cancelled
ci / Postgres e Redis reais (push) Has been cancelled
ci / imagem Docker Linux (push) Has been cancelled
fix(docker): inicializar permissoes do storage com seguranca
2026-10-16 23:47:38 -03:00

126 lines
4.8 KiB
Bash
Executable File

#!/bin/sh
# Entrada única da imagem. Mantém os comandos curtos e garante que todos os
# scripts operacionais rodem no MESMO processo do contêiner.
#
# Por que `node --import tsx` e não o CLI do tsx: o CLI cria um processo filho,
# e o SIGTERM do `docker stop` pararia no pai. O worker precisa receber o sinal
# para esperar o tick corrente e liberar o lock no `finally`.
set -eu
TSX="--import=tsx"
# Armazenamento local (development): o volume `app-storage` montado em /app/.data.
# Homologação e produção usam o bucket do Supabase e não montam este volume.
DATA_DIR=/app/.data
STORAGE_DIR="$DATA_DIR/storage"
# Mesma regra de src/infra/storage/index.ts: sem as três variáveis do Supabase,
# o processo grava no disco local.
local_storage_in_use() {
[ -z "${NEXT_PUBLIC_SUPABASE_URL:-}" ] || [ -z "${SUPABASE_SERVICE_ROLE_KEY:-}" ] || [ -z "${SUPABASE_STORAGE_BUCKET:-}" ]
}
# Web e worker recusam subir com armazenamento local que não aceita escrita.
# Sem isto o processo fica no ar e o primeiro upload responde "Erro interno".
assert_storage_writable() {
local_storage_in_use || return 0
probe="$STORAGE_DIR/.write-probe-$$"
if mkdir -p "$STORAGE_DIR" 2>/dev/null && touch "$probe" 2>/dev/null; then
rm -f "$probe"
return 0
fi
echo "STORAGE_NOT_WRITABLE: $STORAGE_DIR não aceita escrita pelo uid $(id -u)." >&2
echo "Rode o serviço storage-init do compose (docs/spreadsheet-adapter/23-storage-permissions.md)." >&2
exit 1
}
# Roda como root, uma vez, antes de web e worker (serviço storage-init).
# Ajusta só os dois diretórios do armazenamento, sem recursão: arquivos que já
# existem não são tocados, a não ser com --fix-existing, que é passo manual.
prepare_storage() {
if [ "$(id -u)" != "0" ]; then
echo "prepare-storage precisa rodar como root (user: \"0:0\" no serviço storage-init)." >&2
exit 1
fi
app_uid=$(id -u adaptador)
app_gid=$(id -g adaptador)
for dir in "$DATA_DIR" "$STORAGE_DIR"; do
if [ -L "$dir" ]; then
echo "[storage-init] recusado: $dir é link simbólico." >&2
exit 1
fi
mkdir -p "$dir"
owner=$(stat -c '%u:%g' "$dir")
if [ "$owner" != "$app_uid:$app_gid" ]; then
chown "$app_uid:$app_gid" "$dir"
echo "[storage-init] $dir: dono $owner -> $app_uid:$app_gid"
fi
mode=$(stat -c '%a' "$dir")
if [ "$mode" != "750" ]; then
chmod 750 "$dir"
echo "[storage-init] $dir: modo $mode -> 750"
fi
done
if [ "${1:-}" = "--fix-existing" ]; then
# -xdev não atravessa outro sistema de arquivos; -h não segue link simbólico.
find "$DATA_DIR" -xdev ! -user "$app_uid" -exec chown -h "$app_uid:$app_gid" {} +
echo "[storage-init] dono de itens existentes ajustado para $app_uid:$app_gid"
fi
foreign=$(find "$DATA_DIR" -xdev ! -user "$app_uid" | wc -l)
if [ "$foreign" -gt 0 ]; then
echo "[storage-init] AVISO: $foreign item(ns) em $DATA_DIR com outro dono; nada foi alterado neles." >&2
echo "[storage-init] Para ajustar, rode com --fix-existing (procedimento de recuperação no doc 23)." >&2
fi
# Prova com o usuário da aplicação, não com root.
if ! setpriv --reuid="$app_uid" --regid="$app_gid" --clear-groups \
sh -c 'p="$1/.write-probe-$$"; touch "$p" && rm -f "$p"' _ "$STORAGE_DIR"; then
echo "STORAGE_NOT_WRITABLE: o uid $app_uid não escreve em $STORAGE_DIR mesmo após a preparação." >&2
exit 1
fi
echo "[storage-init] STORAGE_WRITABLE: $STORAGE_DIR, dono $app_uid:$app_gid, modo 750"
}
usage() {
cat <<'EOF'
uso: entrypoint.sh <comando> [args]
web servidor Next (saída standalone)
worker processo de fila
migrate [--dry-run]
seed-staging
flag <workspace_id> on|off
cleanup
verify-config
check-bucket
smoke
worker-health confere o heartbeat do worker (healthcheck do contêiner)
prepare-storage [--fix-existing]
prepara /app/.data como root (serviço storage-init)
exec <cmd...> qualquer outro comando
EOF
}
cmd="${1:-web}"
[ $# -gt 0 ] && shift || true
case "$cmd" in
web) assert_storage_writable; exec node server.js ;;
worker) assert_storage_writable; exec node $TSX scripts/worker.ts ;;
prepare-storage) prepare_storage "$@" ;;
migrate) exec node $TSX scripts/migrate.ts "$@" ;;
seed-staging) exec node $TSX scripts/seed-staging.ts "$@" ;;
flag) exec node $TSX scripts/set-flag.ts "$@" ;;
cleanup) exec node $TSX scripts/cleanup-temp.ts "$@" ;;
verify-config) exec node $TSX scripts/verify-config.ts ;;
check-bucket) exec node $TSX scripts/check-bucket.ts ;;
smoke) exec node $TSX scripts/smoke-staging.ts ;;
worker-health) exec node docker/worker-health.mjs ;;
exec) exec "$@" ;;
-h|--help|help) usage ;;
*) echo "comando desconhecido: $cmd" >&2; usage >&2; exit 2 ;;
esac