Files
Momento/.gitea/workflows/ci.yaml
Antigravity 79fd6553b7
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 5m21s
CI / Deploy production (on server) (push) Successful in 3m43s
feat(monitoring): business metrics + hardening sécurité
Métriques business dans /api/metrics :
- Abonnements par tier/status (BASIC/PRO/ENTERPRISE × ACTIVE/CANCELED)
- Nouveaux abonnements ce mois vs mois dernier
- Désabonnements / churn ce mois vs mois dernier
- Utilisateurs actifs 7j / 30j (proxy : note modifiée)
- Nouvelles inscriptions 7j / ce mois
- Runs agents IA par status (30j + aujourd'hui) + tokens consommés
- Usage IA par feature (requêtes + tokens ce mois)
- Logins aujourd'hui / ce mois (via AuditLog)
- Sessions brainstorm ce mois
- Flashcards total + reviews ce mois

Alertes Prometheus :
- HighChurnRate (> 10 désabonnements ce mois)
- NoNewUsersLast7Days (aucune inscription 7j)
- AgentRunsHighErrorRate (> 20% erreurs agents)
- BusinessMetricsCollectionFailed

Hardening monitoring :
- Ports monitoring → 127.0.0.1 (plus exposés publiquement)
- Images pinned (prometheus v2.53.0, grafana 11.1.0, etc.)
- alertmanager-bridge fake → metalmatze/alertmanager-bot:0.4.3
- /api/metrics sécurisé avec METRICS_TOKEN bearer
- Prometheus auth bearer via credentials_file
- Redis AOF + 256mb, healthcheck → /api/build-info
- repeat_interval 4h, inhibit_rules alertmanager
- Secrets CI/CD : AUTH_GOOGLE_SECRET, METRICS_TOKEN, GRAFANA, MCP_API_KEY

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 14:49:34 +00:00

243 lines
10 KiB
YAML

name: CI
on:
push:
branches:
- main
- "*"
pull_request:
branches:
- main
workflow_dispatch:
jobs:
ci:
name: Lint, Unit Tests & Build
runs-on: ubuntu-24.04
defaults:
run:
working-directory: memento-note
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node 22
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Cache node_modules
uses: actions/cache@v3
with:
path: memento-note/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('memento-note/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Generate Prisma client
run: npx prisma generate
- name: Lint
run: npm run lint
- name: Notify lint failure
if: failure()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
MSG=$(printf "❌ Momento CI Failed\nStep: Lint\nCommit: %s\nBranch: %s" "${{ github.sha }}" "${{ github.ref_name }}")
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${MSG}" \
-d "parse_mode=Markdown" || true
fi
- name: Unit tests (Vitest - fast logic tests)
run: npm run test:unit
- name: Notify test failure
if: failure()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
MSG=$(printf "❌ Momento CI Failed\nStep: Unit Tests\nCommit: %s\nBranch: %s" "${{ github.sha }}" "${{ github.ref_name }}")
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${MSG}" \
-d "parse_mode=Markdown" || true
fi
- name: Build
run: npm run build
- name: Notify build failure
if: failure()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
MSG=$(printf "❌ Momento CI Failed\nStep: Build\nCommit: %s\nBranch: %s" "${{ github.sha }}" "${{ github.ref_name }}")
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${MSG}" \
-d "parse_mode=Markdown" || true
fi
- name: Pack web artifact for deploy
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
working-directory: memento-note
run: |
tar czf ../web-artifact.tgz \
.next/standalone .next/static public prisma \
node_modules/.prisma node_modules/@prisma node_modules/prisma \
docker-entrypoint.sh socket-server.ts tsconfig.json
ls -lh ../web-artifact.tgz
- name: Upload web artifact
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: web-artifact
path: web-artifact.tgz
retention-days: 2
deploy:
name: Deploy production (on server)
needs: ci
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: docker-host
steps:
- name: Sync deploy scripts on server
run: |
git config --global --add safe.directory /opt/memento || true
cd /opt/memento
git fetch origin main
git reset --hard origin/main
- name: Download web artifact
uses: actions/download-artifact@v4
continue-on-error: true
with:
name: web-artifact
- name: Update .env.docker
env:
APP_URL: ${{ vars.APP_URL }}
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
ADMIN_EMAIL: ${{ vars.ADMIN_EMAIL }}
ALLOW_REGISTRATION: ${{ vars.ALLOW_REGISTRATION }}
POSTGRES_USER: ${{ vars.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ vars.POSTGRES_DB }}
POSTGRES_PORT: ${{ vars.POSTGRES_PORT }}
AI_PROVIDER_TAGS: ${{ vars.AI_PROVIDER_TAGS }}
AI_MODEL_TAGS: ${{ vars.AI_MODEL_TAGS }}
AI_PROVIDER_EMBEDDING: ${{ vars.AI_PROVIDER_EMBEDDING }}
AI_MODEL_EMBEDDING: ${{ vars.AI_MODEL_EMBEDDING }}
AI_PROVIDER_CHAT: ${{ vars.AI_PROVIDER_CHAT }}
AI_MODEL_CHAT: ${{ vars.AI_MODEL_CHAT }}
CUSTOM_OPENAI_BASE_URL: ${{ vars.CUSTOM_OPENAI_BASE_URL }}
CUSTOM_OPENAI_API_KEY: ${{ secrets.CUSTOM_OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OLLAMA_BASE_URL: ${{ vars.OLLAMA_BASE_URL }}
EMAIL_PROVIDER: ${{ vars.EMAIL_PROVIDER }}
SMTP_FROM: ${{ vars.SMTP_FROM }}
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
SMTP_HOST: ${{ vars.SMTP_HOST }}
SMTP_PORT: ${{ vars.SMTP_PORT }}
SMTP_USER: ${{ vars.SMTP_USER }}
SMTP_PASS: ${{ secrets.SMTP_PASS }}
SMTP_SECURE: ${{ vars.SMTP_SECURE }}
SMTP_IGNORE_CERT: ${{ vars.SMTP_IGNORE_CERT }}
MCP_MODE: ${{ vars.MCP_MODE }}
MCP_PORT: ${{ vars.MCP_PORT }}
WEB_SEARCH_PROVIDER: ${{ vars.WEB_SEARCH_PROVIDER }}
SEARXNG_URL: ${{ vars.SEARXNG_URL }}
BRAVE_SEARCH_API_KEY: ${{ secrets.BRAVE_SEARCH_API_KEY }}
JINA_API_KEY: ${{ secrets.JINA_API_KEY }}
AUTH_GOOGLE_ID: ${{ vars.AUTH_GOOGLE_ID }}
AUTH_GOOGLE_SECRET: ${{ secrets.AUTH_GOOGLE_SECRET }}
SOCKET_INTERNAL_KEY: ${{ secrets.SOCKET_INTERNAL_KEY }}
SOCKET_PORT: ${{ vars.SOCKET_PORT }}
SOCKET_HTTP_PORT: ${{ vars.SOCKET_HTTP_PORT }}
SOCKET_INTERNAL_URL: ${{ vars.SOCKET_INTERNAL_URL }}
NEXT_PUBLIC_SOCKET_URL: ${{ vars.NEXT_PUBLIC_SOCKET_URL }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }}
GRAFANA_ADMIN_PASSWORD: ${{ secrets.GRAFANA_ADMIN_PASSWORD }}
MCP_API_KEY: ${{ secrets.MCP_API_KEY }}
run: |
ENV_FILE="/opt/memento/.env.docker"
touch "$ENV_FILE"
upsert() {
local key="$1" val="$2"
[ -z "$val" ] && return
sed -i "/^[[:space:]]*${key}=/d" "$ENV_FILE"
echo "${key}=\"${val}\"" >> "$ENV_FILE"
}
upsert NEXTAUTH_URL "$APP_URL"
upsert NEXTAUTH_SECRET "$NEXTAUTH_SECRET"
upsert ADMIN_EMAIL "$ADMIN_EMAIL"
upsert ALLOW_REGISTRATION "$ALLOW_REGISTRATION"
upsert POSTGRES_USER "$POSTGRES_USER"
upsert POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
upsert POSTGRES_DB "$POSTGRES_DB"
upsert POSTGRES_PORT "$POSTGRES_PORT"
upsert AI_PROVIDER_TAGS "$AI_PROVIDER_TAGS"
upsert AI_MODEL_TAGS "$AI_MODEL_TAGS"
upsert AI_PROVIDER_EMBEDDING "$AI_PROVIDER_EMBEDDING"
upsert AI_MODEL_EMBEDDING "$AI_MODEL_EMBEDDING"
upsert AI_PROVIDER_CHAT "$AI_PROVIDER_CHAT"
upsert AI_MODEL_CHAT "$AI_MODEL_CHAT"
upsert CUSTOM_OPENAI_BASE_URL "$CUSTOM_OPENAI_BASE_URL"
upsert CUSTOM_OPENAI_API_KEY "$CUSTOM_OPENAI_API_KEY"
upsert OPENAI_API_KEY "$OPENAI_API_KEY"
upsert OLLAMA_BASE_URL "$OLLAMA_BASE_URL"
upsert REDIS_HOST "redis"
upsert EMAIL_PROVIDER "$EMAIL_PROVIDER"
upsert SMTP_FROM "$SMTP_FROM"
upsert RESEND_API_KEY "$RESEND_API_KEY"
upsert SMTP_HOST "$SMTP_HOST"
upsert SMTP_PORT "$SMTP_PORT"
upsert SMTP_USER "$SMTP_USER"
upsert SMTP_PASS "$SMTP_PASS"
upsert SMTP_SECURE "$SMTP_SECURE"
upsert SMTP_IGNORE_CERT "$SMTP_IGNORE_CERT"
upsert MCP_MODE "$MCP_MODE"
upsert MCP_PORT "$MCP_PORT"
upsert WEB_SEARCH_PROVIDER "$WEB_SEARCH_PROVIDER"
upsert SEARXNG_URL "$SEARXNG_URL"
upsert BRAVE_SEARCH_API_KEY "$BRAVE_SEARCH_API_KEY"
upsert JINA_API_KEY "$JINA_API_KEY"
upsert AUTH_GOOGLE_ID "$AUTH_GOOGLE_ID"
upsert AUTH_GOOGLE_SECRET "$AUTH_GOOGLE_SECRET"
upsert SOCKET_INTERNAL_KEY "$SOCKET_INTERNAL_KEY"
upsert SOCKET_PORT "$SOCKET_PORT"
upsert SOCKET_HTTP_PORT "$SOCKET_HTTP_PORT"
upsert SOCKET_INTERNAL_URL "$SOCKET_INTERNAL_URL"
upsert NEXT_PUBLIC_SOCKET_URL "$NEXT_PUBLIC_SOCKET_URL"
upsert TELEGRAM_BOT_TOKEN "$TELEGRAM_BOT_TOKEN"
upsert TELEGRAM_CHAT_ID "$TELEGRAM_CHAT_ID"
upsert METRICS_TOKEN "$METRICS_TOKEN"
upsert GRAFANA_ADMIN_PASSWORD "$GRAFANA_ADMIN_PASSWORD"
upsert MCP_API_KEY "$MCP_API_KEY"
# Write metrics token file for Prometheus (same secret)
[ -n "$METRICS_TOKEN" ] && echo "$METRICS_TOKEN" > /opt/memento/monitoring/metrics-token && chmod 600 /opt/memento/monitoring/metrics-token || true
- name: Deploy on 192.168.1.190
env:
ARTIFACT_TGZ: ${{ github.workspace }}/web-artifact.tgz
EXPECTED_COMMIT: ${{ github.sha }}
run: bash /opt/memento/scripts/deploy-prod.sh