diff --git a/.env.development b/.env.development index dfc65f4..8471e40 100644 --- a/.env.development +++ b/.env.development @@ -1,6 +1,3 @@ -VITE_KEYCLOAK_URL= -VITE_KEYCLOAK_REALM= -VITE_KEYCLOAK_CLIENT_ID= -VITE_API_URL= -# Ativar mock: copie esta linha para .env.development.local e defina como true -VITE_USE_MOCK=false +# Arquivo template, mantido no repositório vazio por convenção. +# Para rodar o portal em dev, copie .env.example para .env e preencha os valores reais. +# .env é ignorado pelo git. diff --git a/.env.example b/.env.example index e670cb0..3e5d02d 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,26 @@ -# Copie este arquivo para .env.development.local e preencha os valores. -# O arquivo .local é ignorado pelo git — nunca commitar credenciais reais. +# Copie este arquivo para .env e preencha os valores. +# .env é ignorado pelo git — nunca commitar credenciais reais neste arquivo de exemplo. +# +# Em produção, popule essas variáveis via secret manager do orquestrador (Docker, K8s, etc.). -# URL base da API REST -VITE_API_URL=https://sistema.modumfiscal.com.br +# ─── Core API ──────────────────────────────────────────────────────────────── +# URL base da API REST consumida pelo BFF (server-side apenas). +NUXT_CORE_API_URL=https://sistema.modumfiscal.com.br -# Keycloak -VITE_KEYCLOAK_URL=https://keycloakprod.modumfiscal.com.br -VITE_KEYCLOAK_REALM=modumfiscal-dev -VITE_KEYCLOAK_CLIENT_ID=portal-modumfiscal-web +# ─── Keycloak (realm dedicado do portal público) ───────────────────────────── +NUXT_KEYCLOAK_URL=https://keycloakprod.modumfiscal.com.br +NUXT_KEYCLOAK_REALM=modumfiscal-portal-dev +NUXT_KEYCLOAK_CLIENT_ID=portal-modumfiscal-bff +# Client confidential — gere no Keycloak (Clients → Credentials → Client secret). +NUXT_KEYCLOAK_CLIENT_SECRET=SUBSTITUIR_PELO_VALOR_REAL_NO_ENV_LOCAL -# Mock — define como true para rodar sem backend (sessão e dados falsos) -# Quando ativo, o bootstrapPrefeitura é pulado e o interceptor de mock é carregado. -VITE_USE_MOCK=false +# ─── Sessão (Token-handler pattern) ────────────────────────────────────────── +# Redis para armazenar tokens server-side e PKCE state. +NUXT_REDIS_URL=redis://localhost:6379 +# Segredo para derivar o cookie de sessão (mín. 32 chars; rotacionar em incidente). +# Gere com: openssl rand -base64 32 OU node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" +NUXT_COOKIE_SECRET=GERE_LOCALMENTE_NUNCA_COMMITAR_SECRET_REAL +# TTL da sessão em segundos (default: 8h = 28800). +NUXT_SESSION_TTL_SECONDS=28800 +# TTL do PKCE state em segundos (default: 5min = 300). +NUXT_PKCE_TTL_SECONDS=300 diff --git a/.gitea/workflows/dev-build-deploy.yml b/.gitea/workflows/dev-build-deploy.yml new file mode 100644 index 0000000..aa2737b --- /dev/null +++ b/.gitea/workflows/dev-build-deploy.yml @@ -0,0 +1,86 @@ +name: Dev Build & Deploy Portal + +on: + push: + branches: + - developer + +# Variáveis necessárias no Gitea (Settings → Variables): +# DEV_NUXT_KEYCLOAK_URL ex: https://keycloakprod.modumfiscal.com.br +# DEV_NUXT_KEYCLOAK_REALM ex: modumfiscal-portal-dev +# DEV_NUXT_KEYCLOAK_CLIENT_ID ex: portal-modumfiscal-bff +# DEV_NUXT_CORE_API_URL ex: https://sistema.modumfiscal.com.br +# DEV_NUXT_REDIS_URL ex: redis://portal-redis:6379 +# +# Secrets necessários (Settings → Secrets): +# REGISTRY_USER +# REGISTRY_PASSWORD +# NUXT_KEYCLOAK_CLIENT_SECRET +# NUXT_COOKIE_SECRET gere com: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" + +jobs: + build-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Define version + id: version + run: | + VERSION=$(date +'%Y.%m.%d.%H%M') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Login registry + env: + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + run: | + echo "$REGISTRY_PASSWORD" | docker login git.modumsolucao.com.br \ + -u "$REGISTRY_USER" \ + --password-stdin + + - name: Build Docker image + run: | + docker build \ + --memory=3g \ + -t git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:latest \ + -t git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:${{ steps.version.outputs.version }} \ + . + + - name: Push image + run: | + docker push git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:latest + docker push git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:${{ steps.version.outputs.version }} + + - name: Deploy DEV + env: + NUXT_KEYCLOAK_URL: ${{ vars.DEV_NUXT_KEYCLOAK_URL }} + NUXT_KEYCLOAK_REALM: ${{ vars.DEV_NUXT_KEYCLOAK_REALM }} + NUXT_KEYCLOAK_CLIENT_ID: ${{ vars.DEV_NUXT_KEYCLOAK_CLIENT_ID }} + NUXT_KEYCLOAK_CLIENT_SECRET: ${{ secrets.NUXT_KEYCLOAK_CLIENT_SECRET }} + NUXT_CORE_API_URL: ${{ vars.DEV_NUXT_CORE_API_URL }} + NUXT_REDIS_URL: ${{ vars.DEV_NUXT_REDIS_URL }} + NUXT_COOKIE_SECRET: ${{ secrets.NUXT_COOKIE_SECRET }} + IMAGE_VERSION: ${{ steps.version.outputs.version }} + run: | + docker service update \ + --image git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:$IMAGE_VERSION \ + --env-add NUXT_KEYCLOAK_URL="$NUXT_KEYCLOAK_URL" \ + --env-add NUXT_KEYCLOAK_REALM="$NUXT_KEYCLOAK_REALM" \ + --env-add NUXT_KEYCLOAK_CLIENT_ID="$NUXT_KEYCLOAK_CLIENT_ID" \ + --env-add NUXT_KEYCLOAK_CLIENT_SECRET="$NUXT_KEYCLOAK_CLIENT_SECRET" \ + --env-add NUXT_CORE_API_URL="$NUXT_CORE_API_URL" \ + --env-add NUXT_REDIS_URL="$NUXT_REDIS_URL" \ + --env-add NUXT_COOKIE_SECRET="$NUXT_COOKIE_SECRET" \ + --with-registry-auth \ + app_portal-modumfiscal-web + + - name: Cleanup old images + run: | + IMAGES_TO_DELETE=$(docker images "git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web" --format "{{.ID}}" | tail -n +3) + if [ -n "$IMAGES_TO_DELETE" ]; then + echo "$IMAGES_TO_DELETE" | xargs -I {} docker rmi -f {} || true + fi + docker image prune -f diff --git a/.gitea/workflows/prod-build-deploy.yml b/.gitea/workflows/prod-build-deploy.yml new file mode 100644 index 0000000..9dad0eb --- /dev/null +++ b/.gitea/workflows/prod-build-deploy.yml @@ -0,0 +1,102 @@ +name: Prod Build & Deploy Portal + +on: + push: + branches: + - main + +# Variáveis necessárias no Gitea (Settings → Variables): +# PROD_NUXT_KEYCLOAK_URL ex: https://keycloakprod.modumfiscal.com.br +# PROD_NUXT_KEYCLOAK_REALM ex: modumfiscal-portal-prod +# PROD_NUXT_KEYCLOAK_CLIENT_ID ex: portal-modumfiscal-bff +# PROD_NUXT_CORE_API_URL ex: https://sistema.modumfiscal.com.br +# PROD_NUXT_REDIS_URL ex: redis://portal-redis:6379 +# +# Secrets necessários (Settings → Secrets): +# REGISTRY_USER +# REGISTRY_PASSWORD +# PROD_NUXT_KEYCLOAK_CLIENT_SECRET +# PROD_NUXT_COOKIE_SECRET + +jobs: + build: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Define version + id: version + run: | + VERSION=$(date +'%Y.%m.%d.%H%M') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Login registry + env: + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + run: | + echo "$REGISTRY_PASSWORD" | docker login git.modumsolucao.com.br \ + -u "$REGISTRY_USER" \ + --password-stdin + + - name: Build Docker image + run: | + docker build \ + --memory=3g \ + -t git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:prod-latest \ + -t git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:prod-${{ steps.version.outputs.version }} \ + . + + - name: Push image + run: | + docker push git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:prod-latest + docker push git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:prod-${{ steps.version.outputs.version }} + + - name: Cleanup old images + run: | + IMAGES_TO_DELETE=$(docker images "git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web" --format "{{.ID}}" | tail -n +3) + if [ -n "$IMAGES_TO_DELETE" ]; then + echo "$IMAGES_TO_DELETE" | xargs -I {} docker rmi -f {} || true + fi + docker image prune -f + + deploy: + runs-on: prod + needs: build + + steps: + - name: Login registry + env: + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + run: | + echo "$REGISTRY_PASSWORD" | docker login git.modumsolucao.com.br \ + -u "$REGISTRY_USER" \ + --password-stdin + + - name: Deploy PROD + env: + NUXT_KEYCLOAK_URL: ${{ vars.PROD_NUXT_KEYCLOAK_URL }} + NUXT_KEYCLOAK_REALM: ${{ vars.PROD_NUXT_KEYCLOAK_REALM }} + NUXT_KEYCLOAK_CLIENT_ID: ${{ vars.PROD_NUXT_KEYCLOAK_CLIENT_ID }} + NUXT_KEYCLOAK_CLIENT_SECRET: ${{ secrets.PROD_NUXT_KEYCLOAK_CLIENT_SECRET }} + NUXT_CORE_API_URL: ${{ vars.PROD_NUXT_CORE_API_URL }} + NUXT_REDIS_URL: ${{ vars.PROD_NUXT_REDIS_URL }} + NUXT_COOKIE_SECRET: ${{ secrets.PROD_NUXT_COOKIE_SECRET }} + IMAGE_VERSION: ${{ needs.build.outputs.version }} + run: | + docker service update \ + --image git.modumsolucao.com.br/modumsolucao/portal-modumfiscal-web:prod-$IMAGE_VERSION \ + --env-add NUXT_KEYCLOAK_URL="$NUXT_KEYCLOAK_URL" \ + --env-add NUXT_KEYCLOAK_REALM="$NUXT_KEYCLOAK_REALM" \ + --env-add NUXT_KEYCLOAK_CLIENT_ID="$NUXT_KEYCLOAK_CLIENT_ID" \ + --env-add NUXT_KEYCLOAK_CLIENT_SECRET="$NUXT_KEYCLOAK_CLIENT_SECRET" \ + --env-add NUXT_CORE_API_URL="$NUXT_CORE_API_URL" \ + --env-add NUXT_REDIS_URL="$NUXT_REDIS_URL" \ + --env-add NUXT_COOKIE_SECRET="$NUXT_COOKIE_SECRET" \ + --with-registry-auth \ + app_portal-modumfiscal-web diff --git a/.gitignore b/.gitignore index a547bf3..8db583d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,30 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +# Dependencies node_modules + +# Build artifacts (Vite legado — pode ser removido após migração) dist dist-ssr + +# Nuxt +.nuxt +.output +.nitro +.cache + +# Local env files *.local +.env +.env.*.local # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store +.claude *.suo *.ntvs* *.njsproj diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0524e9c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Build stage +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Production stage — apenas o .output do Nuxt (SSR via Node.js) +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NUXT_HOST=0.0.0.0 +ENV NUXT_PORT=3000 + +COPY --from=builder /app/.output ./ + +EXPOSE 3000 +CMD ["node", "server/index.mjs"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4e2fb0a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + redis: + image: redis:7-alpine + container_name: portal-redis + ports: + - "6379:6379" + restart: unless-stopped + volumes: + - portal_redis_data:/data + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + portal_redis_data: diff --git a/index.html b/index.html deleted file mode 100644 index b2ed64e..0000000 --- a/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - -
- - - -{{ titulo }}
+{{ descricao }}
+