Compare commits
1 commit
main
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c24b67001c |
3927 changed files with 157184 additions and 277339 deletions
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"extends": ["@commitlint/config-conventional"],
|
||||
"rules": {
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"feat",
|
||||
"fix",
|
||||
"docs",
|
||||
"style",
|
||||
"refactor",
|
||||
"perf",
|
||||
"test",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"revert",
|
||||
"security"
|
||||
]
|
||||
],
|
||||
"subject-case": [0],
|
||||
"header-max-length": [2, "always", 120],
|
||||
"body-max-line-length": [1, "always", 200],
|
||||
"footer-max-line-length": [0]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# Règles de Développement UI - Projet SaaS
|
||||
|
||||
## 0. Scope v0.901 (priorité absolue)
|
||||
## 0. Scope v0.302 (priorité absolue)
|
||||
|
||||
- **Référence** : `docs/V0_901_RELEASE_SCOPE.md` et `docs/SCOPE_CONTROL.md`
|
||||
- Avant toute modification : vérifier si le changement est **dans le scope v0.901**
|
||||
- **v0.803 livré** : Voir `docs/archive/V0_803_RELEASE_SCOPE.md`
|
||||
- **Référence** : `docs/V0_302_RELEASE_SCOPE.md` et `docs/SCOPE_CONTROL.md`
|
||||
- Avant toute modification : vérifier si le changement est **dans le scope v0.302**
|
||||
- **Autorisé v0.302** : lots S2, N1, C2, P2 (groupes avancés, push, appels, rich presence)
|
||||
- **Interdit** : nouvelles routes/pages hors scope, nouvelles dépendances (sauf correctif sécurité)
|
||||
- En cas de doute : ne pas ajouter. Créer une issue pour une version ultérieure.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
# cleanup-failed.yml — workflow_dispatch only.
|
||||
#
|
||||
# Tears down the kept-alive failed-deploy color (the inactive one
|
||||
# that survived a Phase D / Phase F failure for forensics).
|
||||
# Operator triggers this once they have read the journalctl output.
|
||||
#
|
||||
# Hard safety in playbooks/cleanup_failed.yml: refuses to destroy
|
||||
# the currently-active color.
|
||||
name: Veza cleanup failed-deploy color
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to clean up"
|
||||
required: true
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
color:
|
||||
description: "Color to destroy (must NOT be the active one)"
|
||||
required: true
|
||||
type: choice
|
||||
options: [blue, green]
|
||||
|
||||
concurrency:
|
||||
group: cleanup-${{ inputs.env }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Destroy ${{ inputs.color }} app containers in ${{ inputs.env }}
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install ansible
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible
|
||||
ansible-galaxy collection install community.general
|
||||
|
||||
- name: Write vault password
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run cleanup_failed.yml
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-cleanup-${{ inputs.env }}-${{ inputs.color }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ inputs.env }}.yml \
|
||||
playbooks/cleanup_failed.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ inputs.env }} \
|
||||
-e target_color=${{ inputs.color }}
|
||||
|
||||
- name: Upload Ansible log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-cleanup-${{ inputs.env }}-${{ inputs.color }}
|
||||
path: ${{ runner.temp }}/ansible-cleanup-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
|
|
@ -1,360 +0,0 @@
|
|||
# Veza deploy pipeline.
|
||||
#
|
||||
# Triggers (intentionally narrow — see SECURITY note below):
|
||||
# workflow_dispatch → operator-supplied env + sha
|
||||
# (push:main + tag:v* are commented OUT until provisioning is
|
||||
# complete — see docs/RUNBOOK_DEPLOY_BOOTSTRAP.md. Re-enable
|
||||
# once secrets/runner/vault are in place and a manual run via
|
||||
# workflow_dispatch has been verified GREEN.)
|
||||
#
|
||||
# SECURITY: this workflow runs on a self-hosted runner with access to
|
||||
# the Incus unix socket (effectively root on the host). DO NOT add
|
||||
# `pull_request` or any fork-influenced trigger here — an attacker-
|
||||
# controlled fork would be able to `incus exec` arbitrarily. The
|
||||
# narrow trigger list above is the security boundary.
|
||||
#
|
||||
# Sequence : build (3 jobs in parallel) → upload artifacts → deploy.
|
||||
name: Veza deploy
|
||||
|
||||
on:
|
||||
# push: # GATED — uncomment after first
|
||||
# branches: [main] # successful workflow_dispatch run
|
||||
# tags: ['v*'] # see RUNBOOK_DEPLOY_BOOTSTRAP.md
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to deploy"
|
||||
required: true
|
||||
default: staging
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
release_sha:
|
||||
description: "Full git SHA to deploy (defaults to current HEAD if empty)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
# Only one deploy per env at a time. Newer pushes cancel older
|
||||
# in-flight builds for the same env (the user almost always wants
|
||||
# the newer commit).
|
||||
group: deploy-${{ github.ref_type == 'tag' && 'prod' || 'staging' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# Where build artefacts land. Set in Forgejo repo Variables :
|
||||
# FORGEJO_REGISTRY_URL = https://forgejo.veza.fr/api/packages/talas/generic
|
||||
REGISTRY_URL: ${{ vars.FORGEJO_REGISTRY_URL }}
|
||||
|
||||
jobs:
|
||||
# =================================================================
|
||||
# Resolve env + sha from the trigger.
|
||||
# =================================================================
|
||||
resolve:
|
||||
name: Resolve env + SHA
|
||||
runs-on: [self-hosted, incus]
|
||||
outputs:
|
||||
env: ${{ steps.r.outputs.env }}
|
||||
sha: ${{ steps.r.outputs.sha }}
|
||||
steps:
|
||||
- name: Resolve
|
||||
id: r
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
ENV="${{ inputs.env }}"
|
||||
SHA="${{ inputs.release_sha || github.sha }}"
|
||||
elif [ "${{ github.ref_type }}" = "tag" ]; then
|
||||
ENV="prod"
|
||||
SHA="${{ github.sha }}"
|
||||
else
|
||||
ENV="staging"
|
||||
SHA="${{ github.sha }}"
|
||||
fi
|
||||
if ! echo "$SHA" | grep -Eq '^[0-9a-f]{40}$'; then
|
||||
echo "SHA '$SHA' is not a 40-char git SHA"
|
||||
exit 1
|
||||
fi
|
||||
echo "env=$ENV" >> "$GITHUB_OUTPUT"
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved env=$ENV sha=$SHA"
|
||||
|
||||
# =================================================================
|
||||
# Build backend (Go).
|
||||
# =================================================================
|
||||
build-backend:
|
||||
name: Build backend
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache: true
|
||||
cache-dependency-path: veza-backend-api/go.sum
|
||||
|
||||
- name: Test
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
VEZA_SKIP_INTEGRATION: "1"
|
||||
run: go test ./... -short -count=1 -timeout 300s
|
||||
|
||||
- name: Build veza-api (CGO=0, static)
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
run: |
|
||||
go build -trimpath -ldflags "-s -w" \
|
||||
-o ./bin/veza-api ./cmd/api/main.go
|
||||
go build -trimpath -ldflags "-s -w" \
|
||||
-o ./bin/migrate_tool ./cmd/migrate_tool/main.go
|
||||
|
||||
- name: Stage tarball contents
|
||||
working-directory: veza-backend-api
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-backend"
|
||||
mkdir -p "$STAGE/migrations"
|
||||
cp ./bin/veza-api ./bin/migrate_tool "$STAGE/"
|
||||
cp -r ./migrations/* "$STAGE/migrations/" || true
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-backend-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-backend" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-backend-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-backend/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Build stream (Rust).
|
||||
# =================================================================
|
||||
build-stream:
|
||||
name: Build stream
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Set up Rust toolchain
|
||||
run: |
|
||||
command -v rustup >/dev/null || \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
sudo apt-get update -qq && sudo apt-get install -y musl-tools
|
||||
|
||||
- name: Cache cargo + target
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
veza-stream-server/target
|
||||
key: deploy-${{ runner.os }}-cargo-${{ hashFiles('veza-stream-server/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
deploy-${{ runner.os }}-cargo-
|
||||
|
||||
- name: Test
|
||||
working-directory: veza-stream-server
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Build stream_server (musl static)
|
||||
working-directory: veza-stream-server
|
||||
run: |
|
||||
cargo build --release --locked \
|
||||
--target x86_64-unknown-linux-musl
|
||||
|
||||
- name: Stage tarball contents
|
||||
working-directory: veza-stream-server
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-stream"
|
||||
mkdir -p "$STAGE"
|
||||
cp ./target/x86_64-unknown-linux-musl/release/stream_server "$STAGE/"
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-stream-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-stream" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-stream-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-stream/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Build web (React/Vite).
|
||||
# =================================================================
|
||||
build-web:
|
||||
name: Build web
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build design tokens
|
||||
run: npm run build:tokens --workspace=@veza/design-system
|
||||
|
||||
- name: Build SPA
|
||||
working-directory: apps/web
|
||||
env:
|
||||
VITE_API_URL: /api/v1
|
||||
VITE_DOMAIN: ${{ needs.resolve.outputs.env == 'prod' && 'veza.fr' || 'staging.veza.fr' }}
|
||||
VITE_RELEASE_SHA: ${{ needs.resolve.outputs.sha }}
|
||||
run: npm run build
|
||||
|
||||
- name: Stage tarball contents
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-web"
|
||||
mkdir -p "$STAGE"
|
||||
cp -r apps/web/dist/* "$STAGE/"
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-web-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-web" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-web-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-web/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Deploy via Ansible. Runs on the self-hosted runner that has
|
||||
# Incus socket access (label `incus`). Requires Forgejo secrets:
|
||||
# ANSIBLE_VAULT_PASSWORD — unlocks group_vars/all/vault.yml
|
||||
# FORGEJO_REGISTRY_TOKEN — same token the build jobs use,
|
||||
# passed to ansible-playbook so
|
||||
# the data containers can fetch
|
||||
# the tarballs they were just sent.
|
||||
# =================================================================
|
||||
deploy:
|
||||
name: Deploy via Ansible
|
||||
needs: [resolve, build-backend, build-stream, build-web]
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Install ansible + community.general + community.postgresql + community.rabbitmq
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible python3-psycopg2 python3-pip
|
||||
ansible-galaxy collection install \
|
||||
community.general \
|
||||
community.postgresql \
|
||||
community.rabbitmq
|
||||
|
||||
- name: Write vault password to a tmpfile
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run deploy_data.yml (idempotent provisioning + ZFS snapshot)
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-data-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ needs.resolve.outputs.env }}.yml \
|
||||
playbooks/deploy_data.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ needs.resolve.outputs.env }} \
|
||||
-e veza_release_sha=${{ needs.resolve.outputs.sha }} \
|
||||
-e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Run deploy_app.yml (blue/green)
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-app-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ needs.resolve.outputs.env }}.yml \
|
||||
playbooks/deploy_app.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ needs.resolve.outputs.env }} \
|
||||
-e veza_release_sha=${{ needs.resolve.outputs.sha }} \
|
||||
-e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Upload Ansible logs (for forensics)
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-logs-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}
|
||||
path: ${{ runner.temp }}/ansible-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
# rollback.yml — workflow_dispatch only.
|
||||
#
|
||||
# Two modes :
|
||||
# fast — flip HAProxy back to the previous color. ~5s. Requires
|
||||
# the target color's containers to still be alive
|
||||
# (i.e., no later deploy has recycled them).
|
||||
# full — re-run deploy_app.yml with a specific (older) release_sha.
|
||||
# ~5-10min. The artefact must still be in the Forgejo
|
||||
# registry (default retention 30 SHA per component).
|
||||
#
|
||||
# See docs/RUNBOOK_ROLLBACK.md for decision criteria.
|
||||
name: Veza rollback
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to rollback"
|
||||
required: true
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
mode:
|
||||
description: "Rollback mode"
|
||||
required: true
|
||||
type: choice
|
||||
options: [fast, full]
|
||||
target_color:
|
||||
description: "(mode=fast only) color to flip back TO (the prior active one)"
|
||||
required: false
|
||||
type: choice
|
||||
options: [blue, green]
|
||||
release_sha:
|
||||
description: "(mode=full only) 40-char SHA of the release to redeploy"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: rollback-${{ inputs.env }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
rollback:
|
||||
name: Rollback ${{ inputs.env }} (${{ inputs.mode }})
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
run: |
|
||||
if [ "${{ inputs.mode }}" = "fast" ] && [ -z "${{ inputs.target_color }}" ]; then
|
||||
echo "mode=fast requires target_color"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ inputs.mode }}" = "full" ]; then
|
||||
if [ -z "${{ inputs.release_sha }}" ]; then
|
||||
echo "mode=full requires release_sha"
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "${{ inputs.release_sha }}" | grep -Eq '^[0-9a-f]{40}$'; then
|
||||
echo "release_sha is not a 40-char git SHA"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ inputs.mode == 'full' && inputs.release_sha || github.ref }}
|
||||
|
||||
- name: Install ansible + collections
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible python3-psycopg2
|
||||
ansible-galaxy collection install \
|
||||
community.general \
|
||||
community.postgresql \
|
||||
community.rabbitmq
|
||||
|
||||
- name: Write vault password
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run rollback.yml
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-rollback-${{ inputs.env }}-${{ inputs.mode }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
EXTRA="-e veza_env=${{ inputs.env }} -e mode=${{ inputs.mode }}"
|
||||
if [ "${{ inputs.mode }}" = "fast" ]; then
|
||||
EXTRA="$EXTRA -e target_color=${{ inputs.target_color }}"
|
||||
else
|
||||
EXTRA="$EXTRA -e veza_release_sha=${{ inputs.release_sha }}"
|
||||
EXTRA="$EXTRA -e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}"
|
||||
fi
|
||||
ansible-playbook \
|
||||
-i inventory/${{ inputs.env }}.yml \
|
||||
playbooks/rollback.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
$EXTRA
|
||||
|
||||
- name: Upload Ansible log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-rollback-${{ inputs.env }}-${{ inputs.mode }}
|
||||
path: ${{ runner.temp }}/ansible-rollback-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -27,7 +27,7 @@ Ce qui aurait dû se passer.
|
|||
|
||||
## 💻 Contexte
|
||||
|
||||
- Service impacté : (backend-api / stream-server / web-frontend / infra)
|
||||
- Service impacté : (backend-api / chat-server / stream-server / web-frontend / infra)
|
||||
- Branch : (main / develop / autre)
|
||||
- Environnement : (local / dev / staging / prod)
|
||||
|
||||
|
|
|
|||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
|
@ -6,6 +6,12 @@ updates:
|
|||
interval: "weekly"
|
||||
labels: ["dependencies", "go"]
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/veza-chat-server"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels: ["dependencies", "rust"]
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/veza-stream-server"
|
||||
schedule:
|
||||
|
|
|
|||
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
|
|
@ -1,7 +1,7 @@
|
|||
# 🧩 Résumé
|
||||
|
||||
- **Type de changement** : (feat / fix / refactor / chore / docs)
|
||||
- **Scope** : (backend-api / stream-server / web-frontend / infra / docs)
|
||||
- **Scope** : (backend-api / chat-server / stream-server / web-frontend / infra / docs)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -48,6 +48,7 @@ Si oui, préciser :
|
|||
Coche ce qui a été lancé :
|
||||
|
||||
- [ ] `go test ./...` (backend-api)
|
||||
- [ ] `cargo test` (chat-server)
|
||||
- [ ] `cargo test` (stream-server)
|
||||
- [ ] `pnpm test` (web-frontend)
|
||||
- [ ] Tests manuels locaux (décrire rapidement)
|
||||
|
|
|
|||
102
.github/workflows/backend-ci.yml
vendored
Normal file
102
.github/workflows/backend-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
name: Backend API CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "veza-backend-api/**"
|
||||
- ".github/workflows/backend-ci.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "veza-backend-api/**"
|
||||
- ".github/workflows/backend-ci.yml"
|
||||
|
||||
jobs:
|
||||
test-unit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: veza-backend-api
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
cache: true
|
||||
|
||||
- name: Download deps
|
||||
run: go mod download
|
||||
|
||||
- name: Run govulncheck
|
||||
run: |
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
govulncheck ./...
|
||||
|
||||
- name: Run unit tests
|
||||
run: go test ./internal/handlers/... ./internal/services/... -short -coverprofile=coverage.out -covermode=atomic -timeout 5m
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: go-coverage
|
||||
path: veza-backend-api/coverage.out
|
||||
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: veza_test
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 6379:6379
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/veza_test?sslmode=disable
|
||||
REDIS_URL: redis://localhost:6379
|
||||
JWT_SECRET: test-jwt-secret-for-ci
|
||||
APP_ENV: test
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: veza-backend-api
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
cache: true
|
||||
|
||||
- name: Download deps
|
||||
run: go mod download
|
||||
|
||||
- name: Run migrations
|
||||
run: go run cmd/migrate_tool/main.go
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run integration tests
|
||||
run: go test -tags=integration ./internal/... -timeout 15m
|
||||
|
||||
170
.github/workflows/cd.yml
vendored
Normal file
170
.github/workflows/cd.yml
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
name: Veza CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
description: 'Deployment environment'
|
||||
required: true
|
||||
default: 'staging'
|
||||
type: choice
|
||||
options:
|
||||
- staging
|
||||
- production
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy to ${{ github.event.inputs.environment || 'staging' }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
|
||||
environment: ${{ github.event.inputs.environment || 'staging' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Push to registry: set repo secrets DOCKER_REGISTRY, DOCKER_REGISTRY_USERNAME, DOCKER_REGISTRY_PASSWORD
|
||||
# Example: DOCKER_REGISTRY=ghcr.io/org/repo or registry.example.com/veza
|
||||
- name: Build Backend Docker Image
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
docker build -t veza-backend-api:${{ github.sha }} .
|
||||
|
||||
- name: Build Frontend Docker Image
|
||||
run: |
|
||||
cd apps/web
|
||||
docker build -t veza-frontend:${{ github.sha }} .
|
||||
|
||||
- name: Build Rust Services Docker Images
|
||||
run: |
|
||||
cd veza-chat-server
|
||||
docker build -t veza-chat-server:${{ github.sha }} .
|
||||
|
||||
cd ../veza-stream-server
|
||||
docker build -t veza-stream-server:${{ github.sha }} .
|
||||
|
||||
- name: Trivy vulnerability scan
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
with:
|
||||
image-ref: 'veza-backend-api:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Trivy scan frontend
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
with:
|
||||
image-ref: 'veza-frontend:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Trivy scan chat server
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
with:
|
||||
image-ref: 'veza-chat-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Trivy scan stream server
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
with:
|
||||
image-ref: 'veza-stream-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
mkdir -p sbom
|
||||
for svc in veza-backend-api veza-frontend veza-chat-server veza-stream-server; do
|
||||
trivy image --format cyclonedx --output "sbom/${svc}-${{ github.sha }}.json" "${svc}:${{ github.sha }}"
|
||||
done
|
||||
- name: Upload SBOM artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sbom
|
||||
path: sbom/
|
||||
|
||||
- name: Push Images to Registry
|
||||
if: ${{ secrets.DOCKER_REGISTRY != '' }}
|
||||
run: |
|
||||
echo "${{ secrets.DOCKER_REGISTRY_PASSWORD }}" | docker login "${{ secrets.DOCKER_REGISTRY }}" -u "${{ secrets.DOCKER_REGISTRY_USERNAME }}" --password-stdin
|
||||
for svc in veza-backend-api veza-frontend veza-chat-server veza-stream-server; do
|
||||
docker tag "${svc}:${{ github.sha }}" "${{ secrets.DOCKER_REGISTRY }}/${svc}:${{ github.sha }}"
|
||||
docker tag "${svc}:${{ github.sha }}" "${{ secrets.DOCKER_REGISTRY }}/${svc}:latest"
|
||||
docker push "${{ secrets.DOCKER_REGISTRY }}/${svc}:${{ github.sha }}"
|
||||
docker push "${{ secrets.DOCKER_REGISTRY }}/${svc}:latest"
|
||||
done
|
||||
|
||||
- name: Install cosign
|
||||
if: ${{ secrets.DOCKER_REGISTRY != '' && secrets.COSIGN_PRIVATE_KEY != '' }}
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.2.0'
|
||||
- name: Sign images with cosign
|
||||
if: ${{ secrets.DOCKER_REGISTRY != '' && secrets.COSIGN_PRIVATE_KEY != '' }}
|
||||
env:
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
|
||||
run: |
|
||||
for svc in veza-backend-api veza-frontend veza-chat-server veza-stream-server; do
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${{ secrets.DOCKER_REGISTRY }}/${svc}:${{ github.sha }}"
|
||||
cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${{ secrets.DOCKER_REGISTRY }}/${svc}:latest"
|
||||
done
|
||||
|
||||
- name: Deploy to Kubernetes
|
||||
if: ${{ secrets.KUBE_CONFIG != '' }}
|
||||
run: |
|
||||
KUBECONFIG="${{ runner.temp }}/kubeconfig"
|
||||
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > "$KUBECONFIG"
|
||||
chmod 600 "$KUBECONFIG"
|
||||
export KUBECONFIG
|
||||
for svc in veza-backend-api veza-chat-server veza-stream-server; do
|
||||
kubectl set image "deployment/${svc}" "${svc}=${{ secrets.DOCKER_REGISTRY }}/${svc}:${{ github.sha }}" \
|
||||
-n veza --record || echo "Skipping ${svc} (deployment not found)"
|
||||
done
|
||||
kubectl rollout status deployment/veza-backend-api -n veza --timeout=300s || true
|
||||
rm -f "$KUBECONFIG"
|
||||
|
||||
- name: Deployment Summary
|
||||
run: |
|
||||
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Backend: veza-backend-api:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Frontend: veza-frontend:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Chat Server: veza-chat-server:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Stream Server: veza-stream-server:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Environment: ${{ github.event.inputs.environment || 'staging' }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
smoke-post-deploy:
|
||||
name: Smoke tests post-deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: deploy
|
||||
if: ${{ secrets.STAGING_URL != '' || vars.STAGING_URL != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright
|
||||
run: npx playwright install chromium --with-deps
|
||||
|
||||
- name: Run smoke tests
|
||||
env:
|
||||
PLAYWRIGHT_BASE_URL: ${{ secrets.STAGING_URL || vars.STAGING_URL }}
|
||||
run: |
|
||||
cd apps/web
|
||||
npx playwright test --config=playwright.config.smoke.ts
|
||||
|
||||
41
.github/workflows/chat-ci.yml
vendored
Normal file
41
.github/workflows/chat-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Chat Server CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "veza-chat-server/**"
|
||||
- "veza-common/**"
|
||||
- ".github/workflows/chat-ci.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "veza-chat-server/**"
|
||||
- "veza-common/**"
|
||||
- ".github/workflows/chat-ci.yml"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: veza-chat-server
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
- name: Lint with clippy
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
|
||||
- name: Audit dependencies
|
||||
uses: actions-rust-lang/audit@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --all
|
||||
|
||||
467
.github/workflows/ci.yml
vendored
467
.github/workflows/ci.yml
vendored
|
|
@ -1,249 +1,294 @@
|
|||
name: Veza CI
|
||||
name: Veza CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "remediation/*", "feature/mvp-complete"]
|
||||
pull_request:
|
||||
branches: ["main", "feature/mvp-complete"]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||
push:
|
||||
branches: [ "main", "remediation/*", "feature/mvp-complete" ]
|
||||
pull_request:
|
||||
branches: [ "main", "feature/mvp-complete" ]
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
jobs:
|
||||
# ===========================================================================
|
||||
# Backend (Go) — build, test, lint, security
|
||||
# ===========================================================================
|
||||
backend:
|
||||
name: Backend (Go)
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
backend-go:
|
||||
name: Backend (Go)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache: true
|
||||
# go.mod/go.sum live under veza-backend-api, not repo root.
|
||||
# Without this, setup-go warns "Dependencies file is not
|
||||
# found" and skips the mod cache → adds ~60-90s per run.
|
||||
cache-dependency-path: veza-backend-api/go.sum
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Cache Go tool binaries
|
||||
id: go-tools-cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tools-govulncheck-golangci-lint-v2
|
||||
# Save the cache even when later steps (Lint, Test, etc.)
|
||||
# fail so the next run benefits from the installed tools.
|
||||
save-always: true
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache: true
|
||||
|
||||
- name: Install Go tools
|
||||
# NOTE: golangci-lint v2 lives under the /v2/ module path.
|
||||
# The old /cmd/ path still resolves to v1.64.x, which rejects
|
||||
# v2-format .golangci.yml with "please use golangci-lint v2".
|
||||
# Pinned versions so the cache key stays stable.
|
||||
if: steps.go-tools-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Add ~/go/bin to PATH
|
||||
run: echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Run govulncheck
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
govulncheck ./...
|
||||
|
||||
- name: Build
|
||||
run: go build ./...
|
||||
working-directory: veza-backend-api
|
||||
- name: Vet
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go vet ./...
|
||||
|
||||
- name: Test
|
||||
# -short + VEZA_SKIP_INTEGRATION=1 so testcontainers-go (which
|
||||
# needs a Docker socket) is not invoked on the Forgejo runner.
|
||||
# Integration tests run in a dedicated nightly job with DinD.
|
||||
run: go test ./... -short -count=1 -timeout 300s -coverprofile=coverage.out
|
||||
env:
|
||||
VEZA_SKIP_INTEGRATION: "1"
|
||||
working-directory: veza-backend-api
|
||||
- name: Lint
|
||||
run: npx turbo run lint --filter=veza-backend-api
|
||||
|
||||
- name: Lint
|
||||
run: golangci-lint run ./... --timeout 5m
|
||||
working-directory: veza-backend-api
|
||||
- name: Test
|
||||
run: npx turbo run test --filter=veza-backend-api
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
working-directory: veza-backend-api
|
||||
- name: Build
|
||||
run: npx turbo run build --filter=veza-backend-api
|
||||
|
||||
- name: Vulnerability check
|
||||
run: govulncheck ./...
|
||||
working-directory: veza-backend-api
|
||||
rust-services:
|
||||
name: Rust Services (Chat & Stream)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Coverage summary
|
||||
run: |
|
||||
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}')
|
||||
echo "## Backend Coverage: $COVERAGE" >> $GITHUB_STEP_SUMMARY
|
||||
working-directory: veza-backend-api
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
# ===========================================================================
|
||||
# Frontend (Web) — lint, typecheck, build, unit tests
|
||||
# ===========================================================================
|
||||
frontend:
|
||||
name: Frontend (Web)
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Sprint 2 design-system migrated to Style Dictionary; the
|
||||
# generated tokens live in packages/design-system/dist/ which
|
||||
# is gitignored. apps/web imports `@veza/design-system/tokens-generated`,
|
||||
# so dist/ MUST exist before tsc/vitest/build runs.
|
||||
# `prepare` in the package would normally cover npm ci, but
|
||||
# this explicit step makes the dependency loud and runnable
|
||||
# standalone for local debugging.
|
||||
- name: Build design tokens
|
||||
run: npm run build:tokens --workspace=@veza/design-system
|
||||
- name: Install cargo-audit
|
||||
run: cargo install cargo-audit
|
||||
|
||||
# Prevents drift between veza-backend-api/openapi.yaml and
|
||||
# apps/web/src/types/generated/. Regenerates then fails if
|
||||
# git diff is non-empty.
|
||||
- name: Check OpenAPI types in sync
|
||||
run: bash scripts/check-types-sync.sh
|
||||
working-directory: apps/web
|
||||
- name: Auditing Chat Server
|
||||
run: |
|
||||
cd veza-chat-server
|
||||
cargo audit
|
||||
|
||||
- name: Lint
|
||||
# ESLint warning baseline (v1.0.10 dette tech).
|
||||
# Lowered from 1204 → 1108 after no-unused-vars sprint
|
||||
# (134 → 0). Top contributors at this baseline :
|
||||
# 757 no-restricted-syntax (custom design-system rule —
|
||||
# Tailwind defaults / hex literals / native <button>)
|
||||
# 139 @typescript-eslint/no-non-null-assertion
|
||||
# 115 @typescript-eslint/no-explicit-any
|
||||
# 47 react-hooks/exhaustive-deps
|
||||
# 25 react-refresh/only-export-components
|
||||
# 23 storybook/no-redundant-story-name
|
||||
# CI fails on ANY new warning. Lower this number as
|
||||
# warnings are resorbed ; never raise it.
|
||||
run: npx eslint --max-warnings=1108 .
|
||||
working-directory: apps/web
|
||||
- name: Auditing Stream Server
|
||||
run: |
|
||||
cd veza-stream-server
|
||||
cargo audit
|
||||
|
||||
- name: Typecheck
|
||||
run: npx tsc --noEmit
|
||||
working-directory: apps/web
|
||||
- name: Lint
|
||||
run: npx turbo run lint --filter=veza-chat-server --filter=veza-stream-server
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
working-directory: apps/web
|
||||
- name: Build
|
||||
run: npx turbo run build --filter=veza-chat-server --filter=veza-stream-server
|
||||
|
||||
- name: Bundle size gate
|
||||
run: node scripts/check-bundle-size.mjs
|
||||
working-directory: apps/web
|
||||
- name: Test
|
||||
run: npx turbo run test --filter=veza-chat-server --filter=veza-stream-server
|
||||
|
||||
- name: Audit dependencies
|
||||
run: npm audit --audit-level=critical
|
||||
frontend:
|
||||
name: Frontend (Web)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Unit tests
|
||||
run: npx vitest run --reporter=verbose
|
||||
working-directory: apps/web
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
|
||||
# ===========================================================================
|
||||
# Rust (Stream Server) — build, test, lint, audit
|
||||
# ===========================================================================
|
||||
rust:
|
||||
name: Rust (Stream Server)
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Security audit (npm)
|
||||
run: npm audit --audit-level=critical
|
||||
|
||||
- name: Cache rustup toolchain
|
||||
id: rustup-cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: |
|
||||
~/.rustup
|
||||
~/.cargo/bin
|
||||
key: ${{ runner.os }}-rustup-stable-rustfmt-clippy-audit-tarpaulin
|
||||
save-always: true
|
||||
- name: Cache Generated Types
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/web/src/types/generated
|
||||
key: ${{ runner.os }}-generated-types-${{ hashFiles('veza-backend-api/openapi.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-generated-types-
|
||||
|
||||
- name: Set up Rust
|
||||
if: steps.rustup-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --component rustfmt,clippy
|
||||
- name: Generate Types from OpenAPI
|
||||
run: |
|
||||
cd apps/web
|
||||
chmod +x scripts/generate-types.sh
|
||||
./scripts/generate-types.sh
|
||||
continue-on-error: false
|
||||
# This step ensures types are generated before typecheck
|
||||
# If types don't match spec, CI will fail
|
||||
# Cache keyed on openapi.yaml hash, so types regenerate when spec changes
|
||||
|
||||
- name: Add ~/.cargo/bin to PATH
|
||||
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
- name: Lint
|
||||
run: npx turbo run lint --filter=veza-frontend
|
||||
|
||||
- name: Cache Cargo deps and target
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
veza-stream-server/target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('veza-stream-server/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-
|
||||
save-always: true
|
||||
- name: Format Check
|
||||
run: |
|
||||
cd apps/web
|
||||
npm run format:check --if-present
|
||||
|
||||
- name: Build
|
||||
run: cargo build
|
||||
working-directory: veza-stream-server
|
||||
- name: Type Check
|
||||
run: |
|
||||
cd apps/web
|
||||
npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: cargo test --workspace
|
||||
working-directory: veza-stream-server
|
||||
- name: Test
|
||||
run: npx turbo run test --filter=veza-frontend -- --run
|
||||
|
||||
- name: Clippy
|
||||
# NOTE: -D warnings temporarily lifted while the team resorbs
|
||||
# the Rust clippy backlog (~20 warnings: unused imports,
|
||||
# missing Default impls, manual clamp/contains, etc.).
|
||||
# Re-enable once the backlog is cleared.
|
||||
run: cargo clippy --all-targets
|
||||
working-directory: veza-stream-server
|
||||
- name: Contrast Tests
|
||||
run: |
|
||||
cd apps/web
|
||||
npm run test -- --run src/__tests__/contrast.test.ts
|
||||
|
||||
- name: Format check
|
||||
run: cargo fmt -- --check
|
||||
working-directory: veza-stream-server
|
||||
- name: Build
|
||||
run: npx turbo run build --filter=veza-frontend
|
||||
|
||||
- name: Security audit
|
||||
# cargo-audit is cached with the rustup toolchain (~/.cargo/bin),
|
||||
# so the install is a no-op on warm cache.
|
||||
run: |
|
||||
command -v cargo-audit >/dev/null || cargo install cargo-audit --locked
|
||||
cargo audit
|
||||
working-directory: veza-stream-server
|
||||
storybook:
|
||||
name: Storybook Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Rust coverage via cargo-tarpaulin is disabled in ci.yml because
|
||||
# tarpaulin needs CAP_SYS_PTRACE to disable ASLR, which the Docker
|
||||
# container running the Forgejo act runner doesn't grant:
|
||||
# "ERROR cargo_tarpaulin: Failed to run tests:
|
||||
# ASLR disable failed: EPERM: Operation not permitted"
|
||||
# Either (a) add `privileged: true` to the runner's container
|
||||
# config to grant ptrace, or (b) switch to `cargo llvm-cov`
|
||||
# which uses source-based coverage and doesn't need ptrace.
|
||||
# Until then, run coverage locally or in a dedicated nightly job.
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
# ===========================================================================
|
||||
# Notify on failure
|
||||
# ===========================================================================
|
||||
notify-failure:
|
||||
name: Notify on failure
|
||||
needs: [backend, frontend, rust]
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build Storybook
|
||||
run: npm run build-storybook
|
||||
working-directory: apps/web
|
||||
|
||||
- name: Serve Storybook and run audit
|
||||
run: |
|
||||
npx serve -s storybook-static -l 6007 &
|
||||
for i in $(seq 1 30); do
|
||||
if curl -sf http://localhost:6007 >/dev/null; then
|
||||
echo "Storybook ready"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
curl -sf http://localhost:6007 >/dev/null || (echo "Storybook failed to start"; exit 1)
|
||||
npm run test:storybook
|
||||
working-directory: apps/web
|
||||
|
||||
e2e:
|
||||
name: E2E (Playwright)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache-dependency-path: veza-backend-api/go.sum
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Add veza.fr to hosts (for Vite proxy)
|
||||
run: echo "127.0.0.1 veza.fr" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Start backend services (Postgres, Redis, RabbitMQ)
|
||||
run: |
|
||||
docker-compose up -d postgres redis rabbitmq
|
||||
echo "Waiting for Postgres..."
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec veza_postgres pg_isready -U veza 2>/dev/null; then
|
||||
echo "Postgres ready"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
docker-compose ps
|
||||
|
||||
- name: Run database migrations
|
||||
env:
|
||||
DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go run cmd/migrate_tool/main.go
|
||||
|
||||
- name: Create E2E test user
|
||||
env:
|
||||
DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable
|
||||
TEST_EMAIL: e2e@test.com
|
||||
TEST_PASSWORD: Xk9$mP2#vL7@nQ4!wR8
|
||||
TEST_USERNAME: e2e
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go run cmd/tools/create_test_user/main.go
|
||||
|
||||
- name: Start backend API
|
||||
env:
|
||||
APP_ENV: development
|
||||
APP_PORT: "18080"
|
||||
DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable
|
||||
REDIS_URL: redis://localhost:16379
|
||||
JWT_SECRET: dev-secret-key-minimum-32-characters-long
|
||||
COOKIE_SECURE: "false"
|
||||
CORS_ALLOWED_ORIGINS: http://veza.fr:5173,http://veza.fr:5174,http://localhost:5173,http://localhost:5174
|
||||
RABBITMQ_URL: amqp://veza:devpassword@localhost:15672/
|
||||
DISABLE_RATE_LIMIT_FOR_TESTS: "true"
|
||||
ACCOUNT_LOCKOUT_EXEMPT_EMAILS: "e2e@test.com"
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go build -o veza-api ./cmd/api/main.go
|
||||
./veza-api &
|
||||
sleep 10
|
||||
curl -f http://localhost:18080/api/v1/health || (echo "Backend health check failed"; exit 1)
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
working-directory: apps/web
|
||||
|
||||
- name: Run E2E tests
|
||||
run: npx playwright test
|
||||
working-directory: apps/web
|
||||
env:
|
||||
PORT: "5174"
|
||||
VITE_API_URL: '/api/v1'
|
||||
VITE_DOMAIN: veza.fr
|
||||
VITE_BACKEND_PORT: "18080"
|
||||
PLAYWRIGHT_BASE_URL: 'http://localhost:5174'
|
||||
TEST_EMAIL: e2e@test.com
|
||||
TEST_PASSWORD: Xk9$mP2#vL7@nQ4!wR8
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
runs-on: [self-hosted, incus]
|
||||
steps:
|
||||
- name: Summary
|
||||
run: echo "## ❌ CI Failed" >> $GITHUB_STEP_SUMMARY
|
||||
with:
|
||||
name: playwright-report
|
||||
path: apps/web/playwright-report/
|
||||
retention-days: 7
|
||||
|
|
|
|||
79
.github/workflows/cleanup-failed.yml
vendored
79
.github/workflows/cleanup-failed.yml
vendored
|
|
@ -1,79 +0,0 @@
|
|||
# cleanup-failed.yml — workflow_dispatch only.
|
||||
#
|
||||
# Tears down the kept-alive failed-deploy color (the inactive one
|
||||
# that survived a Phase D / Phase F failure for forensics).
|
||||
# Operator triggers this once they have read the journalctl output.
|
||||
#
|
||||
# Hard safety in playbooks/cleanup_failed.yml: refuses to destroy
|
||||
# the currently-active color.
|
||||
name: Veza cleanup failed-deploy color
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to clean up"
|
||||
required: true
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
color:
|
||||
description: "Color to destroy (must NOT be the active one)"
|
||||
required: true
|
||||
type: choice
|
||||
options: [blue, green]
|
||||
|
||||
concurrency:
|
||||
group: cleanup-${{ inputs.env }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Destroy ${{ inputs.color }} app containers in ${{ inputs.env }}
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install ansible
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible
|
||||
ansible-galaxy collection install community.general
|
||||
|
||||
- name: Write vault password
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run cleanup_failed.yml
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-cleanup-${{ inputs.env }}-${{ inputs.color }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ inputs.env }}.yml \
|
||||
playbooks/cleanup_failed.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ inputs.env }} \
|
||||
-e target_color=${{ inputs.color }}
|
||||
|
||||
- name: Upload Ansible log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-cleanup-${{ inputs.env }}-${{ inputs.color }}
|
||||
path: ${{ runner.temp }}/ansible-cleanup-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
360
.github/workflows/deploy.yml
vendored
360
.github/workflows/deploy.yml
vendored
|
|
@ -1,360 +0,0 @@
|
|||
# Veza deploy pipeline.
|
||||
#
|
||||
# Triggers (intentionally narrow — see SECURITY note below):
|
||||
# workflow_dispatch → operator-supplied env + sha
|
||||
# (push:main + tag:v* are commented OUT until provisioning is
|
||||
# complete — see docs/RUNBOOK_DEPLOY_BOOTSTRAP.md. Re-enable
|
||||
# once secrets/runner/vault are in place and a manual run via
|
||||
# workflow_dispatch has been verified GREEN.)
|
||||
#
|
||||
# SECURITY: this workflow runs on a self-hosted runner with access to
|
||||
# the Incus unix socket (effectively root on the host). DO NOT add
|
||||
# `pull_request` or any fork-influenced trigger here — an attacker-
|
||||
# controlled fork would be able to `incus exec` arbitrarily. The
|
||||
# narrow trigger list above is the security boundary.
|
||||
#
|
||||
# Sequence : build (3 jobs in parallel) → upload artifacts → deploy.
|
||||
name: Veza deploy
|
||||
|
||||
on:
|
||||
# push: # GATED — uncomment after first
|
||||
# branches: [main] # successful workflow_dispatch run
|
||||
# tags: ['v*'] # see RUNBOOK_DEPLOY_BOOTSTRAP.md
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to deploy"
|
||||
required: true
|
||||
default: staging
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
release_sha:
|
||||
description: "Full git SHA to deploy (defaults to current HEAD if empty)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
# Only one deploy per env at a time. Newer pushes cancel older
|
||||
# in-flight builds for the same env (the user almost always wants
|
||||
# the newer commit).
|
||||
group: deploy-${{ github.ref_type == 'tag' && 'prod' || 'staging' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# Where build artefacts land. Set in Forgejo repo Variables :
|
||||
# FORGEJO_REGISTRY_URL = https://forgejo.veza.fr/api/packages/talas/generic
|
||||
REGISTRY_URL: ${{ vars.FORGEJO_REGISTRY_URL }}
|
||||
|
||||
jobs:
|
||||
# =================================================================
|
||||
# Resolve env + sha from the trigger.
|
||||
# =================================================================
|
||||
resolve:
|
||||
name: Resolve env + SHA
|
||||
runs-on: [self-hosted, incus]
|
||||
outputs:
|
||||
env: ${{ steps.r.outputs.env }}
|
||||
sha: ${{ steps.r.outputs.sha }}
|
||||
steps:
|
||||
- name: Resolve
|
||||
id: r
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
ENV="${{ inputs.env }}"
|
||||
SHA="${{ inputs.release_sha || github.sha }}"
|
||||
elif [ "${{ github.ref_type }}" = "tag" ]; then
|
||||
ENV="prod"
|
||||
SHA="${{ github.sha }}"
|
||||
else
|
||||
ENV="staging"
|
||||
SHA="${{ github.sha }}"
|
||||
fi
|
||||
if ! echo "$SHA" | grep -Eq '^[0-9a-f]{40}$'; then
|
||||
echo "SHA '$SHA' is not a 40-char git SHA"
|
||||
exit 1
|
||||
fi
|
||||
echo "env=$ENV" >> "$GITHUB_OUTPUT"
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved env=$ENV sha=$SHA"
|
||||
|
||||
# =================================================================
|
||||
# Build backend (Go).
|
||||
# =================================================================
|
||||
build-backend:
|
||||
name: Build backend
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache: true
|
||||
cache-dependency-path: veza-backend-api/go.sum
|
||||
|
||||
- name: Test
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
VEZA_SKIP_INTEGRATION: "1"
|
||||
run: go test ./... -short -count=1 -timeout 300s
|
||||
|
||||
- name: Build veza-api (CGO=0, static)
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
run: |
|
||||
go build -trimpath -ldflags "-s -w" \
|
||||
-o ./bin/veza-api ./cmd/api/main.go
|
||||
go build -trimpath -ldflags "-s -w" \
|
||||
-o ./bin/migrate_tool ./cmd/migrate_tool/main.go
|
||||
|
||||
- name: Stage tarball contents
|
||||
working-directory: veza-backend-api
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-backend"
|
||||
mkdir -p "$STAGE/migrations"
|
||||
cp ./bin/veza-api ./bin/migrate_tool "$STAGE/"
|
||||
cp -r ./migrations/* "$STAGE/migrations/" || true
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-backend-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-backend" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-backend-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-backend/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Build stream (Rust).
|
||||
# =================================================================
|
||||
build-stream:
|
||||
name: Build stream
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Set up Rust toolchain
|
||||
run: |
|
||||
command -v rustup >/dev/null || \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
sudo apt-get update -qq && sudo apt-get install -y musl-tools
|
||||
|
||||
- name: Cache cargo + target
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
veza-stream-server/target
|
||||
key: deploy-${{ runner.os }}-cargo-${{ hashFiles('veza-stream-server/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
deploy-${{ runner.os }}-cargo-
|
||||
|
||||
- name: Test
|
||||
working-directory: veza-stream-server
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Build stream_server (musl static)
|
||||
working-directory: veza-stream-server
|
||||
run: |
|
||||
cargo build --release --locked \
|
||||
--target x86_64-unknown-linux-musl
|
||||
|
||||
- name: Stage tarball contents
|
||||
working-directory: veza-stream-server
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-stream"
|
||||
mkdir -p "$STAGE"
|
||||
cp ./target/x86_64-unknown-linux-musl/release/stream_server "$STAGE/"
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-stream-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-stream" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-stream-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-stream/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Build web (React/Vite).
|
||||
# =================================================================
|
||||
build-web:
|
||||
name: Build web
|
||||
needs: resolve
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build design tokens
|
||||
run: npm run build:tokens --workspace=@veza/design-system
|
||||
|
||||
- name: Build SPA
|
||||
working-directory: apps/web
|
||||
env:
|
||||
VITE_API_URL: /api/v1
|
||||
VITE_DOMAIN: ${{ needs.resolve.outputs.env == 'prod' && 'veza.fr' || 'staging.veza.fr' }}
|
||||
VITE_RELEASE_SHA: ${{ needs.resolve.outputs.sha }}
|
||||
run: npm run build
|
||||
|
||||
- name: Stage tarball contents
|
||||
run: |
|
||||
STAGE="$RUNNER_TEMP/veza-web"
|
||||
mkdir -p "$STAGE"
|
||||
cp -r apps/web/dist/* "$STAGE/"
|
||||
echo "${{ needs.resolve.outputs.sha }}" > "$STAGE/VERSION"
|
||||
|
||||
- name: Pack tarball
|
||||
run: |
|
||||
cd "$RUNNER_TEMP"
|
||||
tar --use-compress-program=zstd -cf \
|
||||
"veza-web-${{ needs.resolve.outputs.sha }}.tar.zst" \
|
||||
-C "$RUNNER_TEMP/veza-web" .
|
||||
|
||||
- name: Push to Forgejo Package Registry
|
||||
env:
|
||||
TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
TARBALL="veza-web-${{ needs.resolve.outputs.sha }}.tar.zst"
|
||||
URL="${REGISTRY_URL}/veza-web/${{ needs.resolve.outputs.sha }}/${TARBALL}"
|
||||
echo "PUT → $URL"
|
||||
curl -fsSL --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file "$RUNNER_TEMP/${TARBALL}" \
|
||||
"${URL}"
|
||||
|
||||
# =================================================================
|
||||
# Deploy via Ansible. Runs on the self-hosted runner that has
|
||||
# Incus socket access (label `incus`). Requires Forgejo secrets:
|
||||
# ANSIBLE_VAULT_PASSWORD — unlocks group_vars/all/vault.yml
|
||||
# FORGEJO_REGISTRY_TOKEN — same token the build jobs use,
|
||||
# passed to ansible-playbook so
|
||||
# the data containers can fetch
|
||||
# the tarballs they were just sent.
|
||||
# =================================================================
|
||||
deploy:
|
||||
name: Deploy via Ansible
|
||||
needs: [resolve, build-backend, build-stream, build-web]
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ needs.resolve.outputs.sha }}
|
||||
|
||||
- name: Install ansible + community.general + community.postgresql + community.rabbitmq
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible python3-psycopg2 python3-pip
|
||||
ansible-galaxy collection install \
|
||||
community.general \
|
||||
community.postgresql \
|
||||
community.rabbitmq
|
||||
|
||||
- name: Write vault password to a tmpfile
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run deploy_data.yml (idempotent provisioning + ZFS snapshot)
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-data-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ needs.resolve.outputs.env }}.yml \
|
||||
playbooks/deploy_data.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ needs.resolve.outputs.env }} \
|
||||
-e veza_release_sha=${{ needs.resolve.outputs.sha }} \
|
||||
-e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Run deploy_app.yml (blue/green)
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-app-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
ansible-playbook \
|
||||
-i inventory/${{ needs.resolve.outputs.env }}.yml \
|
||||
playbooks/deploy_app.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
-e veza_env=${{ needs.resolve.outputs.env }} \
|
||||
-e veza_release_sha=${{ needs.resolve.outputs.sha }} \
|
||||
-e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Upload Ansible logs (for forensics)
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-logs-${{ needs.resolve.outputs.env }}-${{ needs.resolve.outputs.sha }}
|
||||
path: ${{ runner.temp }}/ansible-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
270
.github/workflows/e2e.yml
vendored
270
.github/workflows/e2e.yml
vendored
|
|
@ -1,270 +0,0 @@
|
|||
name: E2E Playwright
|
||||
|
||||
# v1.0.8 Batch C — Playwright E2E suite triggered on PRs (@critical only,
|
||||
# fast feedback) + push to main and nightly (full suite, deeper coverage).
|
||||
# Uses the --ci seed flag (cmd/tools/seed --ci) for ~5s seeding instead
|
||||
# of the ~60s minimal seed.
|
||||
|
||||
on:
|
||||
# GATED on Forgejo (single self-hosted runner) — re-enable
|
||||
# selectively when an additional runner with a Docker label
|
||||
# (e.g. ubuntu-latest:docker://...) is provisioned. Until then,
|
||||
# heavy E2E only runs on operator-triggered workflow_dispatch.
|
||||
# pull_request:
|
||||
# branches: [main]
|
||||
# push:
|
||||
# branches: [main]
|
||||
# schedule:
|
||||
# - cron: "0 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||
# Forces playwright.config.ts:141,155 to spawn fresh backend + Vite
|
||||
# instead of reusing whatever is on the runner.
|
||||
CI: "true"
|
||||
# Falls back to a CI-only dev key if the Forgejo secret is unset.
|
||||
# Used at the "Build + start backend API" step.
|
||||
JWT_SECRET: ${{ secrets.E2E_JWT_SECRET || 'ci-dev-jwt-secret-32-chars-min-padding!!' }}
|
||||
|
||||
jobs:
|
||||
# ===========================================================================
|
||||
# Job: e2e — single matrix entry that selects the test scope per trigger.
|
||||
# - PR → @critical only (5-7min target)
|
||||
# - push main / cron / dispatch → full suite (~25min target)
|
||||
# ===========================================================================
|
||||
e2e:
|
||||
# Scope matrix:
|
||||
# - pull_request → @critical (PR gate, ~5-10min)
|
||||
# - push to main → @critical (commit gate, dev velocity priority)
|
||||
# - schedule (cron) → full suite (nightly coverage)
|
||||
# - workflow_dispatch → full (manual broad sweep)
|
||||
# Push was previously running the full suite (~1h30 pre-perf, ~15-20min
|
||||
# post-perf). The dev velocity cost was unjustifiable for the
|
||||
# incremental coverage over the @critical scope, especially while the
|
||||
# full suite carries pre-existing fixme'd tests. Cron picks up the
|
||||
# rest on a 24h cadence.
|
||||
name: e2e (${{ (github.event_name == 'pull_request' || github.event_name == 'push') && '@critical' || 'full' }})
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: ${{ (github.event_name == 'pull_request' || github.event_name == 'push') && 20 || 45 }}
|
||||
|
||||
# Service containers are managed by act_runner: spawned on the job
|
||||
# network with healthchecks, torn down at the end. This replaces
|
||||
# the previous `docker compose up -d` pattern which relied on
|
||||
# docker socket sharing + host port mappings — fragile (port
|
||||
# collisions across concurrent jobs, manual cleanup, double-DinD,
|
||||
# whole compose file validated even when only 3 services are
|
||||
# needed). Service hostnames (`postgres`, `redis`, `rabbitmq`)
|
||||
# resolve from the job container on standard ports.
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_USER: veza
|
||||
POSTGRES_PASSWORD: devpassword
|
||||
POSTGRES_DB: veza
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U veza"
|
||||
--health-interval 5s
|
||||
--health-timeout 3s
|
||||
--health-retries 10
|
||||
redis:
|
||||
# No-auth redis for CI: act_runner services don't support a
|
||||
# `command:` field, and the redis:7-alpine entrypoint does
|
||||
# NOT read REDIS_ARGS (verified empirically) — so passing
|
||||
# --requirepass via env doesn't work. The dev/prod password
|
||||
# policy (REM-023) is enforced via docker-compose.yml only;
|
||||
# the CI service network is ephemeral and isolated, so
|
||||
# dropping auth here is acceptable.
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 5s
|
||||
--health-timeout 3s
|
||||
--health-retries 10
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management-alpine
|
||||
env:
|
||||
RABBITMQ_DEFAULT_USER: veza
|
||||
RABBITMQ_DEFAULT_PASS: devpassword
|
||||
options: >-
|
||||
--health-cmd "rabbitmq-diagnostics -q check_port_connectivity"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
# Service hostnames + standard ports — no host-port mapping needed.
|
||||
env:
|
||||
DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@postgres:5432/veza?sslmode=disable
|
||||
REDIS_URL: redis://redis:6379
|
||||
RABBITMQ_URL: ${{ secrets.E2E_RABBITMQ_URL || 'amqp://veza:devpassword@rabbitmq:5672/' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache: true
|
||||
cache-dependency-path: veza-backend-api/go.sum
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# Sprint 2 design-system migrated to Style Dictionary; the
|
||||
# generated tokens live in packages/design-system/dist/
|
||||
# (gitignored). The Playwright-spawned Vite imports them via
|
||||
# `@veza/design-system/tokens-generated`, so dist/ MUST exist
|
||||
# before vite starts.
|
||||
- name: Build design tokens
|
||||
run: npm run build:tokens --workspace=@veza/design-system
|
||||
|
||||
# Playwright tests reach the frontend via http://veza.fr:5174,
|
||||
# which the browsers resolve via /etc/hosts. Without this entry
|
||||
# the navigation step times out.
|
||||
- name: Add veza.fr to hosts
|
||||
run: echo "127.0.0.1 veza.fr" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Generate dev JWT keys + SSL cert
|
||||
run: |
|
||||
./scripts/generate-jwt-keys.sh
|
||||
./scripts/generate-ssl-cert.sh
|
||||
|
||||
- name: Run database migrations
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go run cmd/migrate_tool/main.go
|
||||
|
||||
- name: Seed database (CI mode — 5 test accounts + minimal fixtures)
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go run ./cmd/tools/seed --ci
|
||||
|
||||
- name: Build + start backend API
|
||||
env:
|
||||
APP_ENV: test
|
||||
APP_PORT: "18080"
|
||||
COOKIE_SECURE: "false"
|
||||
CORS_ALLOWED_ORIGINS: http://veza.fr:5174,http://localhost:5174
|
||||
DISABLE_RATE_LIMIT_FOR_TESTS: "true"
|
||||
RATE_LIMIT_LIMIT: "10000"
|
||||
RATE_LIMIT_WINDOW: "60"
|
||||
ACCOUNT_LOCKOUT_EXEMPT_EMAILS: "user@veza.music,artist@veza.music,admin@veza.music,mod@veza.music,new@veza.music"
|
||||
run: |
|
||||
cd veza-backend-api
|
||||
go build -o veza-api ./cmd/api/main.go
|
||||
./veza-api > /tmp/backend.log 2>&1 &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# Poll for up to 30s — beats a fixed sleep on a cold start.
|
||||
for i in $(seq 1 30); do
|
||||
if curl -sf -m 2 http://localhost:18080/api/v1/health > /tmp/health.json 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
echo "::error::backend process died before becoming reachable"
|
||||
echo "--- /tmp/backend.log (last 200 lines) ---"
|
||||
tail -200 /tmp/backend.log
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Always print the response body so debugging doesn't
|
||||
# require re-running with extra logging. Artifact upload
|
||||
# is broken under Forgejo (GHES not supported), so the
|
||||
# log step output is our only diagnostic channel.
|
||||
echo "--- /api/v1/health response ---"
|
||||
cat /tmp/health.json
|
||||
echo
|
||||
|
||||
# The /api/v1/health envelope is the standard veza response
|
||||
# shape: {"success": true, "data": {"status": "ok"}}. Earlier
|
||||
# versions of this check used `.status == "ok"` at the root,
|
||||
# which silently misses the actual ok signal nested under
|
||||
# `.data`. The misread surfaced as "backend health is not ok"
|
||||
# despite a 200 + valid body — wasted a CI cycle.
|
||||
if ! jq -e '.data.status == "ok"' /tmp/health.json >/dev/null; then
|
||||
echo "::error::backend health is not ok"
|
||||
echo "--- /tmp/backend.log (last 200 lines) ---"
|
||||
tail -200 /tmp/backend.log
|
||||
exit 1
|
||||
fi
|
||||
echo "Backend healthy"
|
||||
|
||||
# Cache the Playwright browser binaries between runs.
|
||||
# Chromium download is ~150MB and adds 30-60s to every cold
|
||||
# run. The cache key tracks the playwright version pinned in
|
||||
# package-lock.json, so a Playwright bump invalidates the
|
||||
# cache automatically.
|
||||
- name: Resolve Playwright version
|
||||
id: playwright-version
|
||||
run: |
|
||||
PV=$(node -p "require('./node_modules/@playwright/test/package.json').version")
|
||||
echo "version=$PV" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
id: playwright-cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}-chromium
|
||||
restore-keys: |
|
||||
playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}-
|
||||
|
||||
- name: Install Playwright browsers
|
||||
# Browsers cached: only install OS deps (apt-get sweep) so the
|
||||
# download is skipped. Browsers absent: full install + deps.
|
||||
run: |
|
||||
if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then
|
||||
npx playwright install-deps chromium
|
||||
else
|
||||
npx playwright install --with-deps chromium
|
||||
fi
|
||||
|
||||
- name: Run E2E (@critical — PR + push)
|
||||
if: github.event_name == 'pull_request' || github.event_name == 'push'
|
||||
env:
|
||||
PORT: "5174"
|
||||
VITE_API_URL: "/api/v1"
|
||||
VITE_DOMAIN: veza.fr
|
||||
VITE_BACKEND_PORT: "18080"
|
||||
PLAYWRIGHT_BASE_URL: "http://localhost:5174"
|
||||
run: npm run e2e:critical
|
||||
|
||||
- name: Run E2E (full — cron / workflow_dispatch)
|
||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
PORT: "5174"
|
||||
VITE_API_URL: "/api/v1"
|
||||
VITE_DOMAIN: veza.fr
|
||||
VITE_BACKEND_PORT: "18080"
|
||||
PLAYWRIGHT_BASE_URL: "http://localhost:5174"
|
||||
run: npm run e2e
|
||||
|
||||
- name: Upload Playwright report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: |
|
||||
tests/e2e/playwright-report/
|
||||
tests/e2e/test-results/
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload backend log
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: backend-log-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: /tmp/backend.log
|
||||
retention-days: 7
|
||||
39
.github/workflows/frontend-ci.yml
vendored
Normal file
39
.github/workflows/frontend-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
name: Frontend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "apps/web/**"
|
||||
- ".github/workflows/frontend-ci.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "apps/web/**"
|
||||
- ".github/workflows/frontend-ci.yml"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/web
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: 'npm'
|
||||
cache-dependency-path: apps/web/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Audit dependencies
|
||||
run: npm audit --audit-level=high
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test -- --run
|
||||
|
||||
43
.github/workflows/go-fuzz.yml
vendored
43
.github/workflows/go-fuzz.yml
vendored
|
|
@ -1,43 +0,0 @@
|
|||
name: Go Fuzz Tests
|
||||
|
||||
on:
|
||||
# GATED — operator-triggered until extra runner capacity exists.
|
||||
# schedule:
|
||||
# - cron: "0 2 * * *" # Nightly at 2am UTC
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||
|
||||
jobs:
|
||||
fuzz:
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 15
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: veza-backend-api
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
with:
|
||||
go-version: "1.25"
|
||||
cache: true
|
||||
|
||||
- name: Download deps
|
||||
run: go mod download
|
||||
|
||||
- name: Run fuzz tests
|
||||
run: go test -fuzz=Fuzz -fuzztime=60s ./internal/handlers/...
|
||||
|
||||
- name: Upload fuzz corpus
|
||||
if: always()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: fuzz-corpus
|
||||
path: veza-backend-api/testdata/fuzz/
|
||||
retention-days: 30
|
||||
81
.github/workflows/load-test-nightly.yml
vendored
Normal file
81
.github/workflows/load-test-nightly.yml
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
name: Load Tests (Nightly)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
load-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install k6
|
||||
run: |
|
||||
sudo gpg -k
|
||||
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
|
||||
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
|
||||
sudo apt-get update && sudo apt-get install -y k6
|
||||
|
||||
- name: Start infrastructure
|
||||
run: |
|
||||
docker-compose -f docker-compose.yml up -d postgres redis rabbitmq
|
||||
sleep 15
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
cache: true
|
||||
|
||||
- name: Run migrations
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable
|
||||
REDIS_URL: redis://localhost:16379
|
||||
JWT_SECRET: test-jwt-secret-for-load-test
|
||||
APP_ENV: test
|
||||
run: |
|
||||
go mod download
|
||||
go run cmd/migrate_tool/main.go || true
|
||||
|
||||
- name: Start backend API
|
||||
working-directory: veza-backend-api
|
||||
env:
|
||||
DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable
|
||||
REDIS_URL: redis://localhost:16379
|
||||
RABBITMQ_URL: amqp://veza:devpassword@localhost:15672/
|
||||
JWT_SECRET: test-jwt-secret-for-load-test
|
||||
APP_ENV: test
|
||||
PORT: 8080
|
||||
run: |
|
||||
go run cmd/api/main.go &
|
||||
sleep 15
|
||||
|
||||
- name: Wait for backend
|
||||
run: |
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if curl -sf http://localhost:8080/health; then
|
||||
echo "Backend ready"
|
||||
exit 0
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
echo "Backend not ready"
|
||||
exit 1
|
||||
|
||||
- name: Run smoke load test
|
||||
run: k6 run loadtests/smoke.js
|
||||
|
||||
- name: Run backend load test
|
||||
run: |
|
||||
k6 run --out json=load-results.json loadtests/backend/full.js || true
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: load-test-results
|
||||
path: load-results.json
|
||||
if: always()
|
||||
126
.github/workflows/loadtest.yml
vendored
126
.github/workflows/loadtest.yml
vendored
|
|
@ -1,126 +0,0 @@
|
|||
name: k6 nightly load test
|
||||
|
||||
# v1.0.9 W4 Day 20 — runs the mixed-scenarios k6 script against the
|
||||
# staging environment every night at 02:30 UTC. The acceptance gate
|
||||
# is "pass green 3 nuits consécutives" before flipping a release —
|
||||
# the artifact uploaded by this workflow carries the JSON summary
|
||||
# the operator inspects.
|
||||
#
|
||||
# Scope deliberately narrow : runs ONLY on staging, NEVER on prod.
|
||||
# A separate manually-triggered workflow (workflow_dispatch) covers
|
||||
# pre-launch capacity drills with a longer ramp.
|
||||
|
||||
on:
|
||||
# GATED — k6 hammer is too heavy for the single self-hosted runner.
|
||||
# Re-enable the cron once a dedicated load-test runner exists.
|
||||
# schedule:
|
||||
# - cron: "30 2 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
duration:
|
||||
description: "Duration per scenario (e.g. 5m, 15m, 1h)"
|
||||
required: false
|
||||
default: "5m"
|
||||
type: string
|
||||
base_url:
|
||||
description: "Override staging URL"
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
# Defaults — override via workflow_dispatch input or repo vars.
|
||||
DEFAULT_BASE_URL: "https://staging.veza.fr"
|
||||
|
||||
jobs:
|
||||
loadtest:
|
||||
name: k6 mixed scenarios (1650 VU steady)
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install k6
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo gpg -k
|
||||
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
|
||||
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
|
||||
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/k6.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y k6
|
||||
k6 version
|
||||
|
||||
- name: Resolve test inputs
|
||||
id: inputs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
BASE_URL="${{ github.event.inputs.base_url }}"
|
||||
if [ -z "$BASE_URL" ]; then
|
||||
BASE_URL="${{ vars.STAGING_BASE_URL || env.DEFAULT_BASE_URL }}"
|
||||
fi
|
||||
DURATION="${{ github.event.inputs.duration }}"
|
||||
if [ -z "$DURATION" ]; then
|
||||
DURATION="5m"
|
||||
fi
|
||||
echo "base_url=$BASE_URL" >> "$GITHUB_OUTPUT"
|
||||
echo "duration=$DURATION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pre-flight — staging is reachable
|
||||
run: |
|
||||
set -euo pipefail
|
||||
url="${{ steps.inputs.outputs.base_url }}/api/v1/health"
|
||||
echo "::notice::Pre-flight GET $url"
|
||||
status=$(curl -k -sS --max-time 10 -o /dev/null -w "%{http_code}" "$url" || echo "000")
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::Staging /health returned $status — aborting load test."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run k6 mixed scenarios
|
||||
id: run
|
||||
env:
|
||||
BASE_URL: ${{ steps.inputs.outputs.base_url }}
|
||||
DURATION: ${{ steps.inputs.outputs.duration }}
|
||||
USER_TOKEN: ${{ secrets.STAGING_LOADTEST_TOKEN }}
|
||||
STREAM_TRACK_ID: ${{ vars.STAGING_LOADTEST_TRACK_ID || '00000000-0000-0000-0000-000000000001' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$USER_TOKEN" ]; then
|
||||
echo "::warning::STAGING_LOADTEST_TOKEN secret is empty — auth-required scenarios will record 401s as errors."
|
||||
fi
|
||||
k6 run --quiet \
|
||||
--summary-export=k6-summary.json \
|
||||
scripts/loadtest/k6_mixed_scenarios.js
|
||||
|
||||
- name: Upload k6 summary artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: k6-summary-${{ github.run_number }}
|
||||
path: |
|
||||
k6-summary.json
|
||||
scripts/loadtest/k6_mixed_scenarios.js
|
||||
retention-days: 30
|
||||
|
||||
- name: Annotate thresholds in summary
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ ! -f k6-summary.json ]; then
|
||||
echo "::warning::No summary artifact — k6 likely failed before write."
|
||||
exit 0
|
||||
fi
|
||||
echo "## k6 load test summary" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
jq -r '
|
||||
(.metrics.http_reqs.values.count // 0) as $reqs
|
||||
| (.metrics.http_req_failed.values.rate // 0) as $err
|
||||
| (.metrics.http_req_duration.values["p(95)"] // 0) as $p95
|
||||
| (.metrics.http_req_duration.values["p(99)"] // 0) as $p99
|
||||
| "- requests: \($reqs)\n- failed rate: \($err * 100 | round)/100 %\n- p95: \($p95 | round) ms\n- p99: \($p99 | round) ms"
|
||||
' k6-summary.json >> "$GITHUB_STEP_SUMMARY"
|
||||
118
.github/workflows/rollback.yml
vendored
118
.github/workflows/rollback.yml
vendored
|
|
@ -1,118 +0,0 @@
|
|||
# rollback.yml — workflow_dispatch only.
|
||||
#
|
||||
# Two modes :
|
||||
# fast — flip HAProxy back to the previous color. ~5s. Requires
|
||||
# the target color's containers to still be alive
|
||||
# (i.e., no later deploy has recycled them).
|
||||
# full — re-run deploy_app.yml with a specific (older) release_sha.
|
||||
# ~5-10min. The artefact must still be in the Forgejo
|
||||
# registry (default retention 30 SHA per component).
|
||||
#
|
||||
# See docs/RUNBOOK_ROLLBACK.md for decision criteria.
|
||||
name: Veza rollback
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: "Environment to rollback"
|
||||
required: true
|
||||
type: choice
|
||||
options: [staging, prod]
|
||||
mode:
|
||||
description: "Rollback mode"
|
||||
required: true
|
||||
type: choice
|
||||
options: [fast, full]
|
||||
target_color:
|
||||
description: "(mode=fast only) color to flip back TO (the prior active one)"
|
||||
required: false
|
||||
type: choice
|
||||
options: [blue, green]
|
||||
release_sha:
|
||||
description: "(mode=full only) 40-char SHA of the release to redeploy"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: rollback-${{ inputs.env }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
rollback:
|
||||
name: Rollback ${{ inputs.env }} (${{ inputs.mode }})
|
||||
runs-on: [self-hosted, incus]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
run: |
|
||||
if [ "${{ inputs.mode }}" = "fast" ] && [ -z "${{ inputs.target_color }}" ]; then
|
||||
echo "mode=fast requires target_color"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ inputs.mode }}" = "full" ]; then
|
||||
if [ -z "${{ inputs.release_sha }}" ]; then
|
||||
echo "mode=full requires release_sha"
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "${{ inputs.release_sha }}" | grep -Eq '^[0-9a-f]{40}$'; then
|
||||
echo "release_sha is not a 40-char git SHA"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ inputs.mode == 'full' && inputs.release_sha || github.ref }}
|
||||
|
||||
- name: Install ansible + collections
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible python3-psycopg2
|
||||
ansible-galaxy collection install \
|
||||
community.general \
|
||||
community.postgresql \
|
||||
community.rabbitmq
|
||||
|
||||
- name: Write vault password
|
||||
env:
|
||||
VAULT_PW: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
|
||||
run: |
|
||||
printf '%s' "$VAULT_PW" > "$RUNNER_TEMP/vault-pass"
|
||||
chmod 0400 "$RUNNER_TEMP/vault-pass"
|
||||
echo "VAULT_PASS_FILE=$RUNNER_TEMP/vault-pass" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run rollback.yml
|
||||
working-directory: infra/ansible
|
||||
env:
|
||||
ANSIBLE_LOG_PATH: ${{ runner.temp }}/ansible-rollback-${{ inputs.env }}-${{ inputs.mode }}.log
|
||||
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||
run: |
|
||||
EXTRA="-e veza_env=${{ inputs.env }} -e mode=${{ inputs.mode }}"
|
||||
if [ "${{ inputs.mode }}" = "fast" ]; then
|
||||
EXTRA="$EXTRA -e target_color=${{ inputs.target_color }}"
|
||||
else
|
||||
EXTRA="$EXTRA -e veza_release_sha=${{ inputs.release_sha }}"
|
||||
EXTRA="$EXTRA -e vault_forgejo_registry_token=${{ secrets.FORGEJO_REGISTRY_TOKEN }}"
|
||||
fi
|
||||
ansible-playbook \
|
||||
-i inventory/${{ inputs.env }}.yml \
|
||||
playbooks/rollback.yml \
|
||||
--vault-password-file "$VAULT_PASS_FILE" \
|
||||
$EXTRA
|
||||
|
||||
- name: Upload Ansible log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ansible-rollback-${{ inputs.env }}-${{ inputs.mode }}
|
||||
path: ${{ runner.temp }}/ansible-rollback-*.log
|
||||
retention-days: 30
|
||||
|
||||
- name: Shred vault password file
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f "$VAULT_PASS_FILE" ]; then
|
||||
shred -u "$VAULT_PASS_FILE" 2>/dev/null || rm -f "$VAULT_PASS_FILE"
|
||||
fi
|
||||
38
.github/workflows/security-scan.yml
vendored
38
.github/workflows/security-scan.yml
vendored
|
|
@ -1,28 +1,22 @@
|
|||
name: Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
gitleaks:
|
||||
name: Secret Scanning (gitleaks)
|
||||
runs-on: [self-hosted, incus]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
gitleaks:
|
||||
name: Secret Scanning (gitleaks)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install gitleaks
|
||||
run: |
|
||||
wget -q https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz
|
||||
tar xzf gitleaks_8.21.2_linux_x64.tar.gz
|
||||
chmod +x gitleaks
|
||||
|
||||
- name: Run gitleaks
|
||||
run: ./gitleaks detect --source . --no-banner -v --config .gitleaks.toml
|
||||
- name: Run Gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
|||
47
.github/workflows/storybook-audit.yml
vendored
Normal file
47
.github/workflows/storybook-audit.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Storybook audit: build static Storybook, serve it, run the audit script.
|
||||
# Fails the job if any story has console errors, page errors, or unhandled network failures.
|
||||
# See docs/STORYBOOK_CONTRACT.md and apps/web/scripts/audit-storybook.js.
|
||||
name: Storybook Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "apps/web/**"
|
||||
- ".github/workflows/storybook-audit.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "apps/web/**"
|
||||
- ".github/workflows/storybook-audit.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
name: Build & audit Storybook
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/web
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: apps/web/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
run: npx playwright install chromium --with-deps
|
||||
|
||||
- name: Validate Storybook (build, serve 6007, audit)
|
||||
run: npm run validate:storybook
|
||||
env:
|
||||
VITE_API_URL: /api/v1
|
||||
VITE_USE_MSW: "true"
|
||||
VITE_STORYBOOK: "true"
|
||||
41
.github/workflows/stream-ci.yml
vendored
Normal file
41
.github/workflows/stream-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Stream Server CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "veza-stream-server/**"
|
||||
- "veza-common/**"
|
||||
- ".github/workflows/stream-ci.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "veza-stream-server/**"
|
||||
- "veza-common/**"
|
||||
- ".github/workflows/stream-ci.yml"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: veza-stream-server
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
- name: Lint with clippy
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
|
||||
- name: Audit dependencies
|
||||
uses: actions-rust-lang/audit@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --all
|
||||
|
||||
24
.github/workflows/trivy-fs.yml
vendored
24
.github/workflows/trivy-fs.yml
vendored
|
|
@ -1,24 +0,0 @@
|
|||
name: Trivy Filesystem Scan
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
|
||||
jobs:
|
||||
trivy-scan:
|
||||
name: Trivy FS Scan
|
||||
runs-on: [self-hosted, incus]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Install Trivy
|
||||
run: |
|
||||
wget -qO- https://github.com/aquasecurity/trivy/releases/download/v0.58.1/trivy_0.58.1_Linux-64bit.tar.gz | tar xz
|
||||
chmod +x trivy
|
||||
|
||||
- name: Scan filesystem
|
||||
run: ./trivy fs --severity HIGH,CRITICAL --exit-code 1 .
|
||||
170
.gitignore
vendored
170
.gitignore
vendored
|
|
@ -36,11 +36,6 @@ logs/
|
|||
*.seed
|
||||
*.gz
|
||||
|
||||
### Database dumps — SECURITY(REM-034): Never commit database artifacts
|
||||
**/veza_back_api_db/
|
||||
*.sql.dump
|
||||
*.pgdump
|
||||
|
||||
### Editors / IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
|
@ -83,12 +78,10 @@ apps/web/dist_verification/
|
|||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.staging.example
|
||||
**/.env
|
||||
**/.env.local
|
||||
**/.env.*
|
||||
!.env.example
|
||||
!.env.staging.example
|
||||
veza-backend-api/.env
|
||||
veza-chat-server/.env
|
||||
veza-stream-server/.env
|
||||
|
|
@ -99,35 +92,15 @@ apps/web/.env.local
|
|||
docker-data/
|
||||
*.tar
|
||||
|
||||
# HAProxy SSL certs (never commit private keys or full-chain certs)
|
||||
# HAProxy SSL certs (never commit private keys)
|
||||
docker/haproxy/certs/*.key
|
||||
docker/haproxy/certs/*.pem
|
||||
docker/haproxy/certs/*.crt
|
||||
|
||||
# JWT RSA keys (v0.9.1 RS256 migration — NEVER commit)
|
||||
jwt-private.pem
|
||||
jwt-public.pem
|
||||
|
||||
veza-backend-api/main
|
||||
veza-backend-api/api
|
||||
veza-backend-api/veza-api
|
||||
veza-backend-api/migrate_tool
|
||||
chat_exports/
|
||||
|
||||
# Debug/test screenshots (root level)
|
||||
screenshot-*.png
|
||||
sidebar-*.png
|
||||
player-*.png
|
||||
login-*.png
|
||||
search-*.png
|
||||
track-*.png
|
||||
test-*.png
|
||||
dashboard-*.png
|
||||
report-*.html
|
||||
|
||||
# MCP config (local)
|
||||
.mcp.json
|
||||
|
||||
# Environment / Secrets — config templates only, never commit real .env
|
||||
config/incus/env/*.env
|
||||
!config/incus/env/env.example
|
||||
|
|
@ -135,150 +108,11 @@ config/incus/env/*.env
|
|||
# Playwright
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
tests/e2e/test-results/
|
||||
tests/e2e/VEZA_AUDIT_REPORT.html
|
||||
tests/e2e/VEZA_AUDIT_REPORT.json
|
||||
apps/web/e2e-results.json
|
||||
e2e-results.json
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/playwright/.auth/
|
||||
apps/web/e2e/.auth/
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
||||
# v0.941: Swagger docs.go generated by CI (swag init)
|
||||
veza-backend-api/docs/docs.go
|
||||
|
||||
# Claude Code local memory
|
||||
.claude/
|
||||
|
||||
# Test audio files (large binaries)
|
||||
veza-backend-api/audio/
|
||||
|
||||
# SELinux policy (local)
|
||||
qemu-fusefs.*
|
||||
# Root-level 'api' binary produced by `go build` in veza-backend-api/.
|
||||
# Narrower than the previous bare `api` rule which matched any file or
|
||||
# directory named 'api' anywhere (including apps/web/src/services/api/).
|
||||
/api
|
||||
/veza-backend-api/api
|
||||
|
||||
# ============================================================
|
||||
# Post-audit J1 (2026-04-14) — never recommit this debris
|
||||
# ============================================================
|
||||
# Go binaries accidentally committed (v1.0.3 → v1.0.4 cleanup)
|
||||
veza-backend-api/server
|
||||
veza-backend-api/modern-server
|
||||
veza-backend-api/seed
|
||||
veza-backend-api/seed-v2
|
||||
veza-backend-api/encrypt_oauth_tokens
|
||||
|
||||
# Coverage reports (generated, never tracked)
|
||||
veza-backend-api/coverage*.out
|
||||
veza-backend-api/coverage_groups/
|
||||
|
||||
# Frontend build/lint/test artifacts
|
||||
apps/web/lint_report*.json
|
||||
apps/web/tsc*.log
|
||||
apps/web/tsc*.txt
|
||||
apps/web/ts_*.log
|
||||
apps/web/storybook_*.json
|
||||
apps/web/debug-storybook.log
|
||||
apps/web/build_errors*.txt
|
||||
apps/web/build_output.txt
|
||||
apps/web/final_errors.txt
|
||||
apps/web/*.log
|
||||
apps/web/diagnostic-*.log
|
||||
apps/web/frontend.log
|
||||
apps/web/audit.log
|
||||
|
||||
# Backend local logs
|
||||
veza-backend-api/backend*.log
|
||||
|
||||
# Root audit screenshots (belong in docs/assets/ if needed)
|
||||
/audit-*.png
|
||||
|
||||
# AI tooling session state (not code)
|
||||
.cursor/
|
||||
|
||||
# ============================================================
|
||||
# Post-audit J2 (2026-04-20) — branch chore/v1.0.7-cleanup
|
||||
# ============================================================
|
||||
|
||||
# Tracked audio fixtures — use git-lfs or fixtures repo, never commit raw audio
|
||||
veza-backend-api/uploads/
|
||||
|
||||
# TLS/SSL certificates committed pre-2026-04 (regen with scripts/generate-ssl-cert.sh)
|
||||
config/ssl/*.pem
|
||||
config/ssl/*.key
|
||||
config/ssl/*.crt
|
||||
|
||||
# Playwright MCP session debris
|
||||
.playwright-mcp/
|
||||
|
||||
# AI session artefacts / context dumps
|
||||
CLAUDE_CONTEXT.txt
|
||||
UI_CONTEXT_SUMMARY.md
|
||||
*.context.txt
|
||||
*.ai-session.txt
|
||||
|
||||
# One-off generated tooling scripts (should live in scripts/ if kept)
|
||||
/generate_page_fix_prompts.sh
|
||||
/build-archive.log
|
||||
|
||||
# Apps/web stale audit reports (generated, never tracked)
|
||||
apps/web/AUDIT_ISSUES.json
|
||||
apps/web/audit_remediation.json
|
||||
apps/web/lint_comprehensive.json
|
||||
apps/web/storybook-roadmap.json
|
||||
apps/web/storybook-*.json
|
||||
|
||||
# Root PNG screenshots — move to docs/screenshots/ if historical value
|
||||
/design-system-*.png
|
||||
/forgot-password-*.png
|
||||
/register-*.png
|
||||
/reset-password-*.png
|
||||
/settings-*.png
|
||||
/storybook-*.png
|
||||
|
||||
# ============================================================
|
||||
# Post-audit J3 (2026-04-23) — history rewrite (BFG pass, 1.5G → 66M)
|
||||
# ============================================================
|
||||
# Additional Go build artifacts found in BFG scan
|
||||
veza-backend-api/bin/
|
||||
veza-backend-api/veza-backend-api
|
||||
veza-backend-api/migrate
|
||||
|
||||
# Vendored binaries mistakenly committed
|
||||
dev-environment/scripts/kubectl
|
||||
|
||||
# Incus build outputs (generated per release cut)
|
||||
.build/
|
||||
|
||||
# E2E report outputs (Playwright)
|
||||
tests/e2e/audit/results/
|
||||
tests/e2e/playwright-report/
|
||||
|
||||
# Session-scratch screenshots
|
||||
frontend_screenshots/
|
||||
|
||||
# Audit_remediation glob (supersedes J2's exact-match json)
|
||||
apps/web/audit_remediation*
|
||||
|
||||
# ============================================================
|
||||
# Ansible Vault — secrets at rest stay encrypted in vault.yml
|
||||
# (committed). The vault password used to unlock them MUST NOT
|
||||
# be committed; the Forgejo runner reads it from a repo secret.
|
||||
# ============================================================
|
||||
infra/ansible/.vault-pass
|
||||
infra/ansible/.vault-pass.*
|
||||
# Local copies devs sometimes drop next to the repo for editing
|
||||
.vault-pass
|
||||
.vault-pass.*
|
||||
|
||||
# ============================================================
|
||||
# Bootstrap scripts — local config + state stay out of git
|
||||
# ============================================================
|
||||
scripts/bootstrap/.env
|
||||
.git/talas-bootstrap/
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
title = "Veza gitleaks config"
|
||||
|
||||
# Inherit gitleaks v8 default ruleset
|
||||
[extend]
|
||||
useDefault = true
|
||||
|
||||
# Project-wide allowlist
|
||||
#
|
||||
# Categories of allowed paths (every entry below is a known false-positive
|
||||
# source confirmed by reading the file or its history):
|
||||
#
|
||||
# 1. Go test files — fake JWTs like eyJ...invalid_signature for auth-failure tests
|
||||
# 2. Historical .backup-pre-uuid-migration dir — gone from HEAD but in git history
|
||||
# 3. Playwright e2e artifacts — auth state snapshots, test result dumps
|
||||
# 4. Storybook stories + MSW mocks — UI fixtures with placeholder API keys
|
||||
# 5. Documentation — API examples, smoke test logs, integration guides
|
||||
# 6. K8s deployment templates — base64-encoded "secure_pass" placeholders
|
||||
# 7. Local dev TLS certs (CN=localhost) under docker/haproxy/certs/
|
||||
# 8. Rust/TS test fixtures — deterministic constants used only in #[cfg(test)]
|
||||
# 9. Generated bundle analysis HTML
|
||||
# 10. Legacy templates (apps/web/desy/legacy/)
|
||||
#
|
||||
# This allowlist intentionally errs on the side of letting things through.
|
||||
# Real secret rotation should rely on .env, vault, or k8s sealed-secrets.
|
||||
# When tightening, prefer adding a stopword over removing a path entry.
|
||||
[allowlist]
|
||||
description = "Allowlist test fixtures, docs, k8s templates, and dev artifacts"
|
||||
paths = [
|
||||
# Go tests
|
||||
'''.*_test\.go$''',
|
||||
'''.*\.backup-pre-uuid-migration/.*''',
|
||||
'''veza-backend-api/internal/services/\.backup-pre-uuid-migration/.*''',
|
||||
|
||||
# Playwright / e2e artifacts
|
||||
'''apps/web/e2e/\.auth/.*''',
|
||||
'''apps/web/e2e-results\.json$''',
|
||||
'''apps/web/full_test_result\.txt$''',
|
||||
'''apps/web/e2e/.*\.md$''',
|
||||
|
||||
# Storybook + MSW mocks
|
||||
'''apps/web/.*\.stories\.(ts|tsx|js|jsx)$''',
|
||||
'''apps/web/src/mocks/.*''',
|
||||
|
||||
# Documentation (markdown samples are inherently full of example tokens)
|
||||
'''.*\.md$''',
|
||||
|
||||
# K8s deployment templates with base64 placeholders
|
||||
'''.*/k8s/.*\.ya?ml$''',
|
||||
|
||||
# Local dev / self-signed TLS material
|
||||
'''docker/haproxy/certs/.*\.(pem|key|crt|csr)$''',
|
||||
|
||||
# Rust / TS test fixtures inside source files (constants used only in
|
||||
# #[cfg(test)] modules — see veza-stream-server/src/utils/signature.rs)
|
||||
'''veza-stream-server/src/utils/signature\.rs$''',
|
||||
'''veza-stream-server/src/utils/env\.rs$''',
|
||||
'''veza-chat-server/src/env\.rs$''',
|
||||
|
||||
# Legacy / static templates
|
||||
'''apps/web/desy/legacy/.*''',
|
||||
|
||||
# Pre-existing source files with hardcoded *test* keys (must stay until refactor)
|
||||
'''apps/web/src/components/studio/.*''',
|
||||
'''apps/web/src/components/settings/security/TwoFactorSetup\.tsx$''',
|
||||
'''apps/web/src/features/live/.*''',
|
||||
|
||||
# Generated artifacts
|
||||
'''\.build/.*\.html$''',
|
||||
]
|
||||
stopwords = [
|
||||
"invalid_signature",
|
||||
"test-jwt-secret",
|
||||
"test-secret",
|
||||
"test-internal-api-key",
|
||||
"test_secret_key_that_is_long_enough_32chars",
|
||||
"sk-abc123-def456-ghi789",
|
||||
"live_83921_abc123xyz789_secret_key",
|
||||
"secure_pass",
|
||||
]
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
npx --no -- commitlint --edit "$1"
|
||||
|
|
@ -1,34 +1,20 @@
|
|||
#!/usr/bin/env sh
|
||||
# Each step runs in a subshell so the cd does not leak across steps.
|
||||
# Pre-commit runs from the repo root; every cd below is relative to that.
|
||||
|
||||
# Drift guard: ensure apps/web/src/services/generated/ (orval) matches
|
||||
# veza-backend-api/openapi.yaml. Regenerates locally then fails if the
|
||||
# committed types don't match the freshly-regenerated output.
|
||||
# Skip with SKIP_TYPES=1 for emergency commits (documented in CLAUDE.md).
|
||||
if [ -z "$SKIP_TYPES" ]; then
|
||||
(cd apps/web && bash scripts/check-types-sync.sh) || {
|
||||
echo "❌ OpenAPI types are out of sync with veza-backend-api/openapi.yaml."
|
||||
echo "💡 Run: make openapi && cd apps/web && bash scripts/generate-types.sh"
|
||||
echo "💡 Then stage the updated src/services/generated/ and retry."
|
||||
echo "💡 Tip: SKIP_TYPES=1 bypasses (not recommended)."
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
# Generate TypeScript types from OpenAPI spec before commit
|
||||
# This ensures types are always up-to-date with the backend API
|
||||
cd apps/web && bash scripts/generate-types.sh
|
||||
|
||||
# Implicit 10.1: Type checking
|
||||
# Prevent commits with TypeScript errors (warnings are allowed)
|
||||
(cd apps/web && npm run typecheck 2>&1 | grep -q "error TS") && {
|
||||
cd apps/web && npm run typecheck 2>&1 | grep -q "error TS" && {
|
||||
echo "❌ Type checking failed. Please fix TypeScript errors before committing."
|
||||
echo "💡 Run 'npm run typecheck' to see all errors."
|
||||
exit 1
|
||||
} || true
|
||||
|
||||
# Implicit 10.2: Linting
|
||||
# Prevent commits with linting errors (warnings are allowed).
|
||||
# Pattern matches "(N error" with N>=1 in ESLint's summary line —
|
||||
# avoids false positive on "(0 errors, K warnings)".
|
||||
(cd apps/web && npm run lint 2>&1 | grep -qE "\([1-9][0-9]* error") && {
|
||||
# Prevent commits with linting errors (warnings are allowed)
|
||||
cd apps/web && npm run lint 2>&1 | grep -q "error" && {
|
||||
echo "❌ Linting failed. Please fix linting errors before committing."
|
||||
echo "💡 Tip: Run 'npm run lint:fix' to automatically fix some issues."
|
||||
exit 1
|
||||
|
|
@ -38,7 +24,7 @@ fi
|
|||
# Skip if SKIP_TESTS environment variable is set (for quick commits)
|
||||
# Only runs unit tests (not E2E) to keep it fast
|
||||
if [ -z "$SKIP_TESTS" ]; then
|
||||
(cd apps/web && npm test -- --run 2>&1 | grep -q "FAIL") && {
|
||||
cd apps/web && npm test -- --run 2>&1 | grep -q "FAIL" && {
|
||||
echo "❌ Tests failed. Please fix failing tests before committing."
|
||||
echo "💡 Tip: Run 'npm test' to see all test failures."
|
||||
echo "💡 Tip: Set SKIP_TESTS=1 to skip tests for this commit (not recommended)."
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# ============================================================================
|
||||
# Veza pre-push hook — CRITICAL E2E SMOKE
|
||||
# ============================================================================
|
||||
# Runs only @critical Playwright tests before push (~2-3min).
|
||||
# SKIP_E2E=1 git push ... # bypass for quick iterations
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
if [ -n "$SKIP_E2E" ]; then
|
||||
echo "${YELLOW}▶ SKIP_E2E=1 — skipping critical E2E smoke${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "${YELLOW}▶ Running critical E2E smoke tests (Playwright @critical)...${NC}"
|
||||
echo "${YELLOW} Set SKIP_E2E=1 to bypass (not recommended for shared branches)${NC}"
|
||||
|
||||
npm run e2e:critical 2>&1 || {
|
||||
echo "${RED}✗ Critical E2E tests failed — push blocked${NC}"
|
||||
echo "${YELLOW} Tip: run 'npm run e2e:critical' locally to debug${NC}"
|
||||
echo "${YELLOW} Tip: set SKIP_E2E=1 to bypass if you know what you're doing${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "${GREEN}✓ Critical E2E smoke passed — push allowed${NC}"
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* Lighthouse CI Configuration
|
||||
* v0.14.0 TASK-STAG-003: Validation Lighthouse
|
||||
*
|
||||
* Targets:
|
||||
* Performance >= 85
|
||||
* Accessibility >= 90
|
||||
* PWA >= 90 (best-practices proxy when PWA not applicable)
|
||||
* Best Practices >= 85
|
||||
* SEO >= 80
|
||||
*/
|
||||
module.exports = {
|
||||
ci: {
|
||||
collect: {
|
||||
url: [
|
||||
`${process.env.STAGING_URL || 'https://staging.veza.app'}/login`,
|
||||
`${process.env.STAGING_URL || 'https://staging.veza.app'}/register`,
|
||||
],
|
||||
numberOfRuns: 3,
|
||||
settings: {
|
||||
preset: 'desktop',
|
||||
// Throttling: simulate cable connection
|
||||
throttling: {
|
||||
cpuSlowdownMultiplier: 1,
|
||||
downloadThroughputKbps: 10240,
|
||||
uploadThroughputKbps: 5120,
|
||||
rttMs: 40,
|
||||
},
|
||||
// Skip audits that require auth
|
||||
skipAudits: [
|
||||
'uses-http2', // Depends on server config
|
||||
],
|
||||
},
|
||||
},
|
||||
assert: {
|
||||
assertions: {
|
||||
// Performance >= 85
|
||||
'categories:performance': ['error', { minScore: 0.85 }],
|
||||
// Accessibility >= 90
|
||||
'categories:accessibility': ['error', { minScore: 0.90 }],
|
||||
// Best Practices >= 85
|
||||
'categories:best-practices': ['warn', { minScore: 0.85 }],
|
||||
// SEO >= 80
|
||||
'categories:seo': ['warn', { minScore: 0.80 }],
|
||||
|
||||
// Core Web Vitals
|
||||
'first-contentful-paint': ['warn', { maxNumericValue: 1800 }],
|
||||
'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }],
|
||||
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
|
||||
'total-blocking-time': ['warn', { maxNumericValue: 300 }],
|
||||
|
||||
// Accessibility specifics (ORIGIN_UI_UX_SYSTEM compliance)
|
||||
'color-contrast': 'error',
|
||||
'image-alt': 'error',
|
||||
'label': 'error',
|
||||
'button-name': 'error',
|
||||
'link-name': 'error',
|
||||
'document-title': 'error',
|
||||
'html-has-lang': 'error',
|
||||
'meta-viewport': 'error',
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
target: 'filesystem',
|
||||
outputDir: '.lighthouseci',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"apps/web/**/*.{ts,tsx}": [
|
||||
"bash -c 'cd apps/web && npx eslint --max-warnings=0 --fix \"$@\"' --",
|
||||
"bash -c 'cd apps/web && npx tsc --noEmit -p tsconfig.json'"
|
||||
],
|
||||
"apps/web/**/*.{js,jsx,json,css,md}": ["prettier --write"],
|
||||
"veza-backend-api/**/*.go": [
|
||||
"bash -c 'cd veza-backend-api && gofmt -l -w \"$@\"' --",
|
||||
"bash -c 'cd veza-backend-api && go vet ./...'"
|
||||
],
|
||||
"veza-stream-server/**/*.rs": [
|
||||
"bash -c 'cd veza-stream-server && cargo fmt --'"
|
||||
],
|
||||
"*.{json,md,yml,yaml}": ["prettier --write"]
|
||||
}
|
||||
1
.nvmrc
1
.nvmrc
|
|
@ -1 +0,0 @@
|
|||
20
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"defaults": {
|
||||
"standard": "WCAG2AA",
|
||||
"timeout": 30000,
|
||||
"wait": 3000,
|
||||
"chromeLaunchConfig": {
|
||||
"args": ["--no-sandbox"]
|
||||
}
|
||||
},
|
||||
"urls": [
|
||||
"http://localhost:5174/login",
|
||||
"http://localhost:5174/register",
|
||||
"http://localhost:5174/discover"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
node_modules/
|
||||
.git/
|
||||
dist/
|
||||
storybook-static/
|
||||
coverage/
|
||||
*.test.ts
|
||||
*.test.tsx
|
||||
*.spec.ts
|
||||
*_test.go
|
||||
tests/
|
||||
loadtests/
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
10011 IGNORE (Cookie Without Secure Flag - dev only)
|
||||
10054 IGNORE (Cookie Without SameSite Attribute - dev only)
|
||||
|
370
103_RAPPORT_ETAT_FEATURES_2026_02_16.md
Normal file
370
103_RAPPORT_ETAT_FEATURES_2026_02_16.md
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
# Rapport d'état précis des features — Veza
|
||||
|
||||
**Date** : 16 février 2026
|
||||
**Méthode** : Analyse du code source (backend routes, frontend services, migrations DB, tests)
|
||||
|
||||
---
|
||||
|
||||
## 1. CARTOGRAPHIE GLOBALE — ÉTAT PRÉCIS
|
||||
|
||||
### 1.1 Stack
|
||||
|
||||
| Couche | Technologie | Version | Fichiers clés |
|
||||
|--------|-------------|---------|---------------|
|
||||
| Frontend | React + Vite | 18.2 / 7.1.5 | `apps/web/package.json` |
|
||||
| Backend | Go + Gin | 1.24 / 1.11 | `veza-backend-api/go.mod` |
|
||||
| Chat | Rust + Axum | 0.8 | `veza-chat-server/Cargo.toml` |
|
||||
| Stream | Rust + Axum | 0.8 | `veza-stream-server/Cargo.toml` |
|
||||
| DB | PostgreSQL | 16 | `docker-compose.prod.yml` |
|
||||
| Cache | Redis | 7 | idem |
|
||||
| Queue | RabbitMQ | 3 | idem |
|
||||
|
||||
### 1.2 Organisation du repo
|
||||
|
||||
- **apps/web** : Frontend React (features/, services/, mocks/)
|
||||
- **veza-backend-api** : API REST (router principal : `internal/api/router.go`)
|
||||
- **veza-chat-server** : WebSocket chat
|
||||
- **veza-stream-server** : Streaming audio
|
||||
- **veza-common** : Lib Rust partagée
|
||||
- **packages/** : NPM packages partagés
|
||||
|
||||
### 1.3 Point d'entrée API
|
||||
|
||||
Le routeur **actif** est `APIRouter` dans `internal/api/router.go`.
|
||||
Le fichier `api_manager.go` est **exclu de la compilation** (`//go:build ignore`) — tout ce qu'il contient (achievements, leaderboard, GraphQL, gRPC, etc.) est du **code mort**.
|
||||
|
||||
---
|
||||
|
||||
## 2. ÉTAT PRÉCIS DE CHAQUE FEATURE
|
||||
|
||||
### 2.1 Auth (register, login, JWT, refresh)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ Complet | `routes_auth.go` : register, login, refresh, logout, /me, 2FA, OAuth, password reset |
|
||||
| Frontend | ✅ Complet | `authStore`, `LoginForm`, `TwoFactorVerify`, `ProtectedRoute` |
|
||||
| DB | ✅ | Tables users, sessions, refresh_tokens, email_verification_tokens |
|
||||
| Tests | ✅ | `auth_handler_test.go`, `auth_integration_test.go`, `LoginForm.stories` |
|
||||
| Sécurité | ✅ | JWT iss/aud/exp, token version, bcrypt cost 12, rate limit login |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.2 2FA (TOTP)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `TwoFactorHandler` : setup, verify, disable, status |
|
||||
| Frontend | ✅ | `TwoFactorSetup.tsx`, `TwoFactorVerify.tsx` |
|
||||
| DB | ✅ | Colonnes two_factor_enabled, two_factor_secret, backup_codes |
|
||||
| Tests | ✅ | `two_factor_handler_test.go` |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.3 OAuth (Google, GitHub, Discord)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `OAuthHandler` : providers, initiate, callback |
|
||||
| Frontend | ✅ | Boutons OAuth, callback handling |
|
||||
| DB | ✅ | oauth_accounts, users |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Profils utilisateur
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_users.go` : GET/PUT/DELETE /users/:id, settings, avatar, follow, block |
|
||||
| Frontend | ✅ | `ProfileView`, `ProfilePage`, `useUser` |
|
||||
| DB | ✅ | users, user_profiles, user_settings |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Upload de tracks (chunked)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_tracks.go` : initiate, chunk, complete, resume, quota |
|
||||
| Frontend | ✅ | `trackService`, upload flow |
|
||||
| DB | ✅ | tracks, track_uploads |
|
||||
| Sécurité | ✅ | RequireContentCreatorRole, ClamAV optionnel |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.6 CRUD Tracks
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | GET/PUT/DELETE tracks, comments, likes, share, versions, play |
|
||||
| Frontend | ✅ | `trackService`, `LibraryPage`, `TrackDetailPage` |
|
||||
| DB | ✅ | tracks, track_comments, track_likes |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.7 Playlists (CRUD, collaboration)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_playlists.go` : CRUD, collaborators, tracks |
|
||||
| Frontend | ✅ | `playlistService`, `PlaylistDetailPage` |
|
||||
| DB | ✅ | playlists, playlist_collaborators, playlist_tracks |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.8 Chat WebSocket
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_chat.go` : POST /chat/token, GET /chat/stats |
|
||||
| Chat Server | ✅ | Rust, compile OK |
|
||||
| Frontend | ✅ | `ChatView`, WebSocket client |
|
||||
| DB | ✅ | chat_messages (Chat Server) |
|
||||
|
||||
**Verdict** : **Opérationnel** (Chat Server doit être démarré)
|
||||
|
||||
---
|
||||
|
||||
### 2.9 Dashboard
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_core.go:319` : GET /dashboard, `DashboardHandler` |
|
||||
| Frontend | ✅ | `dashboardService.getDashboardData()` → apiClient.get('/dashboard') |
|
||||
| MSW | ✅ | Mock dans `handlers-admin.ts` (fallback Storybook) |
|
||||
|
||||
**Note** : FEATURE_STATUS.md indiquait "MSW" — **faux**. Le backend expose bien `/api/v1/dashboard`.
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.10 Recherche
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `TrackSearchService`, endpoints search |
|
||||
| Frontend | ✅ | `SearchPage`, `searchService` |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.11 Social (feed, posts, groups, follows, blocks)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_social.go` : feed, posts, groups, like, comments, join/leave |
|
||||
| Frontend | ✅ | `SocialView`, `useSocialView` |
|
||||
| DB | ✅ | posts, social_groups, user_follows, user_blocks |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.12 Administration
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_core.go` : admin group, RequireAdmin |
|
||||
| Frontend | ✅ | `AdminDashboardPage`, `adminService` |
|
||||
| Audit | ✅ | audit/logs, audit/stats |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.13 Marketplace
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_marketplace.go` : products, cart, orders, licenses |
|
||||
| Frontend | ✅ | `MarketplacePage`, `Cart`, `PurchasesView` |
|
||||
| Paiement | ✅ | Hyperswitch intégré |
|
||||
| DB | ✅ | marketplace_products, orders, licenses |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.14 Webhooks
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_webhooks.go` : CRUD, regenerate-key, test, stats |
|
||||
| Frontend | ✅ | `webhookService.ts` (apiClient), `WebhooksView` |
|
||||
| DB | ✅ | webhooks |
|
||||
|
||||
**Note** : `webhookApi.ts` supprimé — remplacé par `webhookService.ts` qui appelle l'API directement.
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.15 Inventory / Gear
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_gear.go` : GET/POST/PUT/DELETE /inventory/gear |
|
||||
| Frontend | ✅ | `gearService.ts`, `GearView`, `GearPage` |
|
||||
| DB | ✅ | Migration 076 : `gear_items` |
|
||||
| MSW | ✅ | Mock dans `handlers-misc.ts` (Storybook) |
|
||||
|
||||
**Note** : FEATURE_STATUS.md indiquait "UI + mocks, pas de backend" — **faux**. Backend complet.
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.16 Live Streaming
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_live.go` : GET /live/streams, GET /live/streams/:id, POST (auth) |
|
||||
| Frontend | ✅ | `liveService.ts`, `LiveView`, `LivePage` |
|
||||
| DB | ✅ | Migration 077 : `live_streams` |
|
||||
| MSW | ✅ | Mock dans `handlers-misc.ts` |
|
||||
|
||||
**Note** : Le streaming vidéo réel (WebRTC/HLS) est géré par le Stream Server. Les routes backend gèrent les **métadonnées** des streams (titre, description, is_live).
|
||||
|
||||
**Verdict** : **Opérationnel** (métadonnées). Stream vidéo dépend du Stream Server.
|
||||
|
||||
---
|
||||
|
||||
### 2.17 Analytics
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_analytics.go` : tracks plays, top, dashboard |
|
||||
| Frontend | ✅ | `AnalyticsView`, `useAnalyticsView` |
|
||||
| DB | ✅ | track_plays, analytics events |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.18 Roles
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `setupRoleRoutes` : assign, revoke |
|
||||
| Frontend | ✅ | `AssignRoleModal`, `RolesPage` |
|
||||
| DB | ✅ | roles, user_roles |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.19 Notifications
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ✅ | `routes_core.go` : GET/POST/DELETE /api/v1/notifications, unread-count, read, read-all. Création auto pour follow, like, comment (Phase 2.2) |
|
||||
| Frontend | ✅ | `NotificationsPage`, `notificationService` |
|
||||
| DB | ✅ | Table `notifications` (migration 047) |
|
||||
|
||||
**Verdict** : **Opérationnel**
|
||||
|
||||
---
|
||||
|
||||
### 2.20 Gamification (achievements, leaderboard)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ❌ Code mort | `api_manager.go` (build ignore) : handleGetAchievements, handleGetLeaderboard |
|
||||
| Frontend | ⚠️ Composants | Storybook : AchievementCard, LeaderboardView, XPBar — pas de route /gamification |
|
||||
| MSW | ? | Handlers gamification possibles dans mocks |
|
||||
|
||||
**Verdict** : **Fantôme** — api_manager désactivé, pas de route active
|
||||
|
||||
---
|
||||
|
||||
### 2.21 Studio (Cloud File Browser)
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ❌ | Aucune route |
|
||||
| Frontend | ❌ | Dossier `features/studio/` **n'existe pas** (supprimé) |
|
||||
|
||||
**Verdict** : **Supprimé**
|
||||
|
||||
---
|
||||
|
||||
### 2.22 Education
|
||||
|
||||
| Aspect | État | Preuve |
|
||||
|--------|------|--------|
|
||||
| Backend | ❌ | Aucune route |
|
||||
| Frontend | ❌ | Dossier `features/education/` **n'existe pas** (supprimé) |
|
||||
|
||||
**Verdict** : **Supprimé**
|
||||
|
||||
---
|
||||
|
||||
## 3. RÉCAPITULATIF
|
||||
|
||||
### Features opérationnelles (19)
|
||||
|
||||
Auth, 2FA, OAuth, Profils, Upload tracks, CRUD tracks, Playlists, Chat, Dashboard, Recherche, Social, Admin, Marketplace, Webhooks, Gear, Live (métadonnées), Analytics, Roles, Notifications.
|
||||
|
||||
### Features partielles (0)
|
||||
|
||||
Aucune.
|
||||
|
||||
### Features fantômes (1)
|
||||
|
||||
Gamification — code dans api_manager (mort), composants Storybook.
|
||||
|
||||
### Features supprimées (2)
|
||||
|
||||
Studio, Education — dossiers supprimés.
|
||||
|
||||
---
|
||||
|
||||
## 4. INCOHÉRENCES DOCUMENTATION / CODE
|
||||
|
||||
| Document | Affirmation | Réalité |
|
||||
|----------|-------------|---------|
|
||||
| FEATURE_STATUS.md | Dashboard : MSW | Backend réel GET /dashboard |
|
||||
| FEATURE_STATUS.md | Inventory : pas de backend | Backend complet /inventory/gear |
|
||||
| FEATURE_STATUS.md | Live : contenu minimal | Backend complet /live/streams |
|
||||
| FEATURE_STATUS.md | Studio : UI seule | Dossier supprimé |
|
||||
| FEATURE_STATUS.md | Education : MSW | Dossier supprimé |
|
||||
|
||||
**Recommandation** : Mettre à jour `docs/FEATURE_STATUS.md` et `apps/web/docs/FEATURE_STATUS.md`.
|
||||
|
||||
---
|
||||
|
||||
## 5. FICHIERS CRITIQUES PAR FEATURE
|
||||
|
||||
| Feature | Backend | Frontend service | Route |
|
||||
|---------|---------|------------------|-------|
|
||||
| Auth | routes_auth.go | authStore | /auth/* |
|
||||
| Tracks | routes_tracks.go | trackService | /tracks/* |
|
||||
| Playlists | routes_playlists.go | playlistService | /playlists/* |
|
||||
| Chat | routes_chat.go | - | /chat/* |
|
||||
| Dashboard | routes_core.go | dashboardService | /dashboard |
|
||||
| Social | routes_social.go | - | /social/* |
|
||||
| Marketplace | routes_marketplace.go | - | /marketplace/* |
|
||||
| Webhooks | routes_webhooks.go | webhookService | /webhooks/* |
|
||||
| Gear | routes_gear.go | gearService | /inventory/gear |
|
||||
| Live | routes_live.go | liveService | /live/streams |
|
||||
| Analytics | routes_analytics.go | - | /analytics/* |
|
||||
| Roles | routes_users.go | - | /users/:id/roles |
|
||||
|
||||
---
|
||||
|
||||
*Rapport généré le 16 février 2026*
|
||||
737
103_audit_global_features_states.md
Normal file
737
103_audit_global_features_states.md
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
I now have all the data needed. Let me write the comprehensive audit report.
|
||||
|
||||
---
|
||||
|
||||
# 🔍 AUDIT COMPLET DU MONOREPO VEZA
|
||||
|
||||
**Date** : 16 février 2026
|
||||
**Auditeur** : Architecte IA senior
|
||||
**Scope** : Monorepo complet (`veza-backend-api`, `veza-chat-server`, `veza-stream-server`, `veza-common`, `apps/web`)
|
||||
|
||||
---
|
||||
|
||||
## PARTIE 1 — ÉTAT DE STABILITÉ
|
||||
|
||||
---
|
||||
|
||||
### 1.1 Santé du code
|
||||
|
||||
#### Go Backend (`veza-backend-api/`) ✅
|
||||
|
||||
| Critère | Statut | Détail |
|
||||
|---------|--------|--------|
|
||||
| Compilation (`go build ./...`) | ✅ Passe | 0 erreur, 0 warning |
|
||||
| Vet (`go vet ./...`) | ✅ Passe | 0 issue |
|
||||
| Imports cassés | ✅ Aucun | — |
|
||||
| `.env.template` | ✅ Documenté | Complet avec validation rules |
|
||||
| Secrets hardcodés | ✅ Aucun | Tous via env vars, masqués dans logs |
|
||||
|
||||
**TODOs/FIXMEs critiques (P1) — 7 items :**
|
||||
|
||||
| Fichier | Ligne | Description |
|
||||
|---------|-------|-------------|
|
||||
| `internal/core/track/handler.go` | ~340 | `TODO(P2-GO-004)`: `trackUploadService` attend `int64`, reçoit `uuid.UUID` — migration UUID incomplète |
|
||||
| `internal/core/track/handler.go` | ~355 | `TODO(P2-GO-004)`: même problème, `GetUploadProgress()` incompatible UUID |
|
||||
| `internal/repositories/playlist_collaborator_repository.go` | ~67 | `FIXME`: modèle `PlaylistCollaborator` doit utiliser UUID |
|
||||
| `internal/services/playlist_version_service.go` | ~73 | `FIXME`: `PlaylistVersion` ID types à vérifier |
|
||||
| `internal/services/track_history_service.go` | ~74 | `FIXME`: `TrackHistory` needs UUID migration |
|
||||
| `internal/services/playlist_service.go` | ~216 | `FIXME`: `PlaylistVersionService` needs UUID update |
|
||||
| `internal/handlers/auth_handler_test.go` | 225 | `FIXME`: test attend `StatusForbidden` mais l'implémentation permet login non-vérifié |
|
||||
|
||||
**TODOs P2 (18 items)** — les plus notables :
|
||||
|
||||
| Fichier | Description |
|
||||
|---------|-------------|
|
||||
| `internal/services/job_service.go` | Job queue non connectée (5 TODOs BE-SVC-003) — pas d'async processing |
|
||||
| `internal/database/database.go` | OAuth user lookup non implémenté (3 TODOs) |
|
||||
| `internal/handlers/oauth_handlers.go` | `frontendURL` fallback hardcodé `http://localhost:5173` |
|
||||
| `internal/config/middlewares_init.go:75` | Configuration CORS à améliorer |
|
||||
| `internal/api/admin/service.go` | Admin service partiellement implémenté (3 TODOs) |
|
||||
|
||||
#### Rust Chat Server (`veza-chat-server/`) ✅
|
||||
|
||||
| Critère | Statut | Détail |
|
||||
|---------|--------|--------|
|
||||
| Compilation (`cargo check`) | ✅ Passe | 0 erreur, 0 warning |
|
||||
| Protobuf | ✅ | Utilise fichiers pré-générés |
|
||||
| `.env.lab.example` | ⚠️ Minimal | Seul un template lab, pas de `.env.example` standard |
|
||||
|
||||
**TODOs (3 items) :**
|
||||
- `src/read_receipts.rs:230` — TODO: tracking "delivered" non implémenté
|
||||
- `src/presence.rs:226` — TODO: intégration push notifications (FCM, APNs)
|
||||
- `src/message_handler.rs:327` — TODO: recherche de salon par nom
|
||||
|
||||
#### Rust Stream Server (`veza-stream-server/`) ✅
|
||||
|
||||
| Critère | Statut | Détail |
|
||||
|---------|--------|--------|
|
||||
| Compilation (`cargo check`) | ✅ Passe | 0 erreur, 0 warning |
|
||||
| Protobuf | ✅ | Utilise fichiers pré-générés |
|
||||
| `.env.example` | ✅ Documenté | Variables bien documentées |
|
||||
| `#![allow(dead_code)]` | ⚠️ | Code mort autorisé dans `lib.rs` |
|
||||
|
||||
**Point critique** : le client gRPC vers le backend Go (`src/grpc/mod.rs`) est un **stub** — `attempt_send()` fait juste un `sleep`, il n'envoie rien réellement.
|
||||
|
||||
#### Rust Common (`veza-common/`) ✅
|
||||
|
||||
| Critère | Statut |
|
||||
|---------|--------|
|
||||
| Compilation | ✅ Passe |
|
||||
| TODOs | ✅ Aucun |
|
||||
|
||||
#### Frontend React (`apps/web/`) ✅
|
||||
|
||||
| Critère | Statut | Détail |
|
||||
|---------|--------|--------|
|
||||
| TypeScript (`tsc --noEmit`) | ✅ Passe | 0 erreur |
|
||||
| Build Vite | ✅ Passe | — |
|
||||
| `.env.example` | ✅ Documenté | Complet avec feature flags |
|
||||
|
||||
**TODOs notables :**
|
||||
- `src/services/analyticsService.ts:92-97` — endpoints analytics non implémentés côté backend, retournent des valeurs vides
|
||||
- `src/config/features.ts:50` — HLS endpoints marqués "NOT IMPLEMENTED"
|
||||
- `src/features/user/components/profile/ProfileSecurity.tsx:12` — "Placeholder for profile security"
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Points bloquants fonctionnels
|
||||
|
||||
| Module | Statut | Détail |
|
||||
|--------|--------|--------|
|
||||
| **Auth** | ✅ Fonctionnel | Register → verify email → login → refresh → logout → 2FA TOTP : flow complet. OAuth Google/GitHub opérationnel. Sessions management complet (list/revoke/logout-all). |
|
||||
| **Profils** | ✅ Fonctionnel | Création, édition, avatar upload, profil public (`/u/:username`), social links, paramètres. Toutes les routes connectées frontend ↔ backend. |
|
||||
| **Upload & Fichiers** | ⚠️ Partiel | Upload simple ✅, upload chunked ✅, validation MIME/taille ✅, métadonnées extraites ✅. **Manque** : transcoding async (job queue stub), HLS transcoding désactivé (feature flag `false`). |
|
||||
| **Streaming/Lecteur** | ⚠️ Partiel | Play/pause/seek/next/volume/shuffle/repeat ✅ via `<audio>` HTML5. Waveform visualizer ✅. Queue management ✅. **Manque** : HLS adaptive streaming désactivé, gRPC stream server est un stub, crossfade/gapless non implémentés. |
|
||||
| **Playlists** | ✅ Fonctionnel | CRUD complet ✅, ajout/retrait tracks ✅, réorganisation ✅, collaboration ✅, share links ✅, export JSON/CSV ✅, duplication ✅. |
|
||||
| **Chat** | ⚠️ Partiel | WebSocket connection ✅, envoi/réception messages ✅, conversations ✅, typing indicators ✅, reactions ✅. **Manque** : read receipts partiels (TODO), delivered status (TODO), recherche salon par nom (TODO). Communication avec Go backend via HTTP (pas gRPC). |
|
||||
| **Marketplace** | ✅ Fonctionnel | Création produit ✅, catalogue ✅, panier ✅, wishlist ✅, commandes ✅. Checkout via Hyperswitch (optionnel). Téléchargement post-achat ✅. |
|
||||
| **Recherche** | ✅ Fonctionnel | Recherche globale tracks/users/playlists ✅, autocomplete ✅. Filtres par type ✅. |
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Points bloquants techniques
|
||||
|
||||
#### Base de données ⚠️
|
||||
- **42 migrations** bien structurées, idempotentes, avec `IF NOT EXISTS`
|
||||
- **Migration UUID incomplète** : 6 FIXMEs dans le backend indiquent que certains services (`trackUploadService`, `PlaylistCollaborator`, `PlaylistVersion`, `TrackHistory`) utilisent encore `int64` au lieu de `uuid.UUID`. Cela compile (Go est permissif avec les conversions) mais peut causer des bugs runtime.
|
||||
- Pas de conflits de migrations détectés
|
||||
|
||||
#### API — Routes orphelines ⚠️
|
||||
**Backend non consommé par le frontend :**
|
||||
- `POST /api/v1/tracks/initiate` (chunked upload initiate) — frontend utilise directement `/tracks/chunk`
|
||||
- `POST /api/v1/tracks/complete` (chunked upload complete) — même remarque
|
||||
- `GET /api/v1/tracks/resume/:uploadId` — pas de UI de reprise d'upload
|
||||
- `POST /api/v1/tracks/batch/delete` et `POST /api/v1/tracks/batch/update` — pas de UI batch
|
||||
- `GET /api/v1/tracks/shared/:token` — pas de page de partage par token
|
||||
- `GET /api/v1/users/me/export` — endpoint existe, pas de bouton export dans l'UI
|
||||
- `POST /api/v1/audit/cleanup` — pas d'UI admin pour cleanup
|
||||
|
||||
**Frontend appelle des endpoints qui n'existent pas côté backend :**
|
||||
- `POST /api/v1/roles` (création de rôle) — le backend n'a que `GET /roles` et `GET /roles/:id`
|
||||
- `PUT /api/v1/roles/:id`, `DELETE /api/v1/roles/:id` — idem
|
||||
- `GET /api/v1/social/feed`, `POST /api/v1/social/posts` — pas de routes social dans le backend (uniquement follow/block)
|
||||
- `GET /api/v1/social/groups/*` — pas de routes groupes dans le backend
|
||||
- `GET /api/v1/inventory/gear/*` — pas de routes inventaire dans le backend
|
||||
- `GET /api/v1/live/streams/*` — pas de routes live dans le backend
|
||||
- `GET /api/v1/search` — le backend utilise `/tracks/search`, `/users/search`, pas un endpoint unifié `/search`
|
||||
|
||||
#### Sécurité ✅
|
||||
- JWT correctement validé via middleware auth
|
||||
- CORS configuré (origines spécifiques, pas de wildcard)
|
||||
- CSRF protection via middleware + tokens
|
||||
- Security headers complets (HSTS, CSP, X-Frame-Options, X-Content-Type-Options)
|
||||
- Rate limiting multi-couche (global, par endpoint, par utilisateur)
|
||||
- SQL injection protection (GORM parameterized queries)
|
||||
- Secret masking dans les logs
|
||||
- Aucun secret hardcodé en production (seuls des fallbacks dev dans le code)
|
||||
|
||||
#### Services Rust ⚠️
|
||||
- **Compilation** : ✅ Les deux compilent sans erreur
|
||||
- **Dépendances Cargo** : ✅ Résolues
|
||||
- **Communication avec Go** : 🔴 Le stream server utilise un **stub gRPC** — `attempt_send()` ne fait qu'un `sleep`. Le chat server communique via HTTP vers le backend Go (fonctionnel mais pas gRPC comme prévu).
|
||||
|
||||
#### Docker ✅
|
||||
- `docker-compose.yml` bien structuré : Postgres 16, Redis 7, RabbitMQ 3, backend-api, Hyperswitch (optionnel)
|
||||
- Health checks sur tous les services
|
||||
- Resource limits configurés
|
||||
- Ports isolés (15xxx/16xxx pour éviter les conflits)
|
||||
- Fichiers Dockerfile dev et production pour chaque service
|
||||
|
||||
#### Frontend — Tests ⚠️
|
||||
**Tests unitaires (Vitest)** :
|
||||
- **271/273 fichiers passent** (99.3%)
|
||||
- **3306/3318 tests passent** (99.6%)
|
||||
- **2 fichiers échouent** :
|
||||
1. `src/features/tracks/components/LikeButton.test.tsx` — 11 tests en échec : `aria-label` attend `"Retirer le like"` mais reçoit `"Retirer des favoris"` (problème de label i18n)
|
||||
2. `src/context/ToastContext.test.tsx` — 1 test en échec : `TypeError: (0, default) is not a function` dans `ToastProvider.tsx:40` (import cassé de `react-hot-toast`)
|
||||
|
||||
**Tests E2E (Playwright)** :
|
||||
- Dernière exécution : **36 tests échoués** (sur un nombre indéterminé — la dernière run a échoué en setup à cause d'un conflit de port 5173)
|
||||
- Configuration : 4 browsers (Chromium, Firefox, WebKit, Edge), 1 worker, timeout 60s
|
||||
|
||||
#### Logs & Observabilité ✅
|
||||
- Logging structuré : `zap` (Go), `tracing` (Rust)
|
||||
- Prometheus metrics sur tous les services
|
||||
- Sentry integration (Go backend, frontend)
|
||||
- Health checks : `/health`, `/healthz`, `/readyz`, `/api/v1/status`
|
||||
- Health check détaillé vérifie : DB, Redis, RabbitMQ, S3, chat server, stream server
|
||||
- Audit logs complets avec recherche
|
||||
|
||||
---
|
||||
|
||||
### 1.4 Synthèse stabilité
|
||||
|
||||
```
|
||||
PRIORITÉ CRITIQUE (bloque le lancement) :
|
||||
1. gRPC Stream Server stub — Le stream server ne communique pas réellement avec
|
||||
le backend Go, la chaîne upload→transcode→stream est cassée.
|
||||
Fichier: veza-stream-server/src/grpc/mod.rs
|
||||
Effort: 8h
|
||||
|
||||
2. Routes API frontend ↔ backend désalignées — Le frontend appelle des endpoints
|
||||
inexistants (/social/feed, /social/groups, /inventory/gear, /live/streams, /search).
|
||||
Ces pages fonctionnent uniquement grâce aux mocks MSW.
|
||||
Fichiers: apps/web/src/services/socialService.ts, gearService.ts, liveService.ts, searchService.ts
|
||||
Effort: 16h (créer les routes backend) ou 4h (retirer les pages du routeur)
|
||||
|
||||
3. Job Queue non connectée — Les tâches async (transcoding, email, thumbnails) ne
|
||||
s'exécutent pas en background. Le service existe mais est un shell vide.
|
||||
Fichier: veza-backend-api/internal/services/job_service.go
|
||||
Effort: 8h
|
||||
|
||||
PRIORITÉ HAUTE (dégrade l'expérience) :
|
||||
1. Migration UUID incomplète — 6 services utilisent encore int64, risque de bugs
|
||||
runtime sur upload progress, playlist collaborators, track history.
|
||||
Fichiers: internal/core/track/handler.go:340, internal/services/playlist_*.go,
|
||||
internal/repositories/playlist_collaborator_repository.go
|
||||
Effort: 6h
|
||||
|
||||
2. HLS Streaming désactivé — Le lecteur audio ne supporte que le playback direct
|
||||
(pas d'adaptive bitrate). Feature flag HLS_STREAMING=false.
|
||||
Fichiers: apps/web/src/config/features.ts, veza-stream-server/
|
||||
Effort: 12h
|
||||
|
||||
3. Tests LikeButton et ToastContext cassés — 12 tests unitaires échouent.
|
||||
Fichiers: apps/web/src/features/tracks/components/LikeButton.test.tsx,
|
||||
apps/web/src/context/ToastContext.test.tsx
|
||||
Effort: 1h
|
||||
|
||||
4. Tests E2E non fiables — 36 échecs, configuration port conflict.
|
||||
Fichier: apps/web/playwright.config.ts (reuseExistingServer: false)
|
||||
Effort: 4h
|
||||
|
||||
PRIORITÉ MOYENNE (acceptable pour un PoC) :
|
||||
1. Chat read receipts et delivered status — TODOs non implémentés
|
||||
Fichiers: veza-chat-server/src/read_receipts.rs, src/delivered_status.rs
|
||||
Effort: 4h
|
||||
|
||||
2. OAuth Discord/Spotify non implémentés — Seuls Google et GitHub fonctionnent
|
||||
Fichiers: veza-backend-api/internal/handlers/oauth_handlers.go
|
||||
Effort: 4h par provider
|
||||
|
||||
3. Admin service partiellement implémenté (3 TODOs)
|
||||
Fichier: veza-backend-api/internal/api/admin/service.go
|
||||
Effort: 4h
|
||||
|
||||
4. Analytics backend partiellement stub — Certains endpoints retournent des données vides
|
||||
Fichier: apps/web/src/services/analyticsService.ts:92-97
|
||||
Effort: 6h
|
||||
|
||||
5. Studio et Education supprimés — Features planifiées mais code retiré
|
||||
Impact: Aucun pour le PoC (Tier 2)
|
||||
Effort: 0h (décision produit)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PARTIE 2 — PROGRESSION VERS L'OBJECTIF FINAL (600 FEATURES)
|
||||
|
||||
---
|
||||
|
||||
### 2.1 Matrice de couverture par module
|
||||
|
||||
> **Note** : Le document TIER 0 mentionne "40 features" mais les ranges listées (`1-10, 31-45, 66-90, 106-135, 151-175, 186-200, 226-250, 351-365, 411-425, 436-450`) contiennent en réalité **190 features**. J'utilise les ranges comme référence.
|
||||
|
||||
---
|
||||
|
||||
## Module 1 : Auth & Sécurité — 18/30 features (60%)
|
||||
|
||||
### Implémentées ✅ (backend + frontend connectés) :
|
||||
- #1 : Inscription email/password ✅
|
||||
- #2 : Validation email ✅
|
||||
- #3 : Connexion email/password ✅
|
||||
- #4 : OAuth Google ✅
|
||||
- #5 : OAuth GitHub ✅
|
||||
- #9 : Logout ✅
|
||||
- #10 : Logout all devices ✅
|
||||
- #11 : Reset password par email ✅
|
||||
- #17 : Blocage après tentatives (rate limiting) ✅
|
||||
- #19 : 2FA TOTP ✅
|
||||
- #23 : Session management ✅
|
||||
- #28 : Rate limiting connexion ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #8 : Remember me ⚠️ — Cookies persistent mais pas de checkbox UI explicite
|
||||
- #12 : Changement password (authentifié) ⚠️ — Endpoint frontend existe, backend probablement aussi
|
||||
- #14 : Force du mot de passe ⚠️ — Validation Zod côté frontend, indicateur visuel partiel
|
||||
- #21 : Codes backup 2FA ⚠️ — Modèle `recovery_code.go` existe, UI incomplète
|
||||
- #26 : Historique connexions ⚠️ — Via audit logs, pas de page dédiée
|
||||
- #30 : Détection bruteforce ⚠️ — Via rate limiting, pas de détection spécifique
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #6 : OAuth Discord ❌
|
||||
- #7 : OAuth Spotify ❌
|
||||
- #13 : Historique passwords ❌
|
||||
- #15 : Politique passwords configurable ❌
|
||||
- #16 : Expiration password ❌
|
||||
- #18 : Notification changement password ❌
|
||||
- #20 : 2FA SMS ❌
|
||||
- #22 : Passkeys/WebAuthn ❌
|
||||
- #24 : Notifications connexion inhabituelle ❌
|
||||
- #25 : Géolocalisation connexions ❌
|
||||
- #27 : IP whitelisting ❌
|
||||
- #29 : CAPTCHA ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 2 : Profils & Utilisateurs — 18/35 features (51%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #31 : Avatar upload ✅
|
||||
- #33 : Username unique ✅
|
||||
- #34 : Nom complet ✅
|
||||
- #35 : Bio/description ✅
|
||||
- #39 : Langue préférée ✅
|
||||
- #41 : URL profil (/u/username) ✅
|
||||
- #44 : Liens réseaux sociaux ✅
|
||||
- #46 : Rôle User ✅
|
||||
- #47 : Rôle Artist ✅
|
||||
- #51 : Rôle Modérateur ✅
|
||||
- #52 : Rôle Admin ✅
|
||||
- #53 : Permissions granulaires ✅
|
||||
- #58 : Changement langue UI ✅
|
||||
- #59 : Thème clair/sombre/auto ✅
|
||||
- #65 : Supprimer compte (GDPR) ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #32 : Bannière profil ⚠️ — Modèle existe probablement, pas de route dédiée
|
||||
- #36 : Localisation ⚠️ — Champ probable dans user model
|
||||
- #42 : Profil public/privé ⚠️ — Paramètres de confidentialité existent
|
||||
- #56 : Changer email ⚠️ — Endpoint probable
|
||||
- #57 : Changer username ⚠️ — Via PUT /users/:id
|
||||
- #60-62 : Notifications on/off ⚠️ — Paramètres existent, implémentation partielle
|
||||
- #63-64 : Préférences confidentialité/visibilité ⚠️ — Settings partiels
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #37 : Date de naissance ❌
|
||||
- #38 : Genre ❌
|
||||
- #40 : Fuseau horaire ❌
|
||||
- #43 : Email contact public ❌
|
||||
- #45 : Badges/achievements ❌
|
||||
- #48 : Rôle Producer ❌ (distinct d'Artist)
|
||||
- #49 : Rôle Label ❌
|
||||
- #50 : Rôle Formateur ❌
|
||||
- #54 : Système vérification (badge vérifié) ❌
|
||||
- #55 : KYC ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 3 : Gestion de Fichiers — 14/40 features (35%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #66 : Upload fichier unique ✅
|
||||
- #67 : Upload multiple (batch) ✅
|
||||
- #71 : Progress bar upload ✅
|
||||
- #73 : Validation taille ✅
|
||||
- #74 : Validation type MIME ✅
|
||||
- #79 : Extraction métadonnées ✅
|
||||
- #81-86 : Formats MP3, WAV, FLAC, OGG, AIFF, M4A ✅
|
||||
- #91-94 : Titre, Artiste, Album, Genre ✅
|
||||
- #97 : Durée ✅
|
||||
- #103 : Cover art upload ✅
|
||||
- #104 : Tags personnalisés ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #68 : Drag & drop ⚠️ — Probable via composant upload
|
||||
- #72 : Pause/resume upload ⚠️ — Chunked upload existe mais UI incomplète
|
||||
- #77 : Transcoding auto ⚠️ — Job queue stub, transcoding pipeline Rust existe mais non connecté
|
||||
- #95 : BPM ⚠️ — Modèle existe, extraction auto incertaine
|
||||
- #96 : Key musicale ⚠️ — Idem
|
||||
- #98 : Date de sortie ⚠️ — Champ métadonnée probable
|
||||
- #105 : Tags suggérés ⚠️ — Autocomplete partiel
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #69 : Upload par URL ❌
|
||||
- #70 : Upload depuis cloud (Dropbox/Drive) ❌
|
||||
- #75 : Scan antivirus ❌ (ClamAV configuré mais `ENABLE_CLAMAV=false`)
|
||||
- #76 : Compression auto images ❌
|
||||
- #78 : Thumbnails auto ❌ (job queue stub)
|
||||
- #80 : Watermarking ❌
|
||||
- #87-88 : Archives ZIP/RAR ❌
|
||||
- #89 : Documents PDF ❌
|
||||
- #90 : Presets VST ❌
|
||||
- #99-102 : Label, ISRC, Copyright, Lyrics ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 4 : Streaming Audio — 16/45 features (36%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #106 : Play/pause ✅
|
||||
- #107 : Next track ✅
|
||||
- #108 : Previous track ✅
|
||||
- #109 : Seek ✅
|
||||
- #110 : Volume control ✅
|
||||
- #111 : Mute/unmute ✅
|
||||
- #112 : Shuffle ✅
|
||||
- #113 : Repeat (off/track/playlist) ✅
|
||||
- #117 : Waveform visualizer ✅
|
||||
- #122 : Raccourcis clavier ✅ (Media Session API)
|
||||
- #126 : Queue management ✅
|
||||
- #127 : Ajouter à la queue ✅
|
||||
- #128 : Retirer de la queue ✅
|
||||
- #131 : Vider la queue ✅
|
||||
- #136 : Créer playlist ✅
|
||||
- #137 : Éditer playlist ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #120 : Mini-player ⚠️ — Lecteur bottom-bar existe
|
||||
- #123 : Media Session API ⚠️ — Probable via composant player
|
||||
- #129 : Réorganiser queue ⚠️ — Store support, UI incertaine
|
||||
- #132 : Historique écoute ⚠️ — Backend endpoint existe, UI partielle
|
||||
- #133 : Reprendre où on s'est arrêté ⚠️ — playerStore persiste avec zustand persist
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #114 : Playback speed ❌
|
||||
- #115 : Crossfade ❌
|
||||
- #116 : Gapless playback ❌
|
||||
- #118 : Spectrogram ❌
|
||||
- #119 : Bars visualizer ❌
|
||||
- #121 : Picture-in-picture ❌
|
||||
- #124 : Chromecast ❌
|
||||
- #125 : AirPlay ❌
|
||||
- #130 : Sauvegarder queue comme playlist ❌
|
||||
- #134 : Queue collaborative ❌
|
||||
- #135 : Autoplay recommandations ❌
|
||||
- #138-150 : Playlists CRUD suite (la plupart implémentées — voir Playlists ci-dessus)
|
||||
|
||||
> **Correction Playlists** : Features 136-150 sont dans Module 4 mais le CRUD playlist est complet. En réalité : #136-142 ✅, #143 ✅ (collaboration), #144 ⚠️ (cover custom), #145 ✅ (description), #146 ✅ (partage), #147 ✅ (duplication), #148 ❌ (fusion), #149 ✅ (export), #150 ❌ (playlists intelligentes).
|
||||
|
||||
---
|
||||
|
||||
## Module 5 : Chat & Messagerie — 14/35 features (40%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #151 : DM 1-to-1 ✅
|
||||
- #152 : Salons publics ✅
|
||||
- #153 : Salons privés ✅
|
||||
- #154 : Messages de groupe ✅
|
||||
- #155 : Messages texte ✅
|
||||
- #157 : Réactions emoji ✅
|
||||
- #158 : Édition messages ✅
|
||||
- #159 : Suppression messages ✅
|
||||
- #170 : Notifications temps réel ✅
|
||||
- #173 : Badge non lus ✅
|
||||
- #174 : Typing indicator ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #156 : Emojis ⚠️ — Texte emoji OK, pas de picker dédié
|
||||
- #160 : Threads/réponses ⚠️ — Infrastructure existe dans le hub Rust
|
||||
- #175 : Read receipts ⚠️ — Modèle existe, TODO dans le code
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #161-165 : Mentions, Markdown, images, GIFs, partage tracks ❌
|
||||
- #166-169 : Recherche historique, filtres, pin, bookmarks ❌
|
||||
- #171-172 : Push notifications, son personnalisable ❌
|
||||
- #176-185 : Présence & statuts (en ligne, occupé, custom, AFK, last seen, etc.) ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 6 : Social & Communauté — 7/40 features (18%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #186 : Follow ✅
|
||||
- #187 : Unfollow ✅
|
||||
- #188 : Liste followers ✅ (endpoint existe)
|
||||
- #189 : Liste following ✅
|
||||
- #190 : Bloquer ✅
|
||||
- #191 : Signaler ⚠️ (modération backend, pas de bouton frontend dédié)
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #196 : Partage profil ⚠️ — URL `/u/:username` existe
|
||||
- #198 : Notifications followers ⚠️ — Notifications système existe
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #192-195, 197, 199-200 : Recommandations, suggestions, collaboration, referral, QR code, close friends, abonnements ❌
|
||||
- #201-225 : Mur & publications, groupes & communautés ❌ — Le frontend a des composants Social mais ils appellent des endpoints qui **n'existent pas** dans le backend (uniquement MSW mocks)
|
||||
|
||||
---
|
||||
|
||||
## Module 7 : Marketplace — 16/50 features (32%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #226 : Créer produit ✅
|
||||
- #227 : Éditer produit ✅
|
||||
- #228 : Supprimer produit ✅
|
||||
- #229 : Upload fichiers produit ✅
|
||||
- #233 : Prix fixe ✅
|
||||
- #236 : Catégories ✅
|
||||
- #237 : Tags ✅
|
||||
- #251 : Ajouter au panier ✅
|
||||
- #252 : Panier multi-produits ✅
|
||||
- #253 : Wishlist ✅
|
||||
- #261 : Historique achats ✅
|
||||
- #262 : Re-téléchargement ✅
|
||||
- #266 : Dashboard vendeur ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #230 : Preview/démo ⚠️ — Upload existe, player intégré incertain
|
||||
- #232 : Description rich text ⚠️
|
||||
- #256 : Checkout (Hyperswitch) ⚠️ — Infrastructure existe, optionnel
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #231, 234-235, 238-250, 254-260, 263-275 : Images multi, prix variable, gratuit, BPM/Key, formats, licences complètes, paiements avancés, factures, remboursements, revenus temps réel, reviews, promotions, payout ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 8 : Formation & Éducation — 0/30 features (0%)
|
||||
|
||||
❌ **Entièrement non implémenté**. Le répertoire `src/features/education/` a été supprimé. Aucun code backend ne supporte ce module.
|
||||
|
||||
---
|
||||
|
||||
## Module 9 : Gestion de Matériel — 0/25 features (0%)
|
||||
|
||||
⚠️ Le frontend a des composants via MSW mocks (`/api/v1/inventory/gear`), mais **aucun endpoint backend n'existe**. Code frontend-only, non fonctionnel sans mocks.
|
||||
|
||||
---
|
||||
|
||||
## Module 10 : Cloud & Stockage — 0/20 features (0%)
|
||||
|
||||
❌ **Entièrement non implémenté**. Aucune intégration Nextcloud ou backup.
|
||||
|
||||
---
|
||||
|
||||
## Module 11 : Recherche & Découverte — 6/30 features (20%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #351 : Recherche fulltext ✅
|
||||
- #353 : Recherche tracks ✅
|
||||
- #357 : Recherche utilisateurs ✅
|
||||
- #356 : Recherche playlists ✅
|
||||
- #360 : Autocomplete suggestions ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #352 : Recherche par catégorie ⚠️ — Filtres existent
|
||||
- #373 : Tri par pertinence ⚠️
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #354-355, 358-359, 361-380 : Albums, groupes, cours, phonétique, correction ortho, booléen, historique, recherches sauvées, filtres avancés (BPM, key, durée), recommandations ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 12 : Analytics & Statistiques — 5/30 features (17%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #381 : Dashboard analytics ✅
|
||||
- #383 : Plays par track ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #382 : Statistiques écoute globales ⚠️ — Endpoints partiels, certains retournent des données vides
|
||||
- #393 : Engagement (likes, comments, shares) ⚠️
|
||||
- #406 : Utilisateurs actifs (admin) ⚠️ — Admin dashboard partiel
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #384-392, 394-405, 407-410 : Plays par période, durée moyenne, skip rate, géographie, démographie, devices, sources trafic, peaks, export, revenus, conversions, projections ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 13 : Administration — 8/25 features (32%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #411 : Liste utilisateurs ✅
|
||||
- #412 : Recherche utilisateurs ✅
|
||||
- #418 : Changement de rôle ✅
|
||||
- #419 : Historique actions admin ✅ (audit logs)
|
||||
- #431 : Paramètres généraux ⚠️ (partiel)
|
||||
- #433 : Feature flags ✅
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #413 : Filtres avancés ⚠️
|
||||
- #432 : Limites upload/storage ⚠️ — Configurable via env
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #414-417, 420-430, 434-435 : Édition profil admin, ban, suspension, reset password, notes internes, modération contenu, copyright, appeal, maintenance mode, annonces ❌
|
||||
|
||||
---
|
||||
|
||||
## Module 14 : UX/UI — 8/20 features (40%)
|
||||
|
||||
### Implémentées ✅ :
|
||||
- #436 : Thème clair ✅
|
||||
- #437 : Thème sombre ✅
|
||||
- #438 : Thème auto ✅
|
||||
- #446 : Navigation clavier ✅
|
||||
- #448 : ARIA labels ✅ (partiellement — l'erreur LikeButton montre une incohérence)
|
||||
- #449 : Focus visible ✅
|
||||
- #452 : Réduction animations ✅ (prefers-reduced-motion supporté par Framer Motion)
|
||||
|
||||
### Partiellement implémentées ⚠️ :
|
||||
- #450 : Contraste WCAG AA ⚠️ — Design system existe, conformité non auditée
|
||||
|
||||
### Non implémentées ❌ :
|
||||
- #439-445, 447, 451, 453-455 : Contraste élevé, mode compact/confortable, couleurs custom, layouts custom, screen reader complet, tailles police, transcriptions, sous-titres, dyslexie ❌
|
||||
|
||||
---
|
||||
|
||||
## Modules 15-21 : Fonctionnalités Avancées 🔮
|
||||
|
||||
| Module | Features | Implémenté | Statut |
|
||||
|--------|----------|------------|--------|
|
||||
| 15. IA & Avancé | 45 | 0 | 🔮 Futur — Aucun code |
|
||||
| 16. Intégrations | 20 | 0 | 🔮 Futur — Aucun code |
|
||||
| 17. Apps Natives | 15 | 0 | 🔮 Futur — veza-mobile abandonné |
|
||||
| 18. Gamification | 15 | 0 | 🔮 Futur — MSW mocks uniquement |
|
||||
| 19. Notifications | 20 | 5 ⚠️ | ⚠️ Notifications in-app partielles (#551-555) |
|
||||
| 20. Sécurité Avancée | 15 | 10 ✅ | ✅ Rate limiting, CSRF, XSS, CSP, HSTS, security headers (#571-580), audit logs (#581) |
|
||||
| 21. Développeurs & API | 15 | 4 ⚠️ | ⚠️ API REST partielle (#586), Swagger (#591), Webhooks (#595), Developer dashboard UI only (#600) |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Tableau récapitulatif
|
||||
|
||||
| Module | Total | ✅ Done | ⚠️ Partiel | ❌ Missing | 🔮 Future | % Done |
|
||||
|-------------------------------|-------|---------|------------|-----------|-----------|--------|
|
||||
| 1. Auth & Sécurité | 30 | 12 | 6 | 12 | 0 | 40% |
|
||||
| 2. Profils & Utilisateurs | 35 | 15 | 7 | 13 | 0 | 43% |
|
||||
| 3. Gestion de Fichiers | 40 | 14 | 7 | 19 | 0 | 35% |
|
||||
| 4. Streaming Audio | 45 | 24 | 5 | 16 | 0 | 53% |
|
||||
| 5. Chat & Messagerie | 35 | 11 | 3 | 21 | 0 | 31% |
|
||||
| 6. Social & Communauté | 40 | 5 | 2 | 33 | 0 | 13% |
|
||||
| 7. Marketplace | 50 | 13 | 3 | 34 | 0 | 26% |
|
||||
| 8. Formation & Éducation | 30 | 0 | 0 | 0 | 30 | 0% |
|
||||
| 9. Gestion Matériel | 25 | 0 | 0 | 0 | 25 | 0% |
|
||||
| 10. Cloud & Stockage | 20 | 0 | 0 | 0 | 20 | 0% |
|
||||
| 11. Recherche & Découverte | 30 | 5 | 2 | 23 | 0 | 17% |
|
||||
| 12. Analytics & Statistiques | 30 | 2 | 3 | 25 | 0 | 7% |
|
||||
| 13. Administration | 25 | 6 | 2 | 17 | 0 | 24% |
|
||||
| 14. UX/UI | 20 | 7 | 1 | 12 | 0 | 35% |
|
||||
| 15. Fonctionnalités Avancées | 45 | 0 | 0 | 0 | 45 | 0% |
|
||||
| 16. Intégrations Externes | 20 | 0 | 0 | 0 | 20 | 0% |
|
||||
| 17. Applications Natives | 15 | 0 | 0 | 0 | 15 | 0% |
|
||||
| 18. Gamification | 15 | 0 | 0 | 0 | 15 | 0% |
|
||||
| 19. Notifications | 20 | 3 | 2 | 5 | 10 | 15% |
|
||||
| 20. Sécurité Avancée | 15 | 10 | 1 | 0 | 4 | 67% |
|
||||
| 21. Développeurs & API | 15 | 2 | 2 | 6 | 5 | 13% |
|
||||
| **TOTAL** | **600** | **129** | **46** | **236** | **189** | **21.5%** |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Écart par rapport aux tiers de priorité
|
||||
|
||||
#### TIER 0 (V1 Launch — ranges 1-10, 31-45, 66-90, 106-135, 151-175, 186-200, 226-250, 351-365, 411-425, 436-450 = ~190 features)
|
||||
|
||||
| Sous-range | Total | ✅ | ⚠️ | ❌ | % |
|
||||
|------------|-------|-----|------|------|-----|
|
||||
| Auth 1-10 | 10 | 7 | 1 | 2 | 70% |
|
||||
| Profils 31-45 | 15 | 10 | 3 | 2 | 67% |
|
||||
| Fichiers 66-90 | 25 | 10 | 3 | 12 | 40% |
|
||||
| Streaming 106-135 | 30 | 14 | 4 | 12 | 47% |
|
||||
| Chat 151-175 | 25 | 11 | 3 | 11 | 44% |
|
||||
| Social 186-200 | 15 | 5 | 2 | 8 | 33% |
|
||||
| Marketplace 226-250 | 25 | 10 | 2 | 13 | 40% |
|
||||
| Recherche 351-365 | 15 | 5 | 2 | 8 | 33% |
|
||||
| Admin 411-425 | 15 | 4 | 1 | 10 | 27% |
|
||||
| UX/UI 436-450 | 15 | 7 | 1 | 7 | 47% |
|
||||
| **TOTAL TIER 0** | **190** | **83** | **22** | **85** | **44%** |
|
||||
|
||||
**Estimation effort pour finir TIER 0** : ~85 features manquantes dont beaucoup sont mineures (champs de formulaire, filtres). Estimation réaliste : **200-300h de développement** (6-10 semaines à temps plein).
|
||||
|
||||
#### TIER 1 (V2-V5 — ranges 11-30, 46-65, 91-105, 136-150, 176-185, 201-225, 251-275, 276-305, 306-330, 366-410 = ~230 features)
|
||||
|
||||
- **Déjà commencées** : ~36 features (2FA #19-21, rôles #46-53, playlists avancées #136-150 partiellement, rate limiting #28)
|
||||
- Beaucoup de features TIER 1 sont déjà partiellement en place grâce au backend riche
|
||||
|
||||
#### TIER 2 (V6-V12 — features 426-435, 451-600 = ~160 features + modules 8-10 = ~75 = ~235 features)
|
||||
|
||||
- **Code anticipatoire** : Infrastructure Kubernetes complète (k8s/), monitoring Prometheus/Grafana, load testing scripts, security scanning CI — l'infra est surdimensionnée par rapport au code applicatif.
|
||||
- Le modèle `live_stream.go` et les composants Live frontend anticipent le livestreaming (#471-480)
|
||||
- Les modèles `gear.go`, `hardware.go` anticipent l'inventaire (#306-330)
|
||||
- Les modèles `contest.go`, `royalty.go` anticipent la gamification et les royalties
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Recommandations stratégiques
|
||||
|
||||
#### 1. Les 5 actions les plus impactantes pour la stabilité
|
||||
|
||||
1. **Connecter le stream server gRPC au backend Go** (8h) — Sans ça, la chaîne audio est cassée pour le transcoding et les callbacks. Le stream server fonctionne en isolation mais ne communique pas les résultats au backend.
|
||||
|
||||
2. **Aligner les routes API social/search/inventory/live** (16h) — Soit créer les endpoints manquants côté Go, soit retirer les pages fantômes du frontend. 4 modules entiers sont en mode "MSW-only".
|
||||
|
||||
3. **Connecter la job queue** (8h) — Intégrer `asynq` ou un système similaire pour le transcoding async, les emails, et les thumbnails. Le service est un shell vide.
|
||||
|
||||
4. **Finaliser la migration UUID** (6h) — 6 FIXMEs dans le backend risquent des bugs runtime sur les opérations d'upload, collaborateurs de playlist, et historique.
|
||||
|
||||
5. **Fixer les 12 tests unitaires cassés et stabiliser les E2E** (5h) — Le LikeButton a un label i18n incorrect, ToastContext a un import cassé, et Playwright a un conflit de port.
|
||||
|
||||
#### 2. Choix architecturaux problématiques à l'échelle
|
||||
|
||||
- **Stream server gRPC stub** : L'architecture prévoit gRPC pour la communication inter-services, mais les deux implémentations (chat HTTP, stream stub) ne l'utilisent pas vraiment. Cela crée une incohérence architecturale. **Risque** : si le trafic augmente, la communication HTTP entre services ne passera pas à l'échelle aussi bien que gRPC.
|
||||
|
||||
- **Double source de vérité pour les services API** : Le frontend a des services à deux endroits (`src/services/*.ts` et `src/features/*/services/*.ts`). Certains endpoints sont appelés depuis les deux. **Risque** : maintenance difficile, bugs de désynchro.
|
||||
|
||||
- **Hyperswitch comme payment router** : Choix ambitieux (open-source, multi-provider) mais complexe à opérer. Pour un PoC, Stripe direct serait plus simple. **Risque** : overhead opérationnel important.
|
||||
|
||||
- **42 migrations SQL sans outil de migration formel** : Les migrations sont des fichiers SQL bruts. Pas de `migrate` CLI ou de tracking automatique des versions appliquées. **Risque** : conflits et migrations manquées en production.
|
||||
|
||||
#### 3. Modules surdéveloppés par rapport à leur priorité
|
||||
|
||||
- **Infrastructure Kubernetes** (`k8s/`) : Déploiements, HPA/VPA, monitoring Prometheus/Grafana/Loki, CDN (CloudFront, Cloudflare), certificats Let's Encrypt, network policies, backup cronjobs — tout ça pour un PoC qui n'a pas encore de version stable. **Surdéveloppé** par rapport à l'état du code applicatif.
|
||||
|
||||
- **Sécurité avancée (Module 20)** : 67% complété alors que le social (13%), l'analytics (7%), et la recherche (17%) sont très en retard. Le rate limiting multi-couche et les security headers sont parfaits mais disproportionnés pour un PoC.
|
||||
|
||||
- **CI/CD** (9 workflows GitHub Actions) : Pipeline complet avec vulnerability scans, SBOM, image signing, smoke tests post-deploy — excellent mais prématuré avant la stabilité fonctionnelle.
|
||||
|
||||
#### 4. Modules sous-développés critiques pour le PoC
|
||||
|
||||
- **Social & Communauté (13%)** : Pour une plateforme collaborative musicale, le social est le coeur du produit. Les features de feed, posts, groupes n'existent qu'en mocks MSW sans backend.
|
||||
|
||||
- **Recherche & Découverte (17%)** : La recherche est basique (fulltext sur tracks/users). Aucun filtre par BPM/key/genre — fonctionnalités critiques pour des musiciens.
|
||||
|
||||
- **Analytics (7%)** : Les créateurs ont besoin de voir leurs stats d'écoute. Le dashboard renvoie des données vides sur plusieurs endpoints.
|
||||
|
||||
#### 5. Estimation réaliste pour v0.101 stable
|
||||
|
||||
| Phase | Contenu | Effort |
|
||||
|-------|---------|--------|
|
||||
| Stabilisation technique | gRPC, job queue, UUID migration, tests | 30h |
|
||||
| Alignement API frontend↔backend | Routes social, search, inventory, live | 20h |
|
||||
| Core features manquantes | Recherche avancée, analytics basiques, chat complet | 40h |
|
||||
| Polish & testing | E2E stable, Storybook audit, bug fixes | 20h |
|
||||
| **TOTAL** | | **110h (~3 semaines à temps plein)** |
|
||||
|
||||
---
|
||||
|
||||
## SCORE GLOBAL DE MATURITÉ
|
||||
|
||||
### 32 / 100
|
||||
|
||||
**Détail :**
|
||||
|
||||
| Critère | Score | Pondération | Note |
|
||||
|---------|-------|-------------|------|
|
||||
| Compilation & santé du code | 95/100 | 15% | Tout compile, peu de TODOs critiques |
|
||||
| Architecture & structure | 80/100 | 15% | Bien organisé mais incohérences gRPC/HTTP |
|
||||
| Features TIER 0 | 44/100 | 25% | 44% des features V1 implémentées |
|
||||
| Tests & qualité | 70/100 | 10% | 99.6% unit pass, E2E instable |
|
||||
| Intégration inter-services | 30/100 | 15% | gRPC stub, routes orphelines, MSW-only pages |
|
||||
| Documentation & DevEx | 75/100 | 5% | Bien documenté, env templates complets |
|
||||
| Sécurité | 85/100 | 10% | Excellente pour un PoC |
|
||||
| Infrastructure & Ops | 60/100 | 5% | Surdimensionné mais fonctionnel |
|
||||
|
||||
**Score pondéré : 32/100**
|
||||
|
||||
---
|
||||
|
||||
**Synthèse en une phrase** : Veza possède une base technique solide et bien architecturée (compilation propre, 3300+ tests, sécurité exemplaire, infrastructure K8s complète), mais reste à mi-chemin de la stabilité fonctionnelle : le stream server ne communique pas vraiment avec le backend, 4 modules frontend n'existent qu'en mocks, et seulement 44% des features TIER 0 sont implémentées de bout en bout — il faut environ 3 semaines de travail focalisé pour atteindre une v0.101 stable.
|
||||
695
AUDIT_REPORT.md
695
AUDIT_REPORT.md
|
|
@ -1,695 +0,0 @@
|
|||
# AUDIT_REPORT v2 — monorepo Veza
|
||||
|
||||
> **Date** : 2026-04-20
|
||||
> **Branche** : `main` (HEAD = `89a52944e`, `v1.0.7-rc1`)
|
||||
> **Auditeur** : Claude Code (Opus 4.7 — mode autonome, /effort max, /plan)
|
||||
> **Méthode** : 5 agents Explore en parallèle (frontend, backend Go, Rust stream, infra/DevOps, dette transverse) + mesures macro directes + lecture `docs/audit-2026-04/v107-plan.md` + `CHANGELOG.md` v1.0.5 → v1.0.7-rc1.
|
||||
> **Supersede** : [v1 du 2026-04-14](#annexe-diff-v1-v2) (HEAD `45662aad1`, v1.0.0-mvp-24). Depuis : v1.0.4 → v1.0.5 → v1.0.5.1 → v1.0.6 → v1.0.6.1 → v1.0.6.2 → v1.0.7-rc1. 50+ commits. Le v1 est **obsolète** : son "chemin critique v1.0.5 public-ready" a été réalisé intégralement, mais sa liste de hygiène repo (binaires, screenshots, .git 2.3 GB) est **restée en état**.
|
||||
> **Ton** : brutal, pas de langue de bois. Citations `fichier:ligne`.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR — ce que je retiens en 12 lignes
|
||||
|
||||
1. **Plomberie produit : solide.** v1.0.5 → v1.0.7-rc1 a fermé tout le "chemin critique" fonctionnel : register/verify réels, player fallback `/stream`, refund reverse-charge Hyperswitch, reconciliation sweep, Stripe Connect reversal worker, ledger-health Prometheus gauges, maintenance mode persisté, chat multi-instance avec alarme loud. 50+ commits, **18 findings v1 résolus**. Détail : [FUNCTIONAL_AUDIT.md](FUNCTIONAL_AUDIT.md).
|
||||
2. **Hygiène repo : catastrophique.** `.git` = **2.3 GB** (inchangé depuis v1). Binaire `api` de **99 MB** encore à la racine (tracked, ELF). 44 fichiers audio `.mp3/.wav` encore dans `veza-backend-api/uploads/`. 48 screenshots PNG à la racine (`dashboard-*.png`, `login-*.png`, `design-system-*.png`, `forgot-password-*.png`). 36 `.playwright-mcp/*.yml` debris de sessions MCP. `CLAUDE_CONTEXT.txt` = **977 KB** à la racine.
|
||||
3. **`CLAUDE.md` globalement juste** (v1.0.4, 2026-04-14) mais Vite annoncé "5" → réellement **Vite 7.1.5** (`apps/web/package.json`). Axios "déprécié en dev" → réellement `1.13.5` moderne. `docs/ENV_VARIABLES.md` introuvable alors que CLAUDE.md dit "à maintenir".
|
||||
4. **Frontend** : 1984 fichiers TS/TSX. **36 features** modulaires. Router propre (27 routes top-level, 54 lazy). `src/types/generated/api.ts` = **6550 lignes, régénéré aujourd'hui** — OpenAPI typegen a démarré. **282 occurrences `any`** (dont `services/api/auth.ts:85-100` triple cast token fallback). **6 `console.log` en prod** (checkbox, switch, slider, AdvancedFilters, Onboarding, useLongRunningOperation). 11 composants UI orphelins (`hover-card/*`, `dropdown-menu/*`, `optimized-image/*`). 3.5 MB de dead reports (`e2e-results.json` 3.4 MB, `lint_comprehensive.json` 793 KB, `ts_errors.log` 29 KB).
|
||||
5. **Backend Go** : 877 fichiers `.go`, **197K LOC**. 27 fichiers routes, 135 handlers, 226 services, 81 modèles, **160 migrations** (jusqu'à `983_`), 17 workers, 11 jobs. **Transactions manquantes** sur paths critiques (marketplace `service.go:1050+`, subscription). **31 instances `context.Background()` dans handlers** → timeout middleware défait. 3 binaires trackés (`api`, `main`, `veza-api`). **Duplicate `RespondWithAppError`** (`response/response.go:101` + `handlers/error_response.go:12`).
|
||||
6. **Rust stream server** : Axum 0.8 + Tokio 1.35 + Symphonia. HLS ✅ réel, HTTP Range 206 ✅, WebSocket 1047 LOC ✅, adaptive bitrate 515 LOC ✅. **DASH commenté** (`streaming/protocols/mod.rs:4`). **WebRTC commenté** (`Cargo.toml:62`). **`#![allow(dead_code)]` global** au `lib.rs:5` — camoufle les stubs. 0 `unsafe` (engagement CLAUDE.md tenu). **`proto/chat/chat.proto` orphelin** depuis suppression chat Rust (2026-02-22). `veza-common/src/chat/*` types orphelins.
|
||||
7. **Chat server Rust** : **confirmé absent** (commit `05d02386d`, 2026-02-22). Zéro référence dans k8s (bon). **`proto/chat/*.proto` reste comme spec historique** — à déplacer en `docs/archive/` ou supprimer.
|
||||
8. **Desktop Electron** : **confirmé absent**. Jamais implémenté. Fossile des docs anciennes.
|
||||
9. **Docker** : 6 compose files (dev/prod/staging/test/root/`infra/lab.yml` DEPRECATED Feb 2026). **MinIO pinné `:latest` dans 4 composes** → supply-chain risk. ES 8.11.0 uniquement en dev (orphelin ? backend utilise Postgres FTS). Healthchecks partout mais intervals incohérents (5s→30s). **3 variants Dockerfile par service** (base + .dev + .production) — multi-stage, non-root user `app` (uid 1001), `-w -s` stripped. ⚠️ stream-server Dockerfile.production expose `8082` mais `docker-compose.prod.yml:284` healthcheck attend `3001` — **mismatch**.
|
||||
10. **CI/CD** : 5 workflows actifs (`ci.yml` consolidé + `frontend-ci.yml` + `security-scan.yml` gitleaks + `trivy-fs.yml` + `go-fuzz.yml`). **19 workflows disabled, 1676 LOC mort** (`backend-ci.yml.disabled`, `cd.yml.disabled`, `staging-validation.yml.disabled`, `accessibility.yml.disabled`, etc.). E2E **pas déclenché en CI** alors que Playwright existe. Tests integration skipped (`VEZA_SKIP_INTEGRATION=1`) faute de Docker socket.
|
||||
11. **Sécurité** : JWT RS256 prod / HS256 dev ✅. OAuth (Google/GitHub/Discord/Spotify) ✅. 2FA TOTP ✅. CORS strict en prod ✅. gitleaks + govulncheck + trivy en CI ✅. **Absents** : CSP header, X-Frame-Options (0 grep hit). **.env committé** (`/veza-backend-api/.env`, `-rw-r--r--`). **TLS certs committés** : `/docker/haproxy/certs/veza.pem`, `/config/ssl/{cert,key,veza}.pem` — **rotate + BFG needed**.
|
||||
12. **Verdict monorepo** : **Moyen-Haute dette sur l'hygiène, Faible dette sur le code applicatif**. Le produit fonctionne, la plomberie monétaire est auditée, la sécurité applicative est solide. Mais les items "cleanup" de l'audit v1 n'ont **pas été traités** : binaires trackés, .git 2.3 GB, screenshots racine, .playwright-mcp debris, CLAUDE_CONTEXT.txt 977 KB, 19 workflows disabled, .env/certs committed. **~1 jour de cleanup brutal reste à faire** avant le tag v1.0.7 final.
|
||||
|
||||
---
|
||||
|
||||
## 1. État des lieux — mesures macro directes
|
||||
|
||||
### 1.1 Taille & fichiers
|
||||
|
||||
| Mesure | v1 (14-04) | v2 (20-04) | Delta |
|
||||
| ------------------------- | ------------ | ------------- | -------------------------------------- |
|
||||
| `.git` (du -sh) | 2.3 GB | **2.3 GB** | 0 (pas de `git filter-repo` fait) |
|
||||
| Fichiers trackés | 6425 | **6313** | −112 (quelques cleanups ponctuels) |
|
||||
| Binaires ELF racine | 3 (api/main/veza-api) | **1 (`api` 99 MB)** | 2 supprimés mais 1 persiste |
|
||||
| Screenshots racine | 54 | **48** | −6 |
|
||||
| `.md` total repo | inconnu | **435** (18 active + 417 archive) | — |
|
||||
| `.playwright-mcp/*.yml` | — | **36 (untracked)** | NEW debris |
|
||||
| `CLAUDE_CONTEXT.txt` | — | **977 KB** racine | NEW artifact de session |
|
||||
| `output.txt` racine | — | **27 KB** | NEW |
|
||||
|
||||
### 1.2 Ce qui n'existe PAS (contrairement à certaines docs)
|
||||
|
||||
| Objet | Status | Preuve |
|
||||
| ---------------------------------- | :--------------: | ------------------------------------------------------------------------------------------------ |
|
||||
| `veza-chat-server/` | ❌ absent | `ls /home/senke/git/talas/veza/veza-chat-server` → no such dir. Commit `05d02386d` (2026-02-22). |
|
||||
| `apps/desktop/` (Electron) | ❌ absent | Jamais implémenté. |
|
||||
| `backend/` racine | ❌ absent | C'est `veza-backend-api/`. |
|
||||
| `frontend/` racine | ❌ absent | C'est `apps/web/`. |
|
||||
| `ORIGIN/` racine | ❌ absent | C'est `veza-docs/ORIGIN/`. |
|
||||
| `proto/chat/chat.proto` utilisé | ❌ orphelin | 0 import dans `veza-stream-server/src/`. Chat 100% Go depuis v0.502. |
|
||||
| Runbooks k8s mentionnant chat Rust | ❌ clean (bonne) | Grep `veza-chat-server` dans `k8s/` = 0 hit. |
|
||||
| **Binaire `api` 99 MB racine** | ⚠️ **présent** | `-rwxr-xr-x 1 senke senke 99515104 Mar 24 15:40 api`. **À supprimer.** |
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture & stack — mise à jour exacte
|
||||
|
||||
### 2.1 Arborescence réelle
|
||||
|
||||
```
|
||||
veza/ (2.3 GB .git, 6313 fichiers trackés)
|
||||
├── apps/web/ # React 18.2 + Vite 7.1.5 + TS 5.9.3 + Zustand 4.5 + React Query 5.17
|
||||
│ └── src/ (1984 fichiers TS/TSX)
|
||||
│ ├── features/ (36 feature folders)
|
||||
│ ├── components/ui/ (255 fichiers — design system)
|
||||
│ ├── services/ (73 fichiers)
|
||||
│ ├── types/generated/ (api.ts 6550 lignes, régénéré aujourd'hui)
|
||||
│ └── router/routeConfig.tsx (184 lignes, 27 routes top-level, 54 lazy)
|
||||
│
|
||||
├── veza-backend-api/ # Go 1.25.0 + Gin + GORM + Postgres + Redis + RabbitMQ
|
||||
│ ├── cmd/api/main.go (orchestration wiring)
|
||||
│ ├── cmd/{migrate_tool,backup,generate-config-docs,tools/*} (~6 binaires)
|
||||
│ ├── internal/ (877 fichiers .go, 197K LOC)
|
||||
│ │ ├── api/ (27 routes_*.go)
|
||||
│ │ ├── api/handlers/ (3 fichiers DEPRECATED — chat, rbac)
|
||||
│ │ ├── handlers/ (135 fichiers — source active)
|
||||
│ │ ├── services/ (226 fichiers, 64K LOC)
|
||||
│ │ ├── core/*/ (9 services feature-scoped)
|
||||
│ │ ├── models/ (81 fichiers, 44K LOC)
|
||||
│ │ ├── migrations/ (160 .sql, jusqu'à 983_)
|
||||
│ │ ├── workers/ (17) + jobs/ (11)
|
||||
│ │ ├── middleware/ (~30)
|
||||
│ │ ├── repositories/ (18 GORM-based)
|
||||
│ │ └── repository/ (1 ORPHELIN in-memory mock)
|
||||
│ ├── docs/swagger.{json,yaml} (v1.2.0, 2026-03-03)
|
||||
│ ├── uploads/ (44 .mp3/.wav TRACKÉS !)
|
||||
│ └── {api,main,veza-api} (3 binaires ELF trackés dans CLAUDE.md .gitignore mais présents)
|
||||
│
|
||||
├── veza-stream-server/ # Rust 2021 + Axum 0.8 + Tokio 1.35 + Symphonia 0.5 + sqlx 0.8 + tonic 0.11
|
||||
│ └── src/
|
||||
│ ├── streaming/ (HLS réel, WebSocket 1047 LOC, adaptive 515 LOC, DASH stub commenté)
|
||||
│ ├── audio/ (Symphonia + LAME native; opus/webrtc/fdkaac commentés)
|
||||
│ ├── core/ (StreamManager 10k+ concurrents, sync engine 1920 LOC)
|
||||
│ ├── auth/ (JWT HMAC-SHA256, revocation Redis+in-mem fallback, 825 LOC)
|
||||
│ ├── grpc/ (Stream+Auth+Events — generated 21845 LOC auto)
|
||||
│ ├── transcoding/ (queue job engine 94 LOC — ALPHA)
|
||||
│ ├── event_bus.rs (RabbitMQ degraded mode, 248 LOC)
|
||||
│ └── lib.rs:5 #![allow(dead_code)] GLOBAL — camoufle les stubs
|
||||
│
|
||||
├── veza-common/ # Rust types partagés
|
||||
│ └── src/{chat,ws,files,track,user,playlist,media,api}.rs
|
||||
│ └── chat.rs, track.rs, user.rs, etc. — ORPHELINS depuis suppression chat Rust
|
||||
│
|
||||
├── packages/design-system/ # Tokens design (unique package workspace)
|
||||
│
|
||||
├── proto/
|
||||
│ ├── common/auth.proto ✅ utilisé par stream-server + backend
|
||||
│ ├── stream/stream.proto ✅ utilisé par stream-server
|
||||
│ └── chat/chat.proto ❌ ORPHELIN (chat en Go depuis v0.502)
|
||||
│
|
||||
├── docs/
|
||||
│ ├── audit-2026-04/ (NEW : axis-1-correctness.md + v107-plan.md)
|
||||
│ ├── archive/ (278 fichiers .md historique)
|
||||
│ └── (API_REFERENCE, ONBOARDING, PROJECT_STATE, FEATURE_STATUS, etc.)
|
||||
│
|
||||
├── veza-docs/ # Docusaurus séparé
|
||||
│ ├── docs/{current,vision}/
|
||||
│ └── ORIGIN/ (22 fichiers phase-0 FOSSILE, jamais touchée post-launch)
|
||||
│
|
||||
├── k8s/ # ~30-40 manifests + 5 runbooks disaster-recovery
|
||||
├── config/ # alertmanager, grafana, haproxy, prometheus, incus, ssl/* (.pem TRACKÉS)
|
||||
├── infra/ # nginx-rtmp + docker-compose.lab.yml (DEPRECATED)
|
||||
├── docker/ # haproxy/certs/veza.pem (TRACKÉ, sensible)
|
||||
├── tests/e2e/ # Playwright — SKIPPED_TESTS.md liste les flakies
|
||||
├── .github/workflows/ # 5 actifs + 19 .disabled (1676 LOC mort)
|
||||
├── .husky/ # pre-commit + pre-push + commit-msg (untracked mais fonctionnels)
|
||||
└── {docker-compose*.yml} # 6 files (dev/prod/staging/test/root/env.example)
|
||||
```
|
||||
|
||||
### 2.2 Stack — versions actuelles
|
||||
|
||||
| Composant | Doc (CLAUDE.md) | Réel (code) | Écart ? |
|
||||
| -------------- | --------------- | ----------------- | ----------------- |
|
||||
| Go | 1.25 | **1.25.0** (go.mod) | ✅ OK |
|
||||
| React | 18.2 | 18.2.0 | ✅ OK |
|
||||
| Vite | **5** | **7.1.5** | ❌ CLAUDE.md obsolète |
|
||||
| TypeScript | 5.9.3 | 5.9.3 | ✅ OK |
|
||||
| Zustand | — | 4.5.0 | N/A |
|
||||
| React Query | 5 | 5.17.0 | ✅ OK |
|
||||
| Tailwind | — | **4.0.0** | ✅ récent |
|
||||
| date-fns | 4 | 4.1.0 | ✅ OK |
|
||||
| Axios | non mentionné | 1.13.5 | ✅ moderne |
|
||||
| jwt-go | v5 | v5.3.0 | ✅ OK |
|
||||
| gorm | — | v1.30.0 | ✅ OK |
|
||||
| gin | — | v1.11.0 | ✅ OK |
|
||||
| redis-go | — | v9.16.0 | ✅ OK |
|
||||
| Rust edition | 2021 | 2021 | ✅ OK |
|
||||
| Axum | 0.8 | 0.8 | ✅ OK |
|
||||
| Tokio | 1.35 | 1.35 | ✅ OK |
|
||||
| Symphonia | 0.5 | 0.5 | ✅ OK |
|
||||
| sqlx | 0.8 | 0.8 | ✅ OK |
|
||||
| tonic | — | 0.11 | ✅ récent |
|
||||
| Postgres | 16 | 16-alpine (pinned)| ✅ OK |
|
||||
| Redis | 7 | 7-alpine (pinned) | ✅ OK |
|
||||
| ES | 8.11.0 | 8.11.0 (dev only) | ⚠️ orphelin prod |
|
||||
| RabbitMQ | 3 | 3 (pinned) | ✅ OK |
|
||||
| ClamAV | 1.4 | 1.4 (pinned) | ✅ OK |
|
||||
| MinIO | — | **`:latest`** (4×)| ❌ supply-chain |
|
||||
| Hyperswitch | 2026.03.11.0 | 2026.03.11.0 | ✅ OK |
|
||||
|
||||
**À corriger dans CLAUDE.md v1.0.5** : Vite 5 → Vite 7.1.5. Ajouter ligne MinIO.
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend (`apps/web/`)
|
||||
|
||||
### 3.1 Architecture & routes
|
||||
|
||||
- **36 feature folders** (`src/features/`) — les plus gros : `playlists/` (182), `tracks/` (181), `auth/` (100), `player/` (94), `chat/` (67).
|
||||
- **Router** (`src/router/routeConfig.tsx:1-184`) — 27 routes top-level, **54 composants lazy**. **Zéro route "Coming Soon"/placeholder**. Tous les paths mènent à un composant réel.
|
||||
- **OpenAPI typegen enclenché** : `src/types/generated/api.ts` = **6550 lignes, régénéré 2026-04-19 00:57:21**. La migration "kill hand-written services" prévue post-v1.0.4 a démarré. Script `apps/web/scripts/generate-types.sh` wiré en pre-commit.
|
||||
|
||||
### 3.2 Composants & design system
|
||||
|
||||
- `src/components/ui/` : **255 fichiers**. Untracked : `testids.ts` (NEW, probablement wiring E2E).
|
||||
- **Composants orphelins identifiés** (0-1 imports — candidates suppression) :
|
||||
- `components/ui/optimized-image/OptimizedImageSkeleton.tsx` (0)
|
||||
- `components/ui/optimized-image/ResponsiveImage.tsx` (0)
|
||||
- `components/ui/hover-card/*` (3 fichiers, 0 imports — arbre mort)
|
||||
- `components/ui/dropdown-menu/*` (7 fichiers, 0-1 imports — probablement remplacé par Radix)
|
||||
- Total : **~11 fichiers orphelins dans le DS**.
|
||||
|
||||
### 3.3 State & services
|
||||
|
||||
- **Zustand** : 5 stores principaux (`authStore`, `chatStore`, `playerStore`, `queueSessionStore`, `cartStore`) — tous utilisés.
|
||||
- **React Query** : **seulement 9 fichiers** utilisent `useQuery/useMutation`. `queryKey` ad-hoc (hardcoded, dynamic, constants mélangés). **Pas de factory centralisée** → cache invalidation fragile.
|
||||
- **Services** (73 fichiers) :
|
||||
- Top 4 monolithes : `services/api/auth.ts:553` (token+login+register+2FA), `services/adminService.ts:474` (7+ endpoints), `services/analyticsService.ts:472`, `services/marketplaceService.ts:351`.
|
||||
- **Anti-pattern critique** : `services/api/auth.ts:85-100` fait 3 fallback `const rd = response.data as any` pour parser les tokens. **Pas de validation Zod.**
|
||||
|
||||
### 3.4 Tests
|
||||
|
||||
- **286 fichiers `.test.ts(x)`** (Vitest).
|
||||
- **1 test skipped** : `features/auth/pages/ResetPasswordPage.test.tsx` (async timing).
|
||||
- **E2E** (racine `tests/e2e/`) : Playwright présent, **SKIPPED_TESTS.md documente les flakies** (v107-e2e-04/05/06/08/09 à vérifier en staging).
|
||||
- Tests E2E **PAS déclenchés en CI** (Playwright absent de `.github/workflows/ci.yml`).
|
||||
|
||||
### 3.5 Dette frontend
|
||||
|
||||
| Dette | Count | Sévérité |
|
||||
| ---------------------------------- | :---: | :------: |
|
||||
| `TODO/FIXME/HACK` | 1 | ✅ top |
|
||||
| `console.log` en production | 6 fichiers (checkbox, switch, slider, AdvancedFilters, Onboarding, useLongRunningOperation) | 🔴 |
|
||||
| `any` types | 282 | 🔴 |
|
||||
| `@ts-ignore` / `@ts-expect-error` | 6 fichiers | 🟡 |
|
||||
| Fichiers >500 LOC (non-gen) | ~8 | 🟡 |
|
||||
| Composants V2/V3/_old/_new | 0 | ✅ |
|
||||
| `src/types/v2-v3-types.ts` | présent (mentionné CLAUDE.md) | 🟡 |
|
||||
|
||||
### 3.6 Artefacts morts à la racine de `apps/web/`
|
||||
|
||||
| Fichier | Taille | Date (mtime) | Status |
|
||||
| ---------------------------- | ------ | ------------ | ----------------- |
|
||||
| `e2e-results.json` | 3.4 MB | Mar 15 | 🔴 obsolète |
|
||||
| `lint_comprehensive.json` | 793 KB | Jan 7 | 🔴 obsolète |
|
||||
| `e2e-results.json` (2) | 241 KB | Jan 7 | 🔴 doublon |
|
||||
| `ts_errors.log` | 29 KB | Dec 12 | 🔴 2+ mois stale |
|
||||
| `storybook-roadmap.json` | 8.5 KB | Mar 6 | 🟡 |
|
||||
| `AUDIT_ISSUES.json` | 19 KB | Dec 17 | 🔴 |
|
||||
| `audit.log`, `debug-storybook.log` | 8.5 KB | Feb/Mar | 🟡 |
|
||||
|
||||
**~3.5 MB de reports morts** au bord du frontend. CLAUDE.md §règles 11 interdit ces fichiers en git (ils sont ignorés via `.gitignore` mais traînent en untracked).
|
||||
|
||||
---
|
||||
|
||||
## 4. Backend Go (`veza-backend-api/`)
|
||||
|
||||
### 4.1 Structure
|
||||
|
||||
- **877 fichiers .go** dans `internal/`
|
||||
- **27 fichiers `routes_*.go`** (1 est un test)
|
||||
- **135 handlers actifs** dans `internal/handlers/`
|
||||
- **3 fichiers dans `internal/api/handlers/`** — confirmés DEPRECATED (chat + RBAC, à purger après confirmation aucun import)
|
||||
- **226 services** (`internal/services/`) + **9 core services** (`internal/core/*/service.go`)
|
||||
- **81 modèles** (`internal/models/`, 44K LOC) — pattern GORM + soft-delete
|
||||
- **160 migrations SQL** (jusqu'à `983_hyperswitch_webhook_log.sql`)
|
||||
- **17 workers** + **11 jobs**
|
||||
- **~30 middlewares**
|
||||
|
||||
### 4.2 Routes & handlers
|
||||
|
||||
Handlers complets par domaine, **zéro endpoint retournant 501 ou vide**. Zéro double wiring.
|
||||
|
||||
Top routes par taille : `routes_core.go:512` (20+ routes), `routes_auth.go:245` (14+ routes, 2FA/OAuth inclus), `routes_tracks.go:240` (18+), `routes_users.go:296` (17+), `routes_marketplace.go:174` (15+), `routes_webhooks.go:205` (5+ ; raw payload audit).
|
||||
|
||||
### 4.3 Auth
|
||||
|
||||
| Aspect | Status | Preuve |
|
||||
| -------------------- | :----: | ---------------------------------------------------------------------------------------------------- |
|
||||
| JWT RS256 prod | ✅ | `services/jwt_service.go:17-81`, keys depuis env. |
|
||||
| HS256 dev fallback | ✅ | Idem, 32+ char secret exigé. |
|
||||
| Refresh 7j / Access 5min | ✅ | Configurés. |
|
||||
| 2FA TOTP + backup codes | ✅ | `handlers/two_factor_handler.go:171` (actif). `api/handlers/` vide de 2FA — deprecated purgé. |
|
||||
| OAuth 4 providers | ✅ | `routes_auth.go:122-176` (Google, GitHub, Discord, Spotify). State encrypté via CryptoService. |
|
||||
| Rate limiting multi-couche | ✅ + 🟡 | DDoS global 1000 req/s ✅, endpoint-specific ✅, API key ✅, **`UserRateLimiter` configuré mais pas wiré aux routes**. |
|
||||
| CSRF | ✅ | Middleware actif (e2e confirmé `tests/e2e/45-playlists-deep.spec.ts`). Disabled dev/staging (`router.go:133`). |
|
||||
| Security headers | 🟡 | SecurityHeaders middleware présent (`router.go:204`). **CSP / X-Frame-Options pas vus en grep**. À vérifier. |
|
||||
|
||||
### 4.4 Modèles, DB, transactions
|
||||
|
||||
- Migrations auto-appliquées au démarrage (`database.go:234-256`). Boot fail si erreur SQL.
|
||||
- Repositories : 18 GORM-direct, pattern inline (pas d'interface). **Plus** `internal/repository/` (1 fichier in-memory mock UserRepository) **ORPHELIN** — à supprimer.
|
||||
- **Transactions insuffisantes** — `db.Transaction()` usage = **8×**, `tx.Create/Save/Delete` manuel = **37×**. Chemins critiques (marketplace `core/marketplace/service.go:1050+`, subscription) ne sont **pas dans des transactions explicites**. Risque data corruption si une étape échoue au milieu.
|
||||
|
||||
### 4.5 Services & context
|
||||
|
||||
- Architecture dual-layer `core/` + `services/` **incohérente** : certaines features ont `core/service.go`, d'autres `services/*.go`, sans règle claire. Ex. track publication en `core/track/` mais search indexing en `services/track_search_service.go`, les deux appelés depuis un même handler.
|
||||
- Context propagation : 558 usages propres dans services, **mais 31 `context.Background()` dans `handlers/`** → défait le timeout middleware. Fix grep+sed 1 jour.
|
||||
- **Pas de `services_init.go`** : services instantiés inline dans `routes_*.go`. Re-créés par request-group. Non-singletons.
|
||||
|
||||
### 4.6 Workers & jobs
|
||||
|
||||
- **Actifs lancés par `cmd/api/main.go`** : JobWorker, TransferRetry, StripeReversal, Reconciliation, CloudBackup, GearWarranty, NotifDigest, HardDelete, OrphanTracksCleanup, LedgerHealthSampler.
|
||||
- **Jobs définis mais jamais schedulés** : `SchedulePasswordResetCleanupJob`, `CleanupExpiredSessions`, `CleanupVerificationTokens`, `CleanupHyperswitchWebhookLog` — ~4 cleanup jobs **dead code**. Soit les brancher soit les supprimer.
|
||||
|
||||
### 4.7 Tests
|
||||
|
||||
- **364 fichiers `*_test.go`**. `coverage_v1.out` (Mar 3) indique ~60-70%.
|
||||
- Integration tests skippables via config — mais **pas de variable `VEZA_SKIP_INTEGRATION` trouvée en grep** (CLAUDE.md la mentionne — à vérifier si elle existe réellement ou si c'est un fossile doc).
|
||||
- E2E Playwright n'entre jamais en CI.
|
||||
|
||||
### 4.8 Validation & errors
|
||||
|
||||
- `internal/validators/` — wrapper `go-playground/validator/v10` ✅
|
||||
- `internal/errors/` — `AppError{Code,Message,Err,Details,Context}` ✅
|
||||
- **PROBLÈME** : `RespondWithAppError` défini **2 fois** (`response/response.go:101` + `handlers/error_response.go:12`). Duplication à consolider.
|
||||
- Wrapped errors : 349 usages `errors.Is/As/Unwrap` — bon pattern.
|
||||
|
||||
### 4.9 Config
|
||||
|
||||
- **99 env vars lues** dans `config/config.go` (1087 LOC)
|
||||
- **`Config.Validate()`** :
|
||||
- ✅ Refuse prod si `HYPERSWITCH_ENABLED=false` (`config.go:908-910`, fail-closed).
|
||||
- ✅ Refuse prod sans DATABASE_URL, JWT keys, CORS origins.
|
||||
- ❌ **Pas de check `APP_ENV ∈ {dev,staging,prod}`** — silencieusement default dev.
|
||||
- ❌ **Pas de check `UPLOAD_DIR` exists** — boot success même si dir manquant.
|
||||
- **`.env.template` 190 lignes** vs 263 `os.Getenv` appels code → drift potentiel (~70 vars documentées vs 99 utilisées).
|
||||
|
||||
### 4.10 Dette backend — récap
|
||||
|
||||
| Dette | Sévérité | Effort | Preuve |
|
||||
| ------------------------------------------- | :-------: | :----: | ------------------------------------------------------------- |
|
||||
| Transactions manquantes marketplace/subs | 🔴 | M (3j) | `core/marketplace/service.go:1050+` |
|
||||
| 31× `context.Background()` dans handlers | 🔴 | S (1j) | Grep handlers |
|
||||
| Binaires racine `api` (99MB) + 44 .mp3 | 🔴 | XS (1h)| `git rm --cached` + BFG |
|
||||
| `RespondWithAppError` dupliqué | 🟡 | S (1j) | `response/response.go:101` + `handlers/error_response.go:12` |
|
||||
| `internal/repository/` orphelin | 🟡 | XS | Delete dir |
|
||||
| 4 cleanup jobs jamais schedulés | 🟡 | S | Brancher ou supprimer |
|
||||
| `UserRateLimiter` configuré non wiré | 🟡 | S | Wire en middleware chain |
|
||||
| Écart `.env.template` vs code (29 vars) | 🟠 | S | Sync |
|
||||
| Services re-instantiés par request-group | 🟠 | M | `services_init.go` + singleton pattern |
|
||||
| Architecture core/+services/ incohérente | 🟠 | L | Document la règle OU unifier |
|
||||
|
||||
---
|
||||
|
||||
## 5. Rust stream server (`veza-stream-server/`)
|
||||
|
||||
### 5.1 Modules
|
||||
|
||||
Production-ready : `streaming/` (HLS réel, Range 206, WS 1047 LOC, adaptive 515 LOC), `audio/` (Symphonia native, compression 708 LOC, effects SIMD), `core/` (StreamManager 10k+ concurrents, sync engine NTP-like 1920 LOC), `auth/` (JWT HMAC-SHA256 + revocation Redis-or-in-mem 825 LOC), `cache/` (LRU audio), `event_bus.rs` (RabbitMQ degraded mode).
|
||||
|
||||
Alpha / partiel : `transcoding/engine.rs` (94 LOC, job queue priority-based mais **zéro test d'intégration, zéro tracking live**), `grpc/` (461 LOC business + 21845 LOC généré).
|
||||
|
||||
**Stub / absent** :
|
||||
- `streaming/protocols/mod.rs:4` → `// pub mod dash;` **commenté**.
|
||||
- `Cargo.toml:62` → `// webrtc = "0.7"` **commenté** (deps natives manquantes).
|
||||
|
||||
### 5.2 Audio codecs
|
||||
|
||||
Symphonia couvre MP3, FLAC, Vorbis, AAC **natifs**. LAME MP3 via `minimp3 0.5` (natif). **Commentés** : `opus 0.3` (cmake), `lame 0.1`, `fdkaac 0.7` (non sur crates.io).
|
||||
|
||||
### 5.3 gRPC & protos
|
||||
|
||||
`StreamService`, `AuthService`, `EventsService` (3 services). Utilise `proto/common/auth.proto` + `proto/stream/stream.proto`. **`proto/chat/chat.proto` = 0 import** → orphelin depuis suppression chat Rust.
|
||||
|
||||
### 5.4 Dette Rust
|
||||
|
||||
| Dette | Sévérité | Preuve |
|
||||
| ----------------------------------------------- | :------: | ---------------------------------------------------------------- |
|
||||
| `#![allow(dead_code)]` global dans `lib.rs:5` | 🔴 | Masque tous les stubs. Devrait être granulaire par module. |
|
||||
| 10× `unwrap()` sur broadcast channels | 🔴 | `core/sync.rs:1037-1110`. Panic si receiver drop. `.expect()` + contexte. |
|
||||
| `proto/chat/chat.proto` orphelin | 🟡 | À archiver/supprimer. |
|
||||
| `veza-common` chat types orphelins | 🟡 | ~60 LOC dead. Audit grep `use veza_common::chat` → 0 hit. |
|
||||
| `transcoding/` zéro tests intégration | 🟡 | `engine.rs:36-62`. |
|
||||
| 26× `println!/dbg!` | 🟡 | Devrait utiliser `tracing::`. |
|
||||
| Deps inutilisées (`daemonize`, `notify`) | 🟠 | `Cargo.toml:139, 116`. |
|
||||
|
||||
**0 `unsafe`** ✅ (engagement CLAUDE.md tenu).
|
||||
|
||||
---
|
||||
|
||||
## 6. Infrastructure & DevOps
|
||||
|
||||
### 6.1 Docker Compose (6 fichiers)
|
||||
|
||||
| Fichier | Rôle | État |
|
||||
| ---------------------------- | --------------------------------- | ------------------------------------------ |
|
||||
| `docker-compose.yml` | Dev full-stack avec profiles | ✅ Actif |
|
||||
| `docker-compose.dev.yml` | Infra-only (209 LOC) | ✅ Actif (MailHog + ES 8.11.0 ici uniquement)|
|
||||
| `docker-compose.prod.yml` | Blue-green, HAProxy, Alertmanager (464 LOC) | ✅ Actif (Mar 12) |
|
||||
| `docker-compose.staging.yml` | Caddy (202 LOC) | ✅ Actif (Mar 2) |
|
||||
| `docker-compose.test.yml` | tmpfs CI (64 LOC) | ✅ Actif |
|
||||
| `infra/docker-compose.lab.yml` | DEPRECATED Feb 2026 | 🔴 À supprimer |
|
||||
|
||||
**Pinning** :
|
||||
- ✅ Postgres 16-alpine, Redis 7-alpine, RabbitMQ 3, ClamAV 1.4, Hyperswitch 2026.03.11.0.
|
||||
- ❌ **MinIO `:latest`** dans 4 composes → supply-chain attack vector.
|
||||
|
||||
**Services orphelins en dev-only** :
|
||||
- ES 8.11.0 uniquement `docker-compose.dev.yml:171-204` (34 LOC) — **le backend utilise Postgres FTS, pas ES** (`fulltext_search_service.go`). ES ne sert qu'au hard-delete worker (GDPR cleanup), optionnel. À documenter ou retirer.
|
||||
|
||||
### 6.2 Dockerfiles
|
||||
|
||||
- Backend : `Dockerfile` + `Dockerfile.production` (Go 1.24-alpine, multi-stage, non-root uid 1001, `-w -s`). ⚠️ **CLAUDE.md dit Go 1.25, Dockerfile sur 1.24** — bumper.
|
||||
- Stream : `Dockerfile` + `Dockerfile.production` (rust:1.84-alpine). ⚠️ **Mismatch port** : Dockerfile.production expose `8082` mais `docker-compose.prod.yml:284` healthcheck attend `3001` — **le Dockerfile n'est pas utilisé en prod** (sans doute l'image vient d'ailleurs).
|
||||
- Web : `Dockerfile` + `Dockerfile.dev` + `Dockerfile.production` (node:20-alpine → nginx:1.27-alpine).
|
||||
|
||||
### 6.3 CI/CD
|
||||
|
||||
**Workflows actifs (5)** :
|
||||
1. `ci.yml` (consolidé, ~15min) — backend Go (test, lint, vet, govulncheck), frontend (lint, tsc, build, vitest), rust (build, test, clippy, audit).
|
||||
2. `frontend-ci.yml` (55 LOC) — path-triggered React-only, bundle-size gate, npm audit.
|
||||
3. `security-scan.yml` — gitleaks v8.21.2 secret scan.
|
||||
4. `trivy-fs.yml` — Trivy filesystem scan (HIGH+CRITICAL exit=1).
|
||||
5. `go-fuzz.yml` — Nightly fuzz 60s, corpus upload.
|
||||
|
||||
**Workflows disabled (19 fichiers, 1676 LOC mort)** :
|
||||
`backend-ci.yml.disabled`, `cd.yml.disabled`, `staging-validation.yml.disabled`, `accessibility.yml.disabled`, `chromatic.yml.disabled`, `visual-regression.yml.disabled`, `storybook-audit.yml.disabled`, `contract-testing.yml.disabled`, `zap-dast.yml.disabled`, `container-scan.yml.disabled`, `semgrep.yml.disabled`, `sast.yml.disabled`, `mutation-testing.yml.disabled`, `rust-mutation.yml.disabled`, `load-test-nightly.yml.disabled`, `flaky-report.yml.disabled`, `openapi-lint.yml.disabled`, `commitlint.yml.disabled`, `performance.yml.disabled`.
|
||||
|
||||
**→ 1676 lignes de workflow mort. Soit réactiver ce qui fait sens (SAST, DAST, openapi-lint), soit archiver dans `docs/archive/workflows/` pour ne pas polluer `.github/workflows/`.**
|
||||
|
||||
**Gaps CI** :
|
||||
- E2E Playwright pas déclenché (pourtant `tests/e2e/` existe, `SKIPPED_TESTS.md` documente les flakies).
|
||||
- Integration tests Go skipped (`VEZA_SKIP_INTEGRATION=1` faute de Docker socket sur runner).
|
||||
|
||||
### 6.4 K8s
|
||||
|
||||
- ~30-40 manifests, structure propre (`autoscaling/`, `backends/`, `backups/`, `cdn/`, `disaster-recovery/`, `environments/{prod,staging,dev}`, `secrets/`).
|
||||
- **5 runbooks** : cluster-failover, database-failover, data-restore, rollback-procedure, security-incident.
|
||||
- ✅ **Zéro référence à `veza-chat-server`** dans `k8s/` (grep clean — l'audit v1 disait qu'il y avait 7+ runbooks outdated ; **corrigé**).
|
||||
|
||||
### 6.5 Secrets & sécurité
|
||||
|
||||
| Item | État | Action |
|
||||
| --------------------------------------------- | :------: | -------------------------------------------------------------------- |
|
||||
| `/docker/haproxy/certs/veza.pem` | 🔴 TRACKED | BFG + rotate cert + move to K8s Secret |
|
||||
| `/config/ssl/{cert,key,veza}.pem` | 🔴 TRACKED | Idem |
|
||||
| `veza-backend-api/.env` | 🔴 TRACKED | `git rm --cached`, rotate JWT/DB secrets dev, relire `.gitignore` |
|
||||
| `veza-backend-api/.env.production.example` | 🟢 OK | Template |
|
||||
| Hardcoded secrets en code (`sk_live_`, `AKIA`)| ✅ absent | Grep clean |
|
||||
| gitleaks en CI | ✅ | `security-scan.yml` |
|
||||
| govulncheck | ✅ | `ci.yml` |
|
||||
| CSP header | 🟡 | Grep 0 hit. **À implémenter.** |
|
||||
| X-Frame-Options | 🟡 | Idem |
|
||||
|
||||
### 6.6 Observability
|
||||
|
||||
- Prometheus : **5 gauges ledger-health** déployées en v1.0.7 (`ledger_metrics.go`), **+ counter/histogram reconciler**. Alertmanager `config/alertmanager/ledger.yml` avec 3 règles (VezaOrphanRefundRows, VezaStuckOrdersPending, VezaReconcilerStale). Grafana dashboard `config/grafana/dashboards/ledger-health.json`.
|
||||
- Logs : JSON structuré confirmé (`level`, `time`, `msg`, `request_id`, `user_id`).
|
||||
- **Gap** : `/metrics` endpoint global backend pas vu (à confirmer — il existe probablement via middleware Sentry/Prometheus, mais pas en grep direct).
|
||||
- Sentry : optionnel via env (`SENTRY_DSN`, `SENTRY_SAMPLE_RATE_*`).
|
||||
|
||||
---
|
||||
|
||||
## 7. Documentation
|
||||
|
||||
### 7.1 Racine du repo
|
||||
|
||||
| Fichier | Taille | Date | Verdict |
|
||||
| ------------------------------- | ------ | ---------- | ---------------------------------------------------------------------- |
|
||||
| `CLAUDE.md` | 22 KB | 2026-04-14 | ✅ Autorité. Petite dérive : Vite 5 → 7.1.5 à corriger. |
|
||||
| `CHANGELOG.md` | 87 KB | 2026-04-19 | ✅ À jour (v0.201 → v1.0.7-rc1). |
|
||||
| `README.md` | 2.8 KB | — | ✅ Minimal OK. |
|
||||
| `CONTRIBUTING.md` | 2.7 KB | 2026-02-27 | ✅ OK. |
|
||||
| `VERSION` | — | — | `1.0.7-rc1` ✅ aligné. |
|
||||
| `VEZA_VERSIONS_ROADMAP.md` | 69 KB | — | ⚠️ Historique v0.9xx, peu utile post-launch. Archive. |
|
||||
| `RELEASE_NOTES_V1.md` | 4.7 KB | — | ✅ OK. |
|
||||
| `AUDIT_REPORT.md` | 57 KB | 2026-04-14 | 🔄 **Ce fichier — v2 remplace v1**. |
|
||||
| `FUNCTIONAL_AUDIT.md` | 43 KB | 2026-04-19 | ✅ v2 à jour. |
|
||||
| `UI_CONTEXT_SUMMARY.md` | 6 KB | — | 🟠 Session artifact, devrait être archivé selon CLAUDE.md §12. |
|
||||
| `CLAUDE_CONTEXT.txt` | 977 KB | 2026-04-18 | 🔴 ÉNORME session dump. Archive ou supprime. |
|
||||
| `output.txt` | 27 KB | 2026-04-18 | 🔴 Debris. |
|
||||
| `generate_page_fix_prompts.sh` | 42 KB | Mar 26 | 🟡 Script généré, probablement obsolète. |
|
||||
| `build-archive.log` | 974 B | Mar 25 | 🟡 Log. |
|
||||
|
||||
**48 screenshots PNG racine** (`dashboard-*.png`, `login-*.png`, `design-system-*.png`, `forgot-password-*.png`) — **à déplacer dans `docs/screenshots/` ou supprimer**.
|
||||
|
||||
### 7.2 `docs/` (18 actifs + 417 archive = 435 .md)
|
||||
|
||||
**Actifs** :
|
||||
- `docs/API_REFERENCE.md` (1022 LOC) — **manuel**, pas de typegen. Écart flag vs routes Go. Migration vers OpenAPI typegen backend = priorité.
|
||||
- `docs/ONBOARDING.md`, `docs/PROJECT_STATE.md`, `docs/FEATURE_STATUS.md` — à cross-checker avec code v1.0.7 (non fait ici).
|
||||
- `docs/ENV_VARIABLES.md` — **introuvable en `ls docs/`** alors que CLAUDE.md dit "à maintenir". Soit créé soit manque.
|
||||
- `docs/audit-2026-04/` — **NOUVEAU, très utile** : `axis-1-correctness.md` + `v107-plan.md` — trace des findings et du plan v1.0.7.
|
||||
- `docs/SECURITY_SCAN_RC1.md` / `docs/ASVS_CHECKLIST_v0.12.6.md` / `docs/PENTEST_REPORT_VEZA_v0.12.6.md` — **refs v0.12.6, obsolètes** pour v1.0.7. Refaire ou archiver.
|
||||
|
||||
**Archive** (`docs/archive/` = 278 fichiers) : historique session 2026. Taille totale importante. Ne pose pas de problème immédiat.
|
||||
|
||||
### 7.3 `veza-docs/` (Docusaurus séparé)
|
||||
|
||||
- `veza-docs/docs/{current,vision}/` — doc cible.
|
||||
- `veza-docs/ORIGIN/` (22 fichiers, ~70K lignes) — **phase-0, jamais touchée depuis launch**. Qualifiée "FOSSIL" par agent. Archive ou zip.
|
||||
|
||||
---
|
||||
|
||||
## 8. Dette technique transverse — catalogue
|
||||
|
||||
### 8.1 TODOs / FIXMEs (11 hits)
|
||||
|
||||
1. `tests/e2e/22-performance.spec.ts:8` — "Either add data-testid containers or rewrite test to use API mocking" (3 occurrences).
|
||||
2. `tests/e2e/04-tracks.spec.ts` — "Corriger le bug dans FeedPage.tsx" (ouvert, P1).
|
||||
3. `apps/web/src/features/auth/pages/ResetPasswordPage.test.tsx` — async timing flaky.
|
||||
4. `veza-backend-api/internal/core/marketplace/service.go:1450` — "TODO v1.0.7: Stripe Connect reverse-transfer API" (**effectivement déjà landed en v1.0.7 item A+B** — TODO à supprimer).
|
||||
5. `veza-backend-api/internal/core/subscription/service.go` — "TODO(v1.0.7-item-G): subscription pending_payment state" (in-flight, parked).
|
||||
|
||||
**Aucun TODO daté >6 mois.** Discipline correcte.
|
||||
|
||||
### 8.2 Code mort / orphelin
|
||||
|
||||
| Item | Action |
|
||||
| ------------------------------------------------ | ------------------------------------------------ |
|
||||
| `veza-backend-api/internal/api/handlers/` (3 fichiers) | Confirmer 0 import puis `git rm -r` |
|
||||
| `veza-backend-api/internal/repository/` (in-mem mock) | `git rm -r` |
|
||||
| `apps/web/src/components/ui/hover-card/*` (3) | Delete si confirmé 0 import |
|
||||
| `apps/web/src/components/ui/dropdown-menu/*` (7) | Audit imports, delete si Radix les remplace |
|
||||
| `apps/web/src/components/ui/optimized-image/{OptimizedImageSkeleton,ResponsiveImage}.tsx` | Delete |
|
||||
| `apps/web/src/types/v2-v3-types.ts` | Auditer appelants, renommer ou delete |
|
||||
| `proto/chat/chat.proto` | Archiver `docs/archive/proto-chat/` ou delete |
|
||||
| `veza-common/src/chat.rs` + autres types chat | Audit `use veza_common::chat`, delete si 0 hit |
|
||||
| 19 workflows `.disabled` | Archiver `docs/archive/workflows/` ou delete |
|
||||
| 4 cleanup jobs jamais schedulés (pw-reset, sessions, verif, hyperswitch-log) | Brancher ou delete |
|
||||
|
||||
### 8.3 Binaires / artefacts trackés
|
||||
|
||||
| Item | Taille | Action |
|
||||
| --------------------------------------------------- | ------ | ------------------------------------------------- |
|
||||
| `api` (racine, ELF) | 99 MB | `git rm --cached api` + `.gitignore` |
|
||||
| `veza-backend-api/{main,veza-api,seed,server}` | ~50 MB chacun | Idem (sont dans `.gitignore` mais encore tracked?) |
|
||||
| `veza-backend-api/uploads/*.{mp3,wav}` (44 fichiers)| 12 MB | `git rm -r --cached uploads/` + move to git-lfs ou fixtures |
|
||||
| `CLAUDE_CONTEXT.txt` (racine) | 977 KB | `git rm --cached` ou déplacer |
|
||||
| `apps/web/e2e-results.json` (3.4 MB) | 3.4 MB | `.gitignore` + `rm` |
|
||||
| 48 PNG racine (dashboard-*, login-*, design-system-*, forgot-password-*) | ~5 MB total | Move to `docs/screenshots/` ou delete |
|
||||
| 36 `.playwright-mcp/*.yml` (untracked) | — | `rm -r .playwright-mcp/` |
|
||||
|
||||
### 8.4 Sécurité hors-code
|
||||
|
||||
| Item | Action |
|
||||
| ----------------------------------------- | ------------------------------------------------------ |
|
||||
| `/docker/haproxy/certs/veza.pem` tracked | BFG purge history + rotate cert + K8s Secret |
|
||||
| `/config/ssl/*.pem` tracked | Idem |
|
||||
| `veza-backend-api/.env` tracked | `git rm --cached`, rotate dev secrets, audit team |
|
||||
| CSP header absent | Middleware `SecurityHeaders` — ajouter |
|
||||
| X-Frame-Options absent | Idem |
|
||||
|
||||
### 8.5 Incohérences doc↔code
|
||||
|
||||
| Item | Delta |
|
||||
| ---------------------------------------------- | -------------------------------------------------- |
|
||||
| `CLAUDE.md` : Vite 5 | Réel Vite 7.1.5 — bumper doc |
|
||||
| `CLAUDE.md` : ES 8.11.0 partout | Réel ES 8.11.0 dev-only |
|
||||
| `CLAUDE.md` : Go 1.25 | go.mod 1.25.0 ✅ ; `veza-backend-api/Dockerfile` 1.24 — bumper |
|
||||
| `docs/API_REFERENCE.md` manuel 1022 LOC | 135 handlers — risque drift. OpenAPI typegen backend recommandé. |
|
||||
| `VEZA_VERSIONS_ROADMAP.md` v0.9xx | VERSION = 1.0.7-rc1 — archive le roadmap |
|
||||
| `docs/ASVS_CHECKLIST_v0.12.6.md` etc | Version obsolète. Refaire sur v1.0.7 ou archiver. |
|
||||
| `docs/ENV_VARIABLES.md` mentionné | Pas trouvé en `ls docs/`. Créer. |
|
||||
|
||||
### 8.6 Patterns abandonnés ou à mi-chemin
|
||||
|
||||
1. **OpenAPI typegen frontend** : démarré (`api.ts` 6550 LOC régénéré) mais les **73 services frontend restent hand-written**. Finir la migration (memory entry : "orval recommended").
|
||||
2. **OpenAPI typegen backend** : `docs/API_REFERENCE.md` manuel. Swagger infra (`swaggo/swag`) présente mais pas pleinement exploitée.
|
||||
3. **Repository pattern** : `repositories/` (GORM-direct, 18 fichiers) mixé avec `services/` qui requêtent `gormDB` direct. Pas d'interfaces. Pattern mi-chemin.
|
||||
4. **Architecture `core/` + `services/`** : pas de règle claire. À unifier ou à documenter explicitement quelles features vont où.
|
||||
5. **Transactions** : 8 usages vs 37 tx manuels. Pattern moitié-fait.
|
||||
|
||||
---
|
||||
|
||||
## 9. Top 15 priorités — impact / effort
|
||||
|
||||
> **Mise à jour 2026-04-23** — colonne `Statut` ajoutée après la session cleanup tier 1/2/3 + BFG history rewrite. Voir §9.bis pour le détail des 3 false-positives identifiés pendant l'exécution.
|
||||
|
||||
Classement pour la suite (post-v1.0.7-rc1 → v1.0.7 final → v1.0.8).
|
||||
|
||||
| # | Priorité | Impact | Effort | Statut 2026-04-23 | Rationale / Preuve |
|
||||
| --- | -------------------------------------------------------------------------------- | :----: | :-----: | :---------------- | -------------------------------------------------------------------------- |
|
||||
| 1 | **Supprimer `api` 99 MB + binaires Go trackés racine + `uploads/*.mp3`** | 🔴 CRIT | XS (1h) | ✅ DONE | BFG pass 2026-04-23, 1.5G → 66M. Force-push stages 1+2 OK. |
|
||||
| 2 | **Rotate TLS certs + supprimer `.pem` trackés + .env committed** | 🔴 CRIT | S (4h) | ✅ DONE | `.env*` + certs stripped via BFG. Keys regen, gitignorées. |
|
||||
| 3 | **Transactions marketplace/subscription** | 🔴 CRIT | M (3j) | ✅ DONE | Commit `b5281bec` — `UpdateProductImages` + `SetProductLicenses` en tx. |
|
||||
| 4 | **Context propagation : 31× `context.Background()` dans handlers** | 🔴 | S (1j) | ⚠️ FALSE-POSITIVE | 26/31 dans `*_test.go`, 5 legit (health probes + WS pumps). Voir §9.bis. |
|
||||
| 5 | **Ajouter CSP + X-Frame-Options headers** | 🔴 | S (1j) | ⚠️ FALSE-POSITIVE | `middleware/security_headers.go` couvre déjà CSP + XFO + HSTS + CORP/COEP/COOP. Voir §9.bis. |
|
||||
| 6 | **Pin MinIO `:latest` → tag daté** | 🔴 | XS (10min) | ✅ DONE | Commit `4310dbb7` — pinned `RELEASE.2025-09-07T16-13-09Z` × 4 compose files. |
|
||||
| 7 | **Nettoyer `.playwright-mcp/*.yml` + 48 PNG racine + `CLAUDE_CONTEXT.txt` + dead reports apps/web/** | 🟡 | S (2h) | ✅ DONE | Commits `d12b901d` + `172581ff` + BFG pass. |
|
||||
| 8 | **Terminer OpenAPI typegen** (frontend services + backend swaggo) | 🟡 | L (5j) | 📋 DEFERRED v1.0.8 | Memory entry, drift risk. `api.ts` 6550 LOC déjà là. Plan séparé requis. |
|
||||
| 9 | **Supprimer 19 workflows `.disabled` (1676 LOC mort) OU réactiver utiles (SAST, DAST, openapi-lint)** | 🟡 | S (4h) | ✅ DONE | Archivés dans `docs/archive/workflows/` via commit `172581ff`. |
|
||||
| 10 | **Consolider `RespondWithAppError` dupliqué** | 🟡 | S (1j) | ⚠️ FALSE-POSITIVE | `handlers/error_response.go:12` = wrapper intentionnel déléguant à `response/response.go:101`. Pas dupe. Voir §9.bis. |
|
||||
| 11 | **Wirer `UserRateLimiter` configuré mais non appelé** | 🟡 | S (1j) | ✅ DONE | Commit `ebf3276d` — wired in `AuthMiddleware.RequireAuth()`. |
|
||||
| 12 | **Supprimer `internal/repository/` (in-mem mock orphelin)** | 🟡 | XS | ✅ DONE | `user_repository.go` supprimé dans commit `172581ff`. |
|
||||
| 13 | **Remove/archive `proto/chat/chat.proto` + `veza-common/src/chat.rs`** | 🟡 | XS | ✅ DONE | Commit `172581ff` — proto + `veza-common/{chat.rs, websocket.rs}` supprimés. |
|
||||
| 14 | **Ajouter E2E Playwright en CI** | 🟡 | M (3j) | 📋 DEFERRED v1.0.8 | Playwright existe, SKIPPED_TESTS.md documenté, mais pas trigger CI. |
|
||||
| 15 | **`docs/ENV_VARIABLES.md` — créer si manque, sync avec code** | 🟠 | S (1j) | 📝 PENDING (0.5j) | Seul item réel restant du top-15 avant tag v1.0.7 final. |
|
||||
|
||||
**Bilan** : 10 ✅ DONE · 3 ⚠️ FALSE-POSITIVE · 2 📋 DEFERRED v1.0.8 · 1 📝 PENDING (~0.5j).
|
||||
|
||||
### 9.1 "À supprimer sans regret"
|
||||
|
||||
- `infra/docker-compose.lab.yml` (DEPRECATED Feb 2026)
|
||||
- `scripts/align-8px-grid.py`, `auto_migrate_tailwind_colors*.py` (tailwind migration faite)
|
||||
- 48 PNG racine
|
||||
- 36 `.playwright-mcp/*.yml`
|
||||
- 19 `.disabled` workflows
|
||||
- Binaires Go trackés
|
||||
- 44 fichiers audio `.mp3/.wav` dans `veza-backend-api/uploads/`
|
||||
- `CLAUDE_CONTEXT.txt` racine
|
||||
- `VEZA_VERSIONS_ROADMAP.md` (v0.9xx historique)
|
||||
- `generate_page_fix_prompts.sh` racine (42 KB, Mar 26)
|
||||
- `output.txt`, `build-archive.log` racine
|
||||
- `apps/web/{e2e-results.json, lint_comprehensive.json, ts_errors.log, AUDIT_ISSUES.json}`
|
||||
- `internal/repository/` (orphelin)
|
||||
- `proto/chat/chat.proto` + types `veza-common/src/chat.rs`
|
||||
- `apps/web/src/components/ui/{hover-card,dropdown-menu,optimized-image}/` orphelins
|
||||
- ~~`docs/ASVS_CHECKLIST_v0.12.6.md` + `docs/PENTEST_REPORT_VEZA_v0.12.6.md` + `docs/REMEDIATION_MATRIX_v0.12.6.md`~~ ✅ archivés dans `docs/archive/` (2026-04-23)
|
||||
|
||||
### 9.2 "À finir avant de commencer quoi que ce soit de nouveau"
|
||||
|
||||
> **Mise à jour 2026-04-23** — la liste originale (#1, #2, #3, #4, #5, #7, #8, #9) a été traitée en une session, sauf les 3 false-positives §9.bis et les 2 deferrals. Ne reste qu'un item (§9.3).
|
||||
|
||||
1. ~~**Cleanup repo** (#1, #2, #7, #9)~~ — ✅ fait, 1 session 2026-04-23.
|
||||
2. ~~**Transactions manquantes** (#3)~~ — ✅ fait, commit `b5281bec`.
|
||||
3. ~~**Context propagation** (#4)~~ — ⚠️ false-positive, pas de travail à faire (§9.bis).
|
||||
4. ~~**Security headers** (#5)~~ — ⚠️ false-positive, middleware déjà complet (§9.bis).
|
||||
5. **OpenAPI typegen** (#8) — 📋 deferred v1.0.8, plan séparé requis.
|
||||
|
||||
### 9.bis Corrections post-tier 2 (2026-04-23)
|
||||
|
||||
Trois items du top-15 ont été reclassifiés après inspection directe du code :
|
||||
|
||||
**#4 — "Context propagation : 31× `context.Background()` dans handlers"**
|
||||
Grep réel : 31 hits dans `internal/handlers/`, mais **26 dans des fichiers `_test.go`** (legit, setup tests). Les 5 hits non-test sont tous légitimes :
|
||||
- `handlers/status_handler.go:184` — probe health externe, `ctx` dédié 400ms
|
||||
- `handlers/playback_websocket_handler.go:{142,218,245}` — pumps WebSocket (doivent survivre au cycle HTTP request, pas de parent ctx disponible post-Upgrade)
|
||||
- `handlers/health.go:422` — health check 5s, `ctx` dédié
|
||||
|
||||
Le chiffre "31" masquait des patterns corrects. **Aucun handler qui défait un timeout middleware**. Pas de travail à faire.
|
||||
|
||||
**#5 — "Ajouter CSP + X-Frame-Options headers"**
|
||||
Vérification `veza-backend-api/internal/middleware/security_headers.go` : le middleware existe déjà (BE-SEC-011 + MOD-P2-005) et couvre **tous** les headers OWASP A05 recommandés :
|
||||
- `Strict-Transport-Security` (prod only)
|
||||
- `X-Frame-Options: DENY` (default) / `SAMEORIGIN` (Swagger)
|
||||
- `Content-Security-Policy` — strict `default-src 'none'` par défaut, override Swagger
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-XSS-Protection`, `Referrer-Policy`, `Permissions-Policy`
|
||||
- `X-Permitted-Cross-Domain-Policies: none`
|
||||
- `Cross-Origin-{Embedder,Opener,Resource}-Policy`
|
||||
|
||||
Audit erroné. Pas de travail à faire.
|
||||
|
||||
**#10 — "Consolider `RespondWithAppError` dupliqué"**
|
||||
Vérification :
|
||||
- `internal/response/response.go:101` = implémentation réelle (17 lignes)
|
||||
- `internal/handlers/error_response.go:12` = wrapper **intentionnel** de 3 lignes qui délègue à `response.RespondWithAppError(c, appErr)`. Commenté `// Délègue au package response pour éviter duplication`.
|
||||
|
||||
Le wrapper existe pour permettre aux handlers d'importer depuis le package `handlers` sans traverser la frontière `response/` — pattern de couplage sain. Pas une duplication à consolider. Pas de travail à faire.
|
||||
|
||||
### 9.3 Chemin critique vers v1.0.7 final stable
|
||||
|
||||
> **Mise à jour 2026-04-23** — le plan 5-jours original a été compressé en 1 session (cleanup + BFG + transactions + wiring). Ne reste que l'item doc.
|
||||
|
||||
| Jour (historique) | Tâches planifiées v1 | Statut 2026-04-23 |
|
||||
| :-: | --- | --- |
|
||||
| J1 | Items #1, #2, #6, #7 — cleanup + rotation + BFG + retag | ✅ DONE |
|
||||
| J2 | Items #4, #10, #12, #13 | ⚠️ #4/#10 false-positive · ✅ #12/#13 done |
|
||||
| J3-4 | Item #3 — transactions marketplace | ✅ DONE (commit `b5281bec`) |
|
||||
| J5 | Items #5, #11, #15 + tag `v1.0.7` | ⚠️ #5 false-positive · ✅ #11 done · 📝 #15 reste (0.5j) |
|
||||
|
||||
**Reste à faire avant tag `v1.0.7` final** : item #15 (`docs/ENV_VARIABLES.md` sync) — **0.5j**. Et un quick-win 5min : ajouter `HLS_STREAMING` à `.env.template` (cf. FUNCTIONAL_AUDIT §4 stabilité item 5).
|
||||
|
||||
Ensuite v1.0.8 : OpenAPI typegen (#8, 5j), E2E CI (#14, 3j), item G subscription `pending_payment` (parké dans `docs/audit-2026-04/v107-plan.md`), wire MinIO/S3 dans path upload (2-3j, cf. FUNCTIONAL §4 item 2), STUN/TURN WebRTC si calls public (1-2j).
|
||||
|
||||
---
|
||||
|
||||
## 10. Verdict final
|
||||
|
||||
> **v2 (2026-04-20)** — application solide, dépôt sale.
|
||||
> **v3 (2026-04-23, post-cleanup + BFG)** — **application solide, dépôt propre**.
|
||||
|
||||
- **Code applicatif** : mature, testé (286 tests front + 364 back), sécurisé (gitleaks/govulncheck/trivy, JWT RS256, 2FA, OAuth, CORS strict, CSRF, DDoS rate limit), plomberie monétaire auditée (ledger-health gauges, reconciliation, idempotency, reverse-charge). **Transactions marketplace `DELETE+loop` atomiques depuis `b5281bec`**. **UserRateLimiter wired dans `AuthMiddleware` depuis `ebf3276d`**.
|
||||
- **Code infra** : 3 variants Dockerfile (dev/prod), K8s avec disaster recovery, 5 workflows CI actifs (+ 19 disabled archivés `docs/archive/workflows/`), 6 compose env pinned (MinIO daté), HAProxy blue-green.
|
||||
- **Hygiène repo** : 2.3 GB → **66 MB** `.git` après BFG 2026-04-23 (−97%). Binaires Go, PNG racine, `.playwright-mcp`, audio uploads, `.env*`, TLS certs, kubectl vendoré, builds Incus, reports lint : **tous stripped de l'historique** + ajoutés à `.gitignore` (blocks J1 + J2 + J3).
|
||||
|
||||
**Score** : v1 disait "Moyen-Haute dette". v2 : "Basse dette code / Haute dette hygiène". **v3 : dette résiduelle mineure** — 1 item pending (`docs/ENV_VARIABLES.md`, 0.5j) + 3 false-positives classés + 2 deferrals v1.0.8.
|
||||
|
||||
**En une phrase** : **`v1.0.7-rc1` est prêt à devenir `v1.0.7` final** dès que `docs/ENV_VARIABLES.md` est synchronisé avec les 99 env vars du code. Le reste (OpenAPI typegen, E2E CI, MinIO upload path, STUN/TURN) part sur v1.0.8 avec des plans séparés.
|
||||
|
||||
---
|
||||
|
||||
## Annexe — diff v1 ↔ v2 ↔ v3
|
||||
|
||||
| Thème | v1 (2026-04-14) | v2 (2026-04-20) | v3 (2026-04-23, post-cleanup + BFG) |
|
||||
| -------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| HEAD | `45662aad1` (v1.0.0-mvp-24-g45662aad1) | `89a52944e` (v1.0.7-rc1) | post-BFG : main `6d51f52a`, chore `b5281bec` |
|
||||
| Finding "chemin critique v1.0.5 public-ready"| 6 items listés | **Tous les 6 traités** (v1.0.5 → v1.0.7-rc1, 50+ commits) | — |
|
||||
| 🔴 Player/écoute audio | Bloqueur | Résolu — endpoint `/tracks/:id/stream` + Range bypass | — |
|
||||
| 🔴 IsVerified hardcoded | Bloqueur | Résolu — `core/auth/service.go:200` `IsVerified: false` | — |
|
||||
| 🟡 SMTP silent fail | Bloqueur | Résolu — schema unifié + MailHog default | — |
|
||||
| 🟡 Marketplace dev bypass | Bloqueur | Résolu — fail-closed prod via `Config.Validate:908-910` | — |
|
||||
| 🟡 Refund stub | Bloqueur | Résolu — 3-phase + idempotency + webhook reverse-charge | — |
|
||||
| 🟡 Chat multi-instance silent | Bloqueur | Résolu — log ERROR loud `chat_pubsub.go:23-27` | — |
|
||||
| 🟡 Maintenance mode in-memory | Bloqueur | Résolu — persisté `platform_settings` TTL 10s | — |
|
||||
| 🔵 Reconciliation Hyperswitch | Absent | **Nouveau** — `reconcile_hyperswitch.go:55-150` | — |
|
||||
| 🔵 Webhook raw payload audit | Absent | **Nouveau** — `webhook_log.go:34-80` + cleanup 90j | — |
|
||||
| 🔵 Ledger-health metrics | Absent | **Nouveau** — 5 gauges + 3 alertes + Grafana | — |
|
||||
| 🔵 Stripe Connect reversal async | Absent | **Nouveau** — `reversal_worker.go:12-180` | — |
|
||||
| 🔵 Self-service creator upgrade | Absent | **Nouveau** — `POST /users/me/upgrade-creator` | — |
|
||||
| Hygiène `.git` 2.3 GB | Bloqueur | **Non traité** | ✅ **66 MB après BFG** (−97%) |
|
||||
| Hygiène binaires tracked | 3 binaires | 1 reste (`api` 99 MB racine) | ✅ **0 binaires** (BFG pass + `.gitignore` J3) |
|
||||
| Hygiène `uploads/*.mp3` 44 fichiers | Présent | **Non traité** | ✅ **stripped** (BFG pass, `uploads/` gitignoré J2) |
|
||||
| Hygiène 54 PNG racine | Présent | 48 restent | ✅ **stripped** (BFG pass, patterns gitignorés J2+J3) |
|
||||
| TLS certs committés + `.env*` | Présent | Présent | ✅ **stripped** (BFG pass) |
|
||||
| Transactions marketplace | Non auditée | 🔴 CRIT flaggée | ✅ **fixées** (commit `b5281bec`) |
|
||||
| UserRateLimiter | Non mentionné | Configuré mais non câblé | ✅ **wiré** (commit `ebf3276d`) |
|
||||
| Orphelin `internal/repository/` | Non mentionné | Flaggé | ✅ **supprimé** (commit `172581ff`) |
|
||||
| Orphelins Rust (`proto/chat`, `veza-common/{chat,ws}.rs`) | Non mentionné | Flaggé | ✅ **supprimés** (commit `172581ff`) |
|
||||
| Runbooks k8s outdated (chat Rust) | 7+ runbooks | **0 référence** — clean | — |
|
||||
| CLAUDE.md précis | Faux | **À jour** sauf Vite 5→7 | — |
|
||||
| Site Docusaurus `ORIGIN/` | À réécrire | **22 fichiers FOSSILE encore** — à archiver | (hors scope cleanup) |
|
||||
| Workflows CI | `.github/workflows/*` non consolidé | Consolidé (`ci.yml`) + **19 disabled qui traînent** | ✅ **19 archivés** dans `docs/archive/workflows/` |
|
||||
| `docs/audit-2026-04/` | Absent | **Nouveau** — axis-1-correctness + v107-plan | — |
|
||||
|
||||
**Score global** : v1 "Moyen-Haute dette" → v2 "Basse dette code / Haute dette hygiène" → **v3 "dette résiduelle mineure" (1 item pending, 3 false-positives classés, 2 deferrals v1.0.8)**.
|
||||
|
||||
---
|
||||
|
||||
*Généré par Claude Code Opus 4.7 (1M context, /effort max, /plan) — 5 agents Explore parallèles (frontend, backend Go, Rust stream, infra/DevOps, dette transverse) + mesures macro directes (du, ls, git ls-files) + lecture `CHANGELOG.md` v1.0.5→v1.0.7-rc1 + `docs/audit-2026-04/v107-plan.md`. Cross-référencé avec [FUNCTIONAL_AUDIT.md v2](FUNCTIONAL_AUDIT.md) pour les verdicts fonctionnels.*
|
||||
1967
CHANGELOG.md
1967
CHANGELOG.md
File diff suppressed because it is too large
Load diff
468
CLAUDE.md
468
CLAUDE.md
|
|
@ -1,468 +0,0 @@
|
|||
# CLAUDE.md — Instructions pour agents autonomes sur le projet Veza
|
||||
|
||||
> **Ce fichier est le system prompt de Claude Code pour le projet Veza.**
|
||||
> Il est lu automatiquement à chaque session.
|
||||
>
|
||||
> **Dernière mise à jour** : 2026-04-26 (v1.0.8, post-orval+E2E-CI session).
|
||||
> Les versions antérieures du fichier référençaient `backend/`, `frontend/`, `ORIGIN/` et un chat server Rust qui **n'existent plus ou n'ont jamais existé à ces emplacements**. Voir §Historique à la fin.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Identité
|
||||
|
||||
Tu es l'architecte-développeur principal du projet **Veza**, une plateforme de streaming musical éthique. Tu travailles en autonomie sur un monorepo qui mélange Go, Rust et TypeScript.
|
||||
|
||||
Tu es expert en :
|
||||
|
||||
- **Go** (backend API — Gin, GORM, hexagonal-ish)
|
||||
- **Rust** (stream server — Axum, Tokio, Symphonia)
|
||||
- **TypeScript/React** (frontend — Vite 5, React 18, Zustand, React Query)
|
||||
- **PostgreSQL, Redis, Elasticsearch, RabbitMQ** (infra)
|
||||
- **Docker, GitHub Actions / Forgejo Actions** (DevOps)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture réelle du repo (à jour 2026-04-26)
|
||||
|
||||
```
|
||||
veza/
|
||||
├── apps/
|
||||
│ └── web/ # Frontend React 18 + Vite 5 + TypeScript strict
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # UI + design system (~145 composants)
|
||||
│ │ ├── features/ # Modules métier (auth, library, player, chat, live, ...)
|
||||
│ │ ├── pages/ # Entry points de routes
|
||||
│ │ ├── router/ # routeConfig.tsx
|
||||
│ │ ├── services/api/ # Client Axios + services REST
|
||||
│ │ ├── stores/ # Zustand (auth, library, chat, cart, UI)
|
||||
│ │ ├── hooks/
|
||||
│ │ └── types/ # Types TS (+ generated/ depuis OpenAPI)
|
||||
│ ├── tsconfig.json # strict + noUncheckedIndexedAccess
|
||||
│ ├── vite.config.ts
|
||||
│ └── package.json
|
||||
│
|
||||
├── veza-backend-api/ # Backend Go 1.25 + Gin
|
||||
│ ├── cmd/
|
||||
│ │ ├── api/main.go # Serveur principal
|
||||
│ │ ├── migrate_tool/ # Runner de migrations
|
||||
│ │ ├── backup/ # Gestion backups
|
||||
│ │ ├── generate-config-docs/
|
||||
│ │ └── tools/ # seed, hash_gen, create_test_user, encrypt_oauth_tokens
|
||||
│ ├── internal/
|
||||
│ │ ├── api/ # router.go + routes_*.go (28 fichiers)
|
||||
│ │ ├── core/ # domain services (auth, track, marketplace, ...)
|
||||
│ │ ├── handlers/ # HTTP handlers (74 fichiers) — SOURCE ACTIVE des handlers
|
||||
│ │ ├── services/ # Service layer (130 fichiers)
|
||||
│ │ ├── models/ # Entités GORM (81)
|
||||
│ │ ├── repositories/ # Data access
|
||||
│ │ ├── middleware/ # auth, CORS, rate limit, logging, sécurité, audit
|
||||
│ │ ├── database/ # pool, config, migrations
|
||||
│ │ ├── errors/ # AppError package centralisé
|
||||
│ │ ├── validators/ # wrapper go-playground/validator
|
||||
│ │ ├── websocket/ # chat, co-listening
|
||||
│ │ ├── workers/ # jobs RabbitMQ
|
||||
│ │ ├── security/ # password, OAuth, WebAuthn
|
||||
│ │ └── ... # (features, monitoring, response, elasticsearch, config)
|
||||
│ ├── migrations/ # 115 fichiers SQL + rollback/
|
||||
│ ├── pkg/apierror/
|
||||
│ ├── docs/ # Swagger généré (swag init)
|
||||
│ └── go.mod # Go 1.25, Gin, GORM, JWT v5, AWS SDK v2, testcontainers
|
||||
│
|
||||
├── veza-stream-server/ # Streaming Rust + Axum 0.8 + Tokio 1.35
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs
|
||||
│ │ ├── lib.rs
|
||||
│ │ ├── routes/ # REST endpoints (HLS, encoding, transcode)
|
||||
│ │ ├── streaming/ # hls.rs, websocket.rs, adaptive.rs, protocols/
|
||||
│ │ │ # ⚠️ DASH/WebRTC stubbed (commentés mod.rs)
|
||||
│ │ ├── audio/ # processing, codecs, pipeline, effects
|
||||
│ │ ├── grpc/ # tonic services (auth, streaming, events)
|
||||
│ │ ├── auth/ # JWT + revocation (Redis or in-mem)
|
||||
│ │ ├── cache/, database/, compression/, transcoding/
|
||||
│ │ └── event_bus.rs # RabbitMQ avec fallback degraded mode
|
||||
│ └── Cargo.toml # Axum 0.8, Tokio 1.35, Symphonia 0.5, sqlx 0.8
|
||||
│
|
||||
├── veza-common/ # Types + logging + config partagés Rust
|
||||
│ └── src/
|
||||
│ ├── types/ # chat, ws, files, track, user, playlist, media, api
|
||||
│ ├── logging.rs # LoggingConfig utilisé par stream server
|
||||
│ └── auth.rs, metrics.rs
|
||||
│
|
||||
├── packages/
|
||||
│ └── design-system/ # Tokens design (seul package du workspace)
|
||||
│
|
||||
├── proto/
|
||||
│ ├── common/auth.proto # AuthService (utilisé gRPC stream↔backend)
|
||||
│ ├── stream/stream.proto # StreamService
|
||||
│ └── chat/chat.proto # ⚠️ SPEC HISTORIQUE — le chat est en Go
|
||||
│
|
||||
├── docs/
|
||||
│ ├── API_REFERENCE.md # ⚠️ maintenance manuelle, risque drift
|
||||
│ ├── ENV_VARIABLES.md # À maintenir
|
||||
│ ├── ONBOARDING.md # Setup dev
|
||||
│ ├── PROJECT_STATE.md # État courant
|
||||
│ ├── FEATURE_STATUS.md # Features opérationnelles
|
||||
│ ├── PRODUCTION_DEPLOYMENT.md
|
||||
│ ├── STAGING_DEPLOYMENT.md
|
||||
│ ├── SECURITY_SCAN_RC1.md
|
||||
│ └── archive/ # Retros, smoke tests, plans historiques
|
||||
│ # (v0.12.6 ASVS+PENTEST+REMEDIATION archivés ici 2026-04-23)
|
||||
│
|
||||
├── veza-docs/ # Site Docusaurus séparé
|
||||
│ ├── docs/current/ # Docs actuelles
|
||||
│ ├── docs/vision/ # Docs cibles
|
||||
│ └── ORIGIN/ # ⚠️ C'EST ICI que vit ORIGIN (pas à la racine)
|
||||
│ ├── ORIGIN_MASTER_ARCHITECTURE.md
|
||||
│ ├── ORIGIN_CODE_STANDARDS.md
|
||||
│ ├── ORIGIN_FEATURES_REGISTRY.md
|
||||
│ ├── ORIGIN_SECURITY_FRAMEWORK.md
|
||||
│ ├── ORIGIN_UI_UX_SYSTEM.md
|
||||
│ └── ...
|
||||
│
|
||||
├── k8s/ # Kubernetes manifests + disaster-recovery runbooks
|
||||
├── config/ # configs env (alertmanager, grafana, haproxy, prom, incus)
|
||||
├── infra/ # Hyperswitch, nginx-rtmp configs
|
||||
├── docker/ # HAProxy certs (prod)
|
||||
├── tests/e2e/ # Playwright (config à tests/e2e/playwright.config.ts)
|
||||
├── docker-compose.yml # Dev avec services dockerisés
|
||||
├── docker-compose.dev.yml # Infra only (apps sur l'hôte)
|
||||
├── docker-compose.prod.yml # Blue-green + haproxy + alertmanager
|
||||
├── docker-compose.staging.yml # Staging avec Caddy
|
||||
├── docker-compose.test.yml # CI (tmpfs)
|
||||
├── Makefile # include make/*.mk
|
||||
├── package.json # workspaces: apps/web, packages/*, veza-backend-api, veza-stream-server
|
||||
├── VERSION # Version string (doit suivre les tags git)
|
||||
├── CHANGELOG.md
|
||||
└── VEZA_VERSIONS_ROADMAP.md # Historique des versions (v0.9.x → v1.0.x)
|
||||
```
|
||||
|
||||
### Ce qui N'EXISTE PAS — ne pas chercher
|
||||
|
||||
- ❌ `backend/` à la racine → c'est `veza-backend-api/`
|
||||
- ❌ `frontend/` à la racine → c'est `apps/web/`
|
||||
- ❌ `ORIGIN/` à la racine → c'est `veza-docs/ORIGIN/`
|
||||
- ❌ `veza-chat-server/` → supprimé au commit `05d02386d` (2026-02-22, v0.502). Le chat est 100% côté Go backend (`internal/handlers/`, `internal/websocket/`). Les `.proto` de chat restent comme spec historique.
|
||||
- ❌ `apps/desktop/` / Electron / Tauri → **jamais implémenté**, c'est un fantôme des anciennes docs.
|
||||
- ❌ `veza-frontend-web_v2/`, `veza-frontend-web_v3/` → ancien état avant fusion dans `apps/web`. Reste un fichier `apps/web/src/types/v2-v3-types.ts` à auditer.
|
||||
|
||||
### Stack technique exacte
|
||||
|
||||
| Composant | Techno | Version pinned |
|
||||
| ------------- | ---------------------------------- | ----------------------------------------------- |
|
||||
| Backend API | Go + Gin + GORM | **Go 1.25** (bumped pour golangci-lint v2.11.4) |
|
||||
| Stream | Rust + Axum + Tokio | Axum 0.8, Tokio 1.35 |
|
||||
| Frontend | React + Vite + TS strict | React 18.2, **Vite 7.1.5**, TS 5.9.3 |
|
||||
| State front | Zustand 4.5 + React Query 5.17 | |
|
||||
| HTTP client | Axios 1.13 | |
|
||||
| OpenAPI typegen | **orval ^7** (services + RQ hooks) | `apps/web/orval.config.ts`. Source unique depuis v1.0.8 B9 — `@openapitools/openapi-generator-cli` désinstallé. |
|
||||
| Postgres | 16 | docker-compose pinned |
|
||||
| Redis | 7 | |
|
||||
| Elasticsearch | 8.11.0 | docker-compose.dev.yml uniquement (orphelin prod, search utilise Postgres FTS) |
|
||||
| RabbitMQ | 3-management | |
|
||||
| ClamAV | 1.4 | SEC-MED-003 |
|
||||
| MinIO | RELEASE.2025-09-07T16-13-09Z | 4 compose files pinned (commit `4310dbb7`) |
|
||||
| Hyperswitch | 2026.03.11.0 | |
|
||||
| JWT | RS256 prod / HS256 fallback dev | jwt v5 |
|
||||
| CI | Forgejo Actions (self-hosted R720) | `.github/workflows/{ci,e2e,go-fuzz,security-scan,trivy-fs}.yml` |
|
||||
| E2E | Playwright 1.57 (`@critical` PR / full push+nightly) | `tests/e2e/playwright.config.ts`, runbook `docs/CI_E2E.md` |
|
||||
|
||||
---
|
||||
|
||||
## 🚫 Règles immuables — jamais violer
|
||||
|
||||
Ces règles sont **absolues**. Si une tâche semble les contredire, la règle gagne.
|
||||
|
||||
1. **JAMAIS de code AI/ML** — modules F456-F470 supprimés définitivement. Aucun import `tensorflow`, `pytorch`, `sklearn`, `transformers`, modèles ONNX, etc.
|
||||
2. **JAMAIS de blockchain/Web3** — modules F491-F500 supprimés. Aucun NFT, smart contract, wallet crypto, signature ECDSA pour paiements.
|
||||
3. **JAMAIS de gamification** — modules F536-F550 supprimés. Aucun XP, streak, leaderboard, badge, level up, "points", "achievements".
|
||||
4. **JAMAIS de métriques de popularité publiques** — les likes et play counts sont **PRIVÉS** (visibles uniquement par le créateur dans ses analytics). Aucun compteur visible sur les vues publiques.
|
||||
5. **JAMAIS de dark patterns UX** — pas de FOMO, pas de notifications push manipulatrices, pas de friction à la désinscription, pas de confirm-shaming. Ref : `veza-docs/ORIGIN/ORIGIN_UI_UX_SYSTEM.md` §13.
|
||||
6. **JAMAIS modifier les fichiers `veza-docs/ORIGIN/**/\*.md`\*\* — ils sont la spécification de référence, pas du code. Tu implémentes, tu ne modifies pas la spec.
|
||||
7. **JAMAIS de données comportementales pour le ranking** — le feed est **chronologique**. La découverte est par tags/genres **déclaratifs**. Pas de "tu aimeras aussi" basé sur l'historique.
|
||||
8. **TOUJOURS propager `context.Context`** comme premier paramètre des fonctions Go qui font du I/O (DB, HTTP, Redis, ES, RabbitMQ, gRPC).
|
||||
9. **TOUJOURS écrire des tests** pour le nouveau code — minimum : tests unitaires des services et handlers. Intégration si l'infra est touchée.
|
||||
10. **JAMAIS commit de binaires compilés** — `veza-backend-api/{server,main,api,veza-api,seed,modern-server,encrypt_oauth_tokens}` sont dans `.gitignore`. Si tu crées un binaire pour tests, ne l'ajoute pas à git.
|
||||
11. **JAMAIS commit de rapports générés** — `coverage*.out`, `lint_report*.json`, `tsc_*.log`, `storybook_*.json` sont ignorés. Ils vivent en local ou dans les artifacts CI, pas en git.
|
||||
12. **JAMAIS commit de docs de session** — les `RESUME_*.md`, `PLAN_V*.md`, `AUDIT_*.md`, `FIX_*.md`, `PROGRES_*.md`, etc. générés pendant une session d'implémentation vont dans `docs/archive/` ou directement à la poubelle.
|
||||
|
||||
---
|
||||
|
||||
## 📐 Conventions de code
|
||||
|
||||
### Go (backend)
|
||||
|
||||
- Framework : **Gin**
|
||||
- ORM : **GORM**
|
||||
- Error package centralisé : [`internal/errors`](veza-backend-api/internal/errors) — `AppError{Code, Message, Err, Details, Context}`, utilisé via `RespondWithAppError(c, err)`
|
||||
- Validation : `go-playground/validator/v10` via [`internal/validators`](veza-backend-api/internal/validators)
|
||||
- Format réponse d'erreur :
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "RESOURCE_NOT_FOUND",
|
||||
"message": "Track 123 not found",
|
||||
"context": { "track_id": "123" }
|
||||
}
|
||||
}
|
||||
```
|
||||
- Format réponse paginée :
|
||||
```json
|
||||
{
|
||||
"data": [...],
|
||||
"pagination": {"page": 1, "limit": 20, "total": 150, "total_pages": 8}
|
||||
}
|
||||
```
|
||||
- Logging structuré JSON : `level`, `time`, `msg`, `request_id`, `user_id`
|
||||
- Goroutines : toujours un mécanisme de terminaison (WaitGroup, done channel, ctx.Done())
|
||||
- JWT : **RS256 en prod** (clés RSA), fallback HS256 dev. Access token 5min, refresh 7j. Cookies httpOnly.
|
||||
- Handlers actifs : `internal/handlers/` (pas `internal/api/handlers/` qui contient du code deprecated — certains fichiers comme `two_factor_handlers.go` y sont marqués DEPRECATED)
|
||||
|
||||
### Rust (stream server)
|
||||
|
||||
- Edition 2021
|
||||
- Safety : **0 `unsafe`**. Ne pas introduire de code unsafe sans justification extrême.
|
||||
- Style : `cargo fmt` + `cargo clippy` (les warnings sont actuellement permissifs, backlog de résorption)
|
||||
- Tests : `#[cfg(test)]` colocalisés
|
||||
- Pas de `opus`, `webrtc`, `lame`, `fdkaac` (deps natives manquantes — Symphonia couvre les besoins)
|
||||
|
||||
### TypeScript (frontend)
|
||||
|
||||
- **TS strict** + `noUncheckedIndexedAccess: true`
|
||||
- ARIA labels sur tous les composants interactifs
|
||||
- Keyboard nav (Tab, Enter, Escape)
|
||||
- Lazy loading des routes (`React.lazy` + `Suspense`) — registry dans [`src/components/ui/LazyComponent.tsx`](apps/web/src/components/ui/LazyComponent.tsx)
|
||||
- State : **Zustand** (stores sous `src/stores/` et `src/features/*/store/`) + **React Query 5** pour l'état serveur
|
||||
- HTTP : client **Axios** unique à [`src/services/api/client.ts`](apps/web/src/services/api/client.ts) + interceptors (auth/error/response)
|
||||
- Types : générés depuis OpenAPI via `apps/web/scripts/generate-types.sh` (pre-commit hook)
|
||||
- i18n : `react-i18next` 15
|
||||
- Pas de `moment` (déprécié — utiliser `date-fns@4`)
|
||||
|
||||
### API REST
|
||||
|
||||
```go
|
||||
// Conventions de routes
|
||||
router.GET("/api/v1/{resource}", handler.List) // ?page=1&limit=20
|
||||
router.GET("/api/v1/{resource}/:id", handler.Get)
|
||||
router.POST("/api/v1/{resource}", handler.Create)
|
||||
router.PUT("/api/v1/{resource}/:id", handler.Update)
|
||||
router.DELETE("/api/v1/{resource}/:id", handler.Delete)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Commandes utiles
|
||||
|
||||
```bash
|
||||
# --- Développement ---
|
||||
make dev # Backend docker + web local (mode principal)
|
||||
make dev-full # Tout local avec hot reload
|
||||
make dev-backend-api # Backend Go seul
|
||||
make dev-stream-server # Rust stream server seul
|
||||
make dev-web # Frontend Vite seul
|
||||
make doctor # Vérifie les dépendances système
|
||||
|
||||
# --- Infra seule ---
|
||||
make infra-up-dev # Postgres, Redis, RabbitMQ, ES, MinIO, ClamAV
|
||||
make infra-down # Stop infra
|
||||
|
||||
# --- Tests ---
|
||||
make test # Tous les tests
|
||||
make test-backend-api # Go unit tests
|
||||
make test-web # Vitest frontend
|
||||
make test-stream-server # Cargo test
|
||||
make lint # Linting complet (golangci-lint, ESLint, clippy)
|
||||
|
||||
# --- Backend Go spécifique ---
|
||||
cd veza-backend-api
|
||||
go test ./internal/... -short -count=1
|
||||
go test ./internal/... -short -count=1 -v -run TestXxx
|
||||
VEZA_SKIP_INTEGRATION=1 go test ./internal/... -count=1 # skip testcontainers
|
||||
go build ./...
|
||||
gofmt -l -w .
|
||||
|
||||
# --- Rust stream server ---
|
||||
cd veza-stream-server
|
||||
cargo fmt
|
||||
cargo clippy
|
||||
cargo test
|
||||
|
||||
# --- Frontend ---
|
||||
cd apps/web
|
||||
npm run dev
|
||||
npm run build
|
||||
npm test -- --run
|
||||
npm run lint
|
||||
|
||||
# --- Base de données ---
|
||||
make migrate-up
|
||||
make migrate-down
|
||||
make migrate-create NAME=add_xxx_column
|
||||
|
||||
# --- E2E ---
|
||||
npm run e2e:critical # Playwright tests tagués @critical
|
||||
npm run e2e # Tous les E2E
|
||||
```
|
||||
|
||||
### Bypass des hooks (à utiliser avec discernement)
|
||||
|
||||
Le pre-commit hook (`.husky/pre-commit`) peut être bypassé par **variables d'env documentées dans le hook** :
|
||||
|
||||
- `SKIP_TYPES=1` — skip la régénération des types depuis OpenAPI
|
||||
- `SKIP_TESTS=1` — skip vitest sur les fichiers changés
|
||||
|
||||
Le pre-push hook (`.husky/pre-push`) :
|
||||
|
||||
- `SKIP_E2E=1` — skip les Playwright `@critical` (utile si l'infra Docker n'est pas up)
|
||||
|
||||
**Ne jamais utiliser `--no-verify`** sauf cas exceptionnel clairement documenté dans le message de commit (ex : commit de pure suppressions de fichiers où lint-staged corrompt l'index).
|
||||
|
||||
---
|
||||
|
||||
## 📝 Convention de commits
|
||||
|
||||
Conventional Commits + scope :
|
||||
|
||||
```
|
||||
feat(backend): add playlist sharing by token
|
||||
fix(web): resolve feed rendering bug on iOS Safari
|
||||
refactor(stream): extract HLS manifest generator
|
||||
test(backend): add integration tests for 2FA flow
|
||||
docs: update ENV_VARIABLES.md
|
||||
chore(cleanup): archive session docs from apps/web
|
||||
ci: bump Go to 1.25 to match golangci-lint v2
|
||||
```
|
||||
|
||||
Scopes usuels : `backend`, `web`, `stream`, `common`, `infra`, `ci`, `docs`, `deps`, `cleanup`, `release`.
|
||||
|
||||
Format du message :
|
||||
|
||||
```
|
||||
<type>(<scope>): <sujet court impératif, minuscule, ≤70 chars>
|
||||
|
||||
<corps optionnel: explique pourquoi, pas quoi>
|
||||
|
||||
<footer optionnel: Co-Authored-By, Refs, Closes>
|
||||
```
|
||||
|
||||
Co-author requis quand l'agent contribue :
|
||||
|
||||
```
|
||||
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Scope du projet — ce qu'on fait, ce qu'on refuse
|
||||
|
||||
Veza est une **plateforme de streaming musical éthique** pour créateurs et auditeurs. Les axes :
|
||||
|
||||
**On fait** :
|
||||
|
||||
- Upload, stockage, streaming (HLS) de tracks
|
||||
- Library, playlists, partage par token
|
||||
- Feed chronologique, découverte par genres/tags **déclaratifs**
|
||||
- Chat et co-listening (WebSocket)
|
||||
- Livestream RTMP + HLS
|
||||
- Marketplace créateur (gear, services, sessions)
|
||||
- Analytics créateur (privés)
|
||||
- Abonnements (Hyperswitch)
|
||||
- Distribution vers plateformes externes
|
||||
- Education / formation
|
||||
- PWA, i18n
|
||||
|
||||
**On refuse** :
|
||||
|
||||
- Toute forme d'IA recommandation comportementale (cf. règle 7)
|
||||
- Popularité publique (cf. règle 4)
|
||||
- Gamification (cf. règle 3)
|
||||
- Dark patterns (cf. règle 5)
|
||||
- NFT / Web3 (cf. règle 2)
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Patterns de résolution
|
||||
|
||||
### Quand tu ne sais pas quoi faire
|
||||
|
||||
1. Lis `docs/PROJECT_STATE.md` et `docs/FEATURE_STATUS.md` pour l'état courant.
|
||||
2. Si spec : lis `veza-docs/ORIGIN/` (lecture seule).
|
||||
3. Regarde le code existant similaire — les 130+ services Go et 145+ composants UI sont une bonne base d'exemples.
|
||||
4. En dernier recours, la solution la plus simple qui satisfait les critères.
|
||||
|
||||
### Quand un test échoue
|
||||
|
||||
1. Lis l'erreur complète.
|
||||
2. Vérifie que les migrations DB sont appliquées (`make migrate-up`).
|
||||
3. Vérifie que l'infra tourne (`make infra-up-dev`).
|
||||
4. Reproduire localement, pas deviner.
|
||||
5. Fix soit le test soit le code — pas les deux en même temps.
|
||||
|
||||
### Quand tu trouves un bug existant
|
||||
|
||||
1. Fix-le si dans le scope de ta tâche actuelle.
|
||||
2. Sinon `// TODO(<scope>): description` et note dans le PR description.
|
||||
3. Ne jamais casser un test qui passait pour en faire passer un nouveau.
|
||||
|
||||
### Quand une dépendance manque
|
||||
|
||||
```bash
|
||||
# Go
|
||||
cd veza-backend-api && go get <module>@<version>
|
||||
|
||||
# Frontend
|
||||
cd apps/web && npm install <package>
|
||||
|
||||
# Rust
|
||||
cd veza-stream-server && cargo add <crate>
|
||||
```
|
||||
|
||||
Licence acceptable : MIT, Apache-2.0, BSD-2/3, ISC, MPL-2.0. **GPL interdit** dans le backend.
|
||||
|
||||
### Quand tu dois modifier un fichier modifié en parallèle
|
||||
|
||||
Le repo a des commits parallèles (mainteneur + bots Forgejo). Si `git pull` donne un conflit :
|
||||
|
||||
1. Ne jamais force-push sur `main`.
|
||||
2. Résoudre proprement, commit de résolution explicite.
|
||||
3. Si doute, demander.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Actions qui nécessitent une confirmation humaine
|
||||
|
||||
**NE JAMAIS faire sans demander** :
|
||||
|
||||
- `git push --force` ou `git push --force-with-lease` sur `main`
|
||||
- `git reset --hard` qui perd du travail
|
||||
- `git filter-repo` / purge d'historique
|
||||
- Supprimer des branches distantes (`git push --delete`)
|
||||
- Supprimer des tags distants
|
||||
- Modifier `.github/workflows/*.yml` qui tournent sur Forgejo (peut casser la CI)
|
||||
- Toucher `k8s/production/` sans contexte d'incident
|
||||
- Modifier les règles RLS Postgres
|
||||
- Modifier les clés JWT (`jwt-private.pem`, `jwt-public.pem`)
|
||||
- Modifier les secrets (`docker-compose.prod.yml` env, `.env.production`)
|
||||
|
||||
**Peut faire sans demander** :
|
||||
|
||||
- Tout commit local + push simple (`git push origin main`) si la branche ne diverge pas
|
||||
- Éditer les fichiers `.md` de documentation
|
||||
- Éditer le code applicatif (Go, Rust, TS) avec tests
|
||||
- Ajouter des migrations SQL
|
||||
- Modifier `docker-compose.dev.yml` et configs de dev
|
||||
|
||||
---
|
||||
|
||||
## 📜 Historique
|
||||
|
||||
- **2026-04-14** : Réécriture complète post-audit (v1.0.4). L'ancienne version référençait `backend/`, `frontend/`, `ORIGIN/` à la racine, un chat server Rust et un desktop Electron qui n'existaient pas ou plus. Voir `AUDIT_REPORT.md` pour le détail.
|
||||
- **2026-02-22** (commit `05d02386d`) : suppression de `veza-chat-server/` (chat intégré au backend Go depuis v0.502).
|
||||
- **2026-03-03** : release `v1.0.0`.
|
||||
- **2026-03-13** : tag `v1.0.2`.
|
||||
- **2026-04-14** : tag `v1.0.3` existant, cible `v1.0.4` pour la release post-cleanup.
|
||||
- **2026-04-23** : release `v1.0.7` (BFG history rewrite, .git 2.3 GB → 66 MB, transactions marketplace, UserRateLimiter wired).
|
||||
- **2026-04-26** : release `v1.0.8` (MinIO storage end-to-end, OpenAPI orval migration, drop `@openapitools/openapi-generator-cli` legacy generator, E2E Playwright workflow + `--ci` seed flag, queue+password handler annotations, full authService → orval).
|
||||
|
||||
---
|
||||
|
||||
_Source de vérité pour le comportement de Claude Code sur Veza. Ne jamais modifier sans commit explicite (`docs: update CLAUDE.md [raison]`)._
|
||||
|
|
@ -70,7 +70,7 @@ Exemples :
|
|||
|
||||
- `feat: add adaptive HLS transcoding worker`
|
||||
- `fix: correct JWT user_id mismatch between Go and Rust`
|
||||
- `refactor: isolate DM module in stream-server`
|
||||
- `refactor: isolate DM module in chat-server`
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
159
DEMARRAGE_SIMPLE.md
Normal file
159
DEMARRAGE_SIMPLE.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# 🚀 Démarrage Simple - Test Intégration Veza
|
||||
|
||||
## ✅ Problèmes Corrigés
|
||||
|
||||
1. ✅ Migration SQL corrigée (`050_data_validation_constraints.sql`)
|
||||
2. ✅ Redis démarré correctement
|
||||
3. ✅ Configuration backend créée (`.env`)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Démarrage en 3 Étapes
|
||||
|
||||
### Étape 1: Infrastructure Docker
|
||||
|
||||
```bash
|
||||
make infra-up
|
||||
```
|
||||
|
||||
**Vérification**:
|
||||
```bash
|
||||
docker compose ps
|
||||
# Devrait voir: postgres, redis, rabbitmq (tous "healthy")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Étape 2: Backend Go
|
||||
|
||||
```bash
|
||||
cd veza-backend-api
|
||||
|
||||
# Le fichier .env est déjà créé avec la bonne config
|
||||
# Si besoin, vérifier:
|
||||
cat .env
|
||||
|
||||
# Démarrer le serveur
|
||||
go run cmd/api/main.go
|
||||
```
|
||||
|
||||
**Vérification**:
|
||||
```bash
|
||||
# Dans un autre terminal
|
||||
curl http://localhost:8080/health
|
||||
# Devrait retourner: {"status":"ok"}
|
||||
```
|
||||
|
||||
**URLs**:
|
||||
- API: http://localhost:8080/api/v1
|
||||
- Swagger: http://localhost:8080/docs
|
||||
- Health: http://localhost:8080/health
|
||||
|
||||
---
|
||||
|
||||
### Étape 3: Frontend React
|
||||
|
||||
```bash
|
||||
cd apps/web
|
||||
|
||||
# Démarrer Vite
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Vérification**:
|
||||
- Ouvrir http://localhost:3000 dans le navigateur
|
||||
- La page devrait se charger
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Test Complet
|
||||
|
||||
1. **Ouvrir** http://localhost:3000
|
||||
2. **Tester Register**:
|
||||
- Créer un compte
|
||||
- Vérifier que ça fonctionne
|
||||
3. **Tester Login**:
|
||||
- Se connecter
|
||||
- Vérifier DevTools → Network → Headers
|
||||
- Devrait voir `Authorization: Bearer <token>`
|
||||
- Devrait voir `X-CSRF-Token: <token>` sur les mutations
|
||||
4. **Tester API**:
|
||||
- Ouvrir http://localhost:8080/docs
|
||||
- Tester un endpoint depuis Swagger UI
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Backend (`veza-backend-api/.env`)
|
||||
|
||||
```bash
|
||||
APP_ENV=development
|
||||
JWT_SECRET=dev-secret-key-minimum-32-characters-long-for-testing
|
||||
DATABASE_URL=postgres://veza:password@localhost:5432/veza?sslmode=disable
|
||||
REDIS_URL=redis://localhost:6379
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
|
||||
APP_PORT=8080
|
||||
LOG_LEVEL=INFO
|
||||
RABBITMQ_URL=amqp://veza:password@localhost:5672/
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
Aucune configuration nécessaire - valeurs par défaut OK:
|
||||
- `VITE_API_URL=http://127.0.0.1:8080/api/v1` ✅
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Si Problème
|
||||
|
||||
### Backend ne démarre pas
|
||||
|
||||
```bash
|
||||
# Vérifier DB
|
||||
docker compose exec postgres psql -U veza -d veza -c "SELECT 1;"
|
||||
|
||||
# Vérifier Redis
|
||||
docker compose exec redis redis-cli ping
|
||||
|
||||
# Vérifier logs backend
|
||||
cd veza-backend-api
|
||||
go run cmd/api/main.go 2>&1 | tee backend.log
|
||||
```
|
||||
|
||||
### Frontend ne se connecte pas
|
||||
|
||||
```bash
|
||||
# Vérifier CORS
|
||||
curl -v -H "Origin: http://localhost:3000" \
|
||||
-H "Access-Control-Request-Method: GET" \
|
||||
-X OPTIONS \
|
||||
http://localhost:8080/api/v1/auth/me
|
||||
|
||||
# Devrait voir: Access-Control-Allow-Origin: http://localhost:3000
|
||||
```
|
||||
|
||||
### Port occupé
|
||||
|
||||
```bash
|
||||
# Trouver processus
|
||||
lsof -i :8080 # Backend
|
||||
lsof -i :3000 # Frontend
|
||||
|
||||
# Tuer si nécessaire
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist Finale
|
||||
|
||||
- [ ] Infrastructure Docker démarrée (`make infra-up`)
|
||||
- [ ] Backend démarré sur port 8080
|
||||
- [ ] Frontend démarré sur port 3000
|
||||
- [ ] Backend health check OK (`curl http://localhost:8080/health`)
|
||||
- [ ] Frontend accessible (http://localhost:3000)
|
||||
- [ ] Swagger accessible (http://localhost:8080/docs)
|
||||
|
||||
**Prêt à tester ! 🎉**
|
||||
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
# FUNCTIONAL_AUDIT v2 — Veza, ce qu'un utilisateur peut RÉELLEMENT faire
|
||||
|
||||
> **Date** : 2026-04-19
|
||||
> **Branche** : `main` (HEAD = `89a52944e`, `v1.0.7-rc1`)
|
||||
> **Auditeur** : Claude Code (Opus 4.7 — mode autonome, /effort max, /plan)
|
||||
> **Méthode** : 5 agents Explore en parallèle + vérifications ponctuelles directes + relecture de `docs/audit-2026-04/v107-plan.md` et `CHANGELOG.md`. **Trace statique** (pas de runtime), comme v1.
|
||||
> **Supersede** : [v1 du 2026-04-16](#6-diff-vs-audit-v1-2026-04-16). La v1 listait 1 🔴 + 9 🟡. Entre le 16 et aujourd'hui, v1.0.5 → v1.0.7-rc1 ont shippé (50+ commits, la majorité ciblant exactement les findings v1).
|
||||
> **Ton** : brutal, sans langue de bois. Citations `fichier:ligne`.
|
||||
|
||||
---
|
||||
|
||||
## 0. Résumé en 5 lignes
|
||||
|
||||
1. **Le bloqueur `🔴 Player` de la v1 est résolu.** Un endpoint direct `/api/v1/tracks/:id/stream` avec support Range (`routes_tracks.go:118-120`) sert l'audio sans HLS. Le middleware bypass cache (`response_cache.go:87-104`, commit `b875efcff`) permet le range-request. Le player frontend tombe automatiquement sur `/stream` si HLS échoue (`playerService.ts:280-293`). `HLS_STREAMING=false` reste le default (`config.go:355`) **mais ce n'est plus un blocker** : l'audio sort.
|
||||
2. **Inscription / vérification email : cassée en v1, corrigée.** `IsVerified: false` (`core/auth/service.go:200`), `VerifyEmail` endpoint réellement vivant, login gate 403 sur unverified (`service.go:527`), MailHog branché par défaut dans `docker-compose.dev.yml`, SMTP env schema unifié (commit `066144352`). Tout le parcours register → mail → click → login fonctionne.
|
||||
3. **Paiements solidifiés de façon massive.** Refund fait **reverse-charge Hyperswitch avec idempotency-key** (`service.go:1297-1436`). Reconciliation worker sweep les stuck orders/refunds/orphans (`reconcile_hyperswitch.go:55-150`). Webhook raw payload audit (`webhook_log.go`). 5 gauges Prometheus ledger-health + 3 alert rules. **Dev bypass persiste** (simulated payment si `HYPERSWITCH_ENABLED=false`, `service.go:550-586`) **mais `Config.Validate` refuse de booter en prod** sans Hyperswitch (`config.go:908-910`). Fail-closed en prod, fail-open en dev.
|
||||
4. **Points rugueux restants** : (a) **WebRTC 1:1 sans STUN/TURN** — signaling ✅ mais NAT traversal HS en prod ; (b) **Stockage local disque only** — le code S3/MinIO existe mais n'est pas wiré dans l'upload path ; (c) **HLS toujours off par défaut** → pas d'adaptive bitrate out-of-the-box ; (d) **Transcoding dual-trigger** (gRPC Rust + RabbitMQ) — redondance non documentée.
|
||||
5. **Verdict** : Veza v1.0.7-rc1 est prêt pour une **démo publique contrôlée** (un seul pod, infra dev, Hyperswitch sandbox). Pour un **déploiement prod multi-pod avec utilisateurs réels** il manque : MinIO wiré, STUN/TURN pour les calls, et la documentation d'exploitation des gauges ledger-health. La surface "un utilisateur lambda peut register → verify → upload → play → acheter → rembourser" est **entièrement opérationnelle**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Tableau des features — verdict réel au 2026-04-19
|
||||
|
||||
Légende : **✅ COMPLET** câblé de bout-en-bout · **🟡 PARTIEL** gotchas exploitables · **🔴 FAÇADE** UI sans backend réel · **⚫ ABSENT**.
|
||||
|
||||
| # | Feature | Verdict | v1 | Détail + citation |
|
||||
| --- | ---------------------------------------------------------------- | :-----: | :-: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Register / Login / JWT / Refresh | ✅ | 🟡 | `IsVerified: false` (`core/auth/service.go:200`). Login 403 si unverified (`service.go:527`). JWT RS256 prod / HS256 dev. |
|
||||
| 2 | Verify email | ✅ | 🔴 | `POST /auth/verify-email` actif (`routes_auth.go:103-107`). Token généré + stocké en DB, email envoyé via MailHog par défaut. |
|
||||
| 3 | Forgot / Reset password | ✅ | 🟡 | `password_reset_handler.go:67-250`. Token en DB avec expiry, invalide toutes les sessions à l'usage. |
|
||||
| 4 | 2FA TOTP | ✅ | ✅ | `internal/handlers/two_factor_handler.go:171`. Obligatoire pour admin. |
|
||||
| 5 | OAuth (Google/GitHub/Discord/Spotify) | ✅ | ✅ | `routes_auth.go:122-176`. |
|
||||
| 6 | Profils utilisateur + slug / username | ✅ | ✅ | `profile_handler.go:102`. |
|
||||
| 7 | Upload de tracks | 🟡 | 🟡 | ClamAV sync ✅ (fail-secure par défaut, `upload_validator.go:87-88`). **Stockage local disque** (`track_upload_handler.go:376`). Dual trigger transcoding (gRPC + RabbitMQ) non doc. |
|
||||
| 8 | CRUD Tracks / Library | ✅ | ✅ | List / filtres / pagination réels. Library filtrée sur `status=Completed`. |
|
||||
| 9 | **Player + Queue + écoute audio** | ✅ | 🔴 | **🔴 → ✅** : `/tracks/:id/stream` avec Range (`routes_tracks.go:118-120`, `track_hls_handler.go:266`). Cache bypass wiré (`response_cache.go:87-104`). HLS optionnel, off par défaut. |
|
||||
| 10 | Playlists (CRUD + share par token) | ✅ | ✅ | `playlist_handler.go:43`. |
|
||||
| 11 | Queue collaborative (host-authority) | ✅ | ✅ | `queue_handler.go`. |
|
||||
| 12 | Chat WebSocket (messages, typing, reactions, attachments) | ✅ | 🟡 | DB persist avant broadcast (`handler_messages.go:91-113`). 12 features wirées (edit/delete/typing/read/delivered/reactions/attachments/search/convos/channel/DM/calls). |
|
||||
| 13 | Chat multi-instance | ✅ | 🟡 | **🟡 → ✅** : Redis pubsub + fallback in-memory **avec log ERROR loud** (`chat_pubsub.go:23-27, 48`). Plus de silent fail. |
|
||||
| 14 | WebRTC 1:1 calls | 🟡 | 🟡 | Signaling ✅ (`handler.go:89-98`). **STUN/TURN absent** — pas d'env var, pas de grep hit. NAT symétrique = call HS. |
|
||||
| 15 | Co-listening (listen-together) | ✅ | ✅ | `colistening/hub.go:104-148`, host-authority, keepalive 30s. |
|
||||
| 16 | **Livestream (RTMP ingest)** | ✅ | 🟡 | **🟡 → ✅** : `/api/v1/live/health` (`live_health_handler.go:78-96`) + banner UI (`useLiveHealth.ts:41-61`, commit `64fa0c9ac`). Plus de silent OBS fail. |
|
||||
| 17 | Livestream viewer playback | ✅ | ✅ | HLS via nginx-rtmp (`live_stream_callback.go:66`). URL dans `streamURL`. |
|
||||
| 18 | Dashboard | ✅ | ✅ | `/api/v1/dashboard`. |
|
||||
| 19 | Recherche (unifiée + tracks) | ✅ | ✅ | `search_handlers.go:41` — ES puis fallback Postgres LIKE + pg_trgm. |
|
||||
| 20 | Social / Feed / Posts / Groups | ✅ | ✅ | `social.go:161`, chronologique. |
|
||||
| 21 | Discover (genres/tags déclaratifs) | ✅ | ✅ | `discover.go:49-63`. |
|
||||
| 22 | Presence + rich presence | ✅ | ✅ | `presence_handler.go:30-46`. |
|
||||
| 23 | Notifications + Web Push | ✅ | ✅ | `notification_handlers.go:197`. |
|
||||
| 24 | **Marketplace + checkout** | ✅ | 🟡 | Hyperswitch wiré (`service.go:522-548`). **Simulated payment si dev** (`:550-586`) **mais `Config.Validate` refuse prod sans Hyperswitch** (`config.go:908-910`). Cart côté server ✅. |
|
||||
| 25 | **Refund (reverse-charge)** | ✅ | 🟡 | **🟡 → ✅** : 3 phases avec idempotency-key `refund.ID` (`service.go:1297-1436`, commits `4f15cfbd9` `959031667`). Webhook handler wiré. |
|
||||
| 26 | Hyperswitch reconciliation sweep | ✅ | ⚫ | **⚫ → ✅** (nouveauté v1.0.7) : `reconcile_hyperswitch.go:55-150` couvre stuck orders/refunds/orphans, 10 tests green. |
|
||||
| 27 | Webhook raw payload audit log | ✅ | ⚫ | **⚫ → ✅** (v1.0.7) : `webhook_log.go:34-80` + cleanup 90j (`cleanup_hyperswitch_webhook_log.go`). |
|
||||
| 28 | Ledger-health metrics + alerts | ✅ | ⚫ | **⚫ → ✅** (v1.0.7 item F) : 5 gauges Prometheus + 3 alert rules Alertmanager + dashboard Grafana. |
|
||||
| 29 | Seller dashboard + Stripe Connect payout | ✅ | ✅ | `sell_handler.go`, transfer auto post-webhook. |
|
||||
| 30 | **Stripe Connect reversal (async)** | ✅ | 🟡 | **🟡 → ✅** (v1.0.7 items A+B) : `reversal_worker.go:12-180`, state machine `reversal_pending`, `stripe_transfer_id` persisté, exp. backoff 1m→1h. |
|
||||
| 31 | Reviews / Factures | ✅ | ✅ | DB + handlers wirés. |
|
||||
| 32 | Subscription plans | ✅ | 🟡 | **🟡 → ✅** (v1.0.6.2 hotfix `d31f5733d`) : `hasEffectivePayment()` gate (`subscription/service.go:140-155`). Plus de bypass. |
|
||||
| 33 | Distribution plateformes externes | ✅ | ✅ | `distribution_handler.go:32-62`. |
|
||||
| 34 | Formation / Education | ✅ | ✅ | `education_handler.go:33` — DB-backed. |
|
||||
| 35 | Support tickets | ✅ | ✅ | `support_handler.go:54-100`. |
|
||||
| 36 | Developer portal (API keys + webhooks) | ✅ | ✅ | `routes_developer.go:11`. |
|
||||
| 37 | Analytics (creator stats) | ✅ | ✅ | `playback_analytics_handler.go`, CSV/JSON export. |
|
||||
| 38 | Admin — dashboard / users / modération / flags / audit | ✅ | 🟡 | `admin/handler.go:43-54`. **Maintenance mode 🟡 → ✅** via `platform_settings` + TTL 10s (`middleware/maintenance.go:16-100`, commit `3a95e38fd`). |
|
||||
| 39 | Admin — transfers (v0.701) | ✅ | ✅ | `admin_transfer_handler.go:36-91`. |
|
||||
| 40 | Self-service creator role upgrade | ✅ | ⚫ | **⚫ → ✅** (commit `c32278dc1`) : `POST /users/me/upgrade-creator` gate email-verified, idempotent. |
|
||||
| 41 | Upload-size SSOT | ✅ | ⚫ | **⚫ → ✅** (commit `5848c2e40`) : `config/upload_limits.go` + `GET /api/v1/upload/limits` consommé par `useUploadLimits` côté web. |
|
||||
| 42 | Tag suggestions | ✅ | ✅ | `tag_handler.go:15-32`. |
|
||||
| 43 | PWA (install + service worker + wake lock) | ✅ | ✅ | `components/pwa/`, v0.801. |
|
||||
| 44 | Orphan tracks cleanup | ✅ | ⚫ | **⚫ → ✅** (commit `553026728`) : `jobs/cleanup_orphan_tracks.go`, hourly, flip `processing`→`failed` si fichier disque manquant. |
|
||||
| 45 | Stem upload & sharing (F482) | ✅ | ✅ | `routes_tracks.go:185-189`, ownership guard. |
|
||||
|
||||
**Score** : 43 ✅ / 2 🟡 / 0 🔴 / 0 ⚫. La seule 🔴 de la v1 (Player/écoute audio) est résolue.
|
||||
|
||||
**Les 2 🟡 restants** : **Upload** (stockage local disque → pas prêt pour production scale) et **WebRTC 1:1** (pas de STUN/TURN → NAT traversal HS).
|
||||
|
||||
---
|
||||
|
||||
## 2. Les 6 parcours — étape par étape
|
||||
|
||||
### Parcours 1 — Écouter de la musique
|
||||
|
||||
**Verdict : ✅ OPÉRATIONNEL.** Le bloqueur v1 est résolu — le fallback direct stream existe.
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | ------------------------ | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Créer un compte | ✅ | `POST /auth/register` → `core/auth/service.go:104-469`. `IsVerified: false` (`:200`), token en DB. |
|
||||
| 2 | Recevoir l'email | ✅ | MailHog par défaut dans `docker-compose.dev.yml:114-130`. UI sur port 8025. Prod : 500 hard si SMTP down (`service.go:387`). |
|
||||
| 3 | Cliquer le lien verify | ✅ | `POST /auth/verify-email?token=X` → `core/auth/service.go:747-765` check token + flip `is_verified=true`. |
|
||||
| 4 | Se connecter | ✅ | `POST /auth/login` → 403 Forbidden si `!IsVerified` (`service.go:527`). Lockout après 5 tentatives / 15 min. |
|
||||
| 5 | Chercher un morceau | ✅ | `GET /api/v1/search` → `search_handlers.go:41`, ES ou fallback Postgres tsvector. |
|
||||
| 6 | Lancer la lecture | ✅ | Player React tente HLS d'abord (`playerService.ts:283-293`), fallback direct `/stream`. |
|
||||
| 7 | **Le son sort ?** | ✅ | `GET /tracks/:id/stream` avec `http.ServeContent` (`track_hls_handler.go:266`), Range supporté, cache bypass wiré (`response_cache.go:87-104`). |
|
||||
|
||||
**Piège dev** : si on upload un fichier mais que le transcoding (Rust stream server) échoue, le track reste en `Processing`. Le cleanup worker hourly le flippera à `Failed` après 1h. Le fichier **reste lisible via `/stream`** pendant ce temps, mais il n'apparaît pas en library (filtre `status=Completed`).
|
||||
|
||||
### Parcours 2 — Uploader un morceau (artiste)
|
||||
|
||||
**Verdict : ✅ MAIS sur local disque.**
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | --------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Login | ✅ | Comme parcours 1. |
|
||||
| 2 | Upgrade creator (si besoin) | ✅ | `POST /api/v1/users/me/upgrade-creator` — gate email-verified, idempotent (`upgrade_creator_handler.go`). UI `AccountSettingsCreatorCard.tsx`. |
|
||||
| 3 | Uploader un fichier audio | ✅ | `POST /api/v1/tracks/upload` → `track_upload_handler.go:39-171`. Multipart, taille SSOT (`config/upload_limits.go`), ClamAV **sync** fail-secure. |
|
||||
| 4 | Stockage physique | 🟡 | **`uploads/tracks/<userID>/<filename>` sur disque local** (`track_upload_handler.go:376`). Code S3/MinIO présent mais **non wiré** dans ce chemin. |
|
||||
| 5 | Transcoding | 🟡 | **Dual-trigger** : gRPC Rust stream server (`stream_service.go:49`) **et** RabbitMQ job (`EnqueueTranscodingJob`). Redondance non documentée. |
|
||||
| 6 | Track visible en library | ✅ | Après `status=Completed`. Avant : utilisateur voit son upload en "Processing" dans son tableau de bord. |
|
||||
| 7 | Autre user peut trouver/lire| ✅ | Via search + parcours 1. Si track reste `Processing` (transcoding down) → pas en library mais `/tracks/:id/stream` sert quand même le raw. |
|
||||
|
||||
### Parcours 3 — Acheter sur le marketplace
|
||||
|
||||
**Verdict : ✅ (sandbox testing) + solidifiés massivement depuis v1.**
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | ---------------------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Browse produits | ✅ | `GET /api/v1/marketplace/products`, handlers DB réels. |
|
||||
| 2 | Ajouter au panier | ✅ | `POST /api/v1/cart/items` → `cart.go:25-97`, DB-backed (table `cart_items`). |
|
||||
| 3 | Checkout | ✅ | `POST /api/v1/orders` → `service.go:522-548` (prod flow Hyperswitch) ou `:550-586` (dev simulated). |
|
||||
| 4 | **Paiement Hyperswitch** | ✅ | `paymentProvider.CreatePayment()` avec `Idempotency-Key: order.ID` (commit `4f15cfbd9`). Retourne `client_secret` consommé par `CheckoutPaymentForm.tsx`. |
|
||||
| 5 | Webhook paiement | ✅ | `POST /api/v1/webhooks/hyperswitch` → raw payload logged (`webhook_log.go`), signature HMAC-SHA512 vérifiée, dispatcher `ProcessPaymentWebhook`. |
|
||||
| 6 | Reconciliation si webhook perdu | ✅ | `reconcile_hyperswitch.go` sweep stuck orders > 30m avec payment_id non vide, synthèse webhook → `ProcessPaymentWebhook`. Idempotent. Configurable `RECONCILE_INTERVAL=1h` (5m pendant incident). |
|
||||
| 7 | Confirmation + accès contenu | ✅ | Création licenses dans la transaction (`service.go:561-585`), lock `FOR UPDATE` pour exclusive. |
|
||||
| 8 | Remboursement | ✅ | 3-phase `service.go:1297-1436` : pending row → `CreateRefund` PSP → persist `hyperswitch_refund_id`. Webhook `refund.succeeded` révoque licenses + débite vendeur. |
|
||||
| 9 | Reverse-charge Stripe Connect | ✅ | `reversal_worker.go:12-180`, state `reversal_pending`, async, backoff 1m→1h. Rows pré-v1.0.7 sans `stripe_transfer_id` → `permanently_failed` avec message explicite. |
|
||||
|
||||
**Piège prod** : `HYPERSWITCH_ENABLED=false` = dev bypass. **Garde-fou** : `Config.Validate` refuse de booter en prod si `HYPERSWITCH_ENABLED=false` (`config.go:908-910`) — message explicite "marketplace orders complete without charging, effectively giving away products". Fail-closed au bon endroit.
|
||||
|
||||
### Parcours 4 — Chat
|
||||
|
||||
**Verdict : ✅ sur toutes les surfaces.**
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | ------------------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Ouvrir le chat | ✅ | `apps/web/src/features/chat/pages/ChatPage.tsx`. |
|
||||
| 2 | Rejoindre / créer une room | ✅ | `POST /api/v1/conversations` → `CreateRoom:54`. |
|
||||
| 3 | Envoyer un message | ✅ | WS dispatcher `handler.go:54-106` → `HandleSendMessage:18` → DB **avant** broadcast (`handler_messages.go:91-113`). |
|
||||
| 4 | Recevoir (temps réel) | ✅ | Hub local, puis PubSub pour multi-instance. |
|
||||
| 5 | Persistance | ✅ | `chat_messages` table, indexed. |
|
||||
| 6 | Multi-instance sans Redis | ✅ | Fallback in-memory **avec log ERROR loud** ("Redis unavailable, cross-instance messages will be lost") (`chat_pubsub.go:23-27`). Plus de silent fail. |
|
||||
| 7 | Typing / reactions / attach. | ✅ | 12 features wirées (voir §1 ligne 12). |
|
||||
|
||||
### Parcours 5 — Livestream
|
||||
|
||||
**Verdict : ✅ avec banner UI si RTMP down.**
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | ------------------------ | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Démarrer un live | ✅ | `POST /api/v1/live/streams` → `live_stream_handler.go:71-98`, génère `stream_key` UUID + `rtmp_url`. |
|
||||
| 2 | Push OBS → nginx-rtmp | ✅ | `on_publish` callback `live_stream_callback.go:38-80` avec secret `X-RTMP-Callback-Secret`, flip `is_live=true`. |
|
||||
| 3 | Health check visible | ✅ | `GET /api/v1/live/health` (`live_health_handler.go:78-96`) + poll 15s front (`useLiveHealth.ts:41-61`). Banner warn si `rtmp_reachable=false`.|
|
||||
| 4 | Viewer play live | ✅ | HLS via nginx-rtmp (`streamURL` = `baseURL + /{streamKey}/playlist.m3u8`). |
|
||||
| 5 | Co-listening en parallèle| ✅ | Feature séparée, `colistening/hub.go:104-148`, host-authority sync 100ms drift threshold. |
|
||||
|
||||
**Piège** : nécessite `docker compose --profile live up` pour démarrer nginx-rtmp. Sans ça, banner red immédiat. Plus de silent fail comme en v1.
|
||||
|
||||
### Parcours 6 — Admin
|
||||
|
||||
**Verdict : ✅ complet avec persistance maintenance mode.**
|
||||
|
||||
| # | Étape | Verdict | Preuve |
|
||||
| --- | ------------------------ | :-----: | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Accéder /admin | ✅ | Middleware JWT + role check, 2FA obligatoire. |
|
||||
| 2 | Voir stats | ✅ | `admin/handler.go:43-54` `GetPlatformMetrics`. |
|
||||
| 3 | Modérer (queue, bans) | ✅ | `moderation/handler.go:44` `GetModerationQueue`, ban/suspend wirés. |
|
||||
| 4 | Gérer utilisateurs | ✅ | Admin handlers (user upgrade, role change). |
|
||||
| 5 | Maintenance mode | ✅ | Persisté `platform_settings` (`middleware/maintenance.go:16-100`, TTL 10s). Survit au restart. **🟡 v1 → ✅ v2**. |
|
||||
| 6 | Feature flags | ✅ | DB-backed. |
|
||||
| 7 | Ledger health dashboard | ✅ | Grafana `config/grafana/dashboards/ledger-health.json` + 5 gauges + 3 alert rules (voir §1 ligne 28). |
|
||||
| 8 | Admin transfers | ✅ | `admin_transfer_handler.go:36-91`, manual retry, state machine persistée. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Carte des dépendances
|
||||
|
||||
### 3.1 Services — hard-required vs optionnels
|
||||
|
||||
| Service | Status | Comportement si down | Preuve |
|
||||
| -------------------- | --------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| **PostgreSQL** | 🔴 Hard-req | App panique au boot (`main.go:112-120`, migrations auto-run). | `db.Initialize()` + `RunMigrations()` fatal. |
|
||||
| **Migrations** | 🔴 Auto | Appliquées au démarrage, boot fail si erreur SQL. | `database.go:234-256`. |
|
||||
| **Redis** | 🟢 Dégradation | TokenBlacklist nil-safe. Chat PubSub fallback in-memory avec **log ERROR loud**. Rate limiter dégradé. | `chat_pubsub.go:23-27` ; `config.go:55-58`. |
|
||||
| **RabbitMQ** | 🟢 Dégradation | EventBus publish failures maintenant **loggés ERROR** (commit `bf688af35`) au lieu de silent drop. | `main.go:128-139` ; `config.go:690-693`. |
|
||||
| **MinIO / S3** | 🟢 Non utilisé | `AWS_S3_ENABLED=false` par défaut, **code S3 présent mais non wiré dans upload path**. Disque local always. | `config.go:697-720` ; `track_upload_handler.go:376`. |
|
||||
| **Elasticsearch** | 🟢 Optionnel | Search fallback Postgres full-text search (tsvector + pg_trgm). ES non utilisé en chemin chaud. | `fulltext_search_service.go:14-30` ; `main.go:288-297` (cleanup only). |
|
||||
| **ClamAV** | 🟠 Fail-secure | `CLAMAV_REQUIRED=true` par défaut → upload **rejeté** (503) si down. `=false` = bypass avec warning. | `upload_validator.go:87-88, 140-150` ; `services_init.go:27-46`. |
|
||||
| **Hyperswitch** | 🟠 Prod-gate | `HYPERSWITCH_ENABLED=false` = dev bypass. **Prod : `Config.Validate` refuse boot** si false. | `config.go:908-910` ; `service.go:522-548, 550-586`. |
|
||||
| **Stripe Connect** | 🟠 Prod-gate | Reversal worker tourne si config présente. Rows pre-v1.0.7 sans id → `permanently_failed`. | `reversal_worker.go:12-180` ; `main.go:188`. |
|
||||
| **Nginx-RTMP** | 🟢 Profil live | `docker compose --profile live up`. Si down : banner UI immédiat sur Go Live page. | `live_health_handler.go:78-96` ; `useLiveHealth.ts:41-61`. |
|
||||
| **Rust stream srv** | 🟢 Optionnel | HLS gated `HLSEnabled=false` default. Direct `/stream` fallback toujours disponible. Transcoding async. | `stream_service.go:49` ; `config.go:355` ; `track_hls_handler.go:266`. |
|
||||
| **MailHog (SMTP)** | 🟢 Dev default | Branché `docker-compose.dev.yml:114-130`, port 1025. Dev : fail email → log + continue. Prod : 500 hard. | `.env.template:160-165` ; `service.go:381-407`. |
|
||||
|
||||
**Résumé** : **3 hard-required** (Postgres, migrations, bcrypt) · **le reste est optionnel avec fallback, fail-secure, ou prod-gate explicite**. C'est l'évolution la plus importante depuis v1 : il n'y a plus de silent failures non documentés.
|
||||
|
||||
### 3.2 Seeding
|
||||
|
||||
- `veza-backend-api/cmd/tools/seed/main.go` : modes `production` / `full` / `smoke`. Truncate tables → insert users → tracks → playlists → social → chat. **Manuel**, pas auto-run. Marche.
|
||||
|
||||
---
|
||||
|
||||
## 4. Stabilité — points de fragilité restants
|
||||
|
||||
| # | Fragilité | Impact | Preuve |
|
||||
| -- | ------------------------------------------- | :-----: | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | **WebRTC 1:1 sans STUN/TURN** | 🟡 Prod | Pas d'env var, pas de grep hit. NAT symétrique = call failures silencieuses (les signals passent, mais le flux média échoue). |
|
||||
| 2 | **Stockage local disque only** | 🟡 Prod | `uploads/tracks/<userID>/` sur FS local. Pas scalable multi-pod sans volume partagé. Le code S3/MinIO est dead in upload path. |
|
||||
| 3 | **HLS `HLSEnabled=false` par défaut** | 🟢 Dev | Fonctionnel grâce au fallback `/stream`. Pas d'adaptive bitrate out-of-box. Opérateur doit activer explicitement. |
|
||||
| 4 | **Transcoding dual-trigger** | 🟡 Ops | `StreamService.StartProcessing` (gRPC) **et** `EnqueueTranscodingJob` (RabbitMQ) appelés tous les deux. Redondance non documentée. |
|
||||
| 5 | **`HLS_STREAMING` absent de .env.template** | 🟠 Doc | Dev qui veut HLS doit trouver la var ailleurs. `.env.template` à compléter. |
|
||||
| 6 | **Dev bypass Hyperswitch** | 🟢 Ops | Fail-closed prod (`Config.Validate`), mais en staging un opérateur distrait peut servir des licences gratuites. Mettre un warning loud au boot. |
|
||||
| 7 | **Email tokens en query param** | 🟠 Sec | `?token=X` peut leak via Referer / logs proxy. Migration flagged v0.2 (commentaire `handlers/auth.go` L339). |
|
||||
| 8 | **Register issue JWT avant email send** | 🟠 UX | User a ses tokens avant que l'email parte → login 403 immédiat tant que non-vérifié. Cohérent mais friction. |
|
||||
| 9 | **ClamAV 10s timeout sync** | 🟢 UX | Upload bloque jusqu'à 10s sur scan. Acceptable pour fichiers audio <100MB. |
|
||||
| 10 | **Subscription `pending_payment` item G** | 🟢 Roadm| v1.0.6.2 compense via filter, item G dans v107-plan refait le path proprement. Pas un bug, juste techdebt flaggée. |
|
||||
|
||||
**Zero silent fails** parmi les 6 surfaces critiques (Chat Redis, RabbitMQ, RTMP, HLS, SMTP, Hyperswitch). C'est le grand changement depuis v1.
|
||||
|
||||
---
|
||||
|
||||
## 5. Verdict final
|
||||
|
||||
**Veza v1.0.7-rc1 est prêt pour :**
|
||||
- ✅ **Démo publique contrôlée** — un pod, infra dev `make dev`, Hyperswitch sandbox. Le parcours "register → verify → search → play → upload → purchase → refund" est intégralement opérationnel.
|
||||
- ✅ **Sandbox payment testing** — refund réel, reconciliation, ledger-health gauges, Stripe Connect reversal. Toute la plomberie monétaire est audit-ready.
|
||||
- ✅ **Beta privée multi-utilisateurs** — chat multi-instance avec alarme loud si Redis manque, co-listening host-authority, livestream avec health banner. Pas de silent fails.
|
||||
|
||||
**Veza v1.0.7-rc1 n'est PAS prêt pour :**
|
||||
- 🟡 **Production publique grand-public scale** — le stockage uploads sur disque local ne survit pas à un second pod. MinIO/S3 doit être wiré dans le path upload (le code dort, il faut juste l'appeler).
|
||||
- 🟡 **Calls WebRTC fiables hors LAN** — sans STUN/TURN, symmetric NAT = échec silencieux du flux média. À configurer avant d'ouvrir la feature calls au public.
|
||||
- 🟠 **Opérateur ops naïf** — le dashboard Grafana ledger-health est là mais ne sert à rien si personne ne le regarde. Nécessite un runbook d'exploitation.
|
||||
|
||||
**Ce qui a changé depuis la v1 du 2026-04-16** — en 3 jours, l'équipe a fermé **7 findings 🔴/🟡** et ajouté **10 nouvelles capacités** (reconciliation, audit log webhook, ledger metrics, reversal async, upgrade creator, upload SSOT, RTMP health, orphan cleanup, maintenance persist, SMTP unified). Voir §6.
|
||||
|
||||
**En une phrase** : **le code est solide, la plomberie est honnête, les seuls 🟡 restants sont des features "scale" (storage, NAT) pas des bugs**.
|
||||
|
||||
---
|
||||
|
||||
## 6. Diff vs audit v1 (2026-04-16)
|
||||
|
||||
Tableau des évolutions : chaque ligne = un finding v1 avec son statut aujourd'hui.
|
||||
|
||||
| Finding v1 | v1 | v2 | Commit / Preuve |
|
||||
| ---------------------------------------------------------- | :-: | :-: | ------------------------------------------------------------------------------------------------------ |
|
||||
| Player/écoute audio sans fallback (HLSEnabled=false) | 🔴 | ✅ | Endpoint direct `/tracks/:id/stream` + Range cache bypass. `b875efcff`, `routes_tracks.go:118-120`. |
|
||||
| Register : `IsVerified: true` hardcoded | 🔴 | ✅ | `service.go:200` → `IsVerified: false`. Commit trail. |
|
||||
| Verify email : dead code | 🔴 | ✅ | Endpoint actif, login 403 sur unverified (`service.go:527`). |
|
||||
| SMTP silent fail | 🟡 | ✅ | Env schema unifié (`066144352`). Prod : 500 hard. Dev : log + continue. MailHog branché par défaut. |
|
||||
| Marketplace dev bypass | 🟡 | ✅ | Prod gate `Config.Validate` refuse boot (`config.go:908-910`). Dev bypass conservé, assumé. |
|
||||
| Refund : row DB only, pas de reverse-charge | 🟡 | ✅ | 3-phase avec idempotency key. `959031667`, `4f15cfbd9`, `service.go:1297-1436`. |
|
||||
| Subscription : payment gate bypass | 🟡 | ✅ | v1.0.6.2 hotfix `d31f5733d`, `hasEffectivePayment()`. |
|
||||
| Chat multi-instance silent fallback | 🟡 | ✅ | Redis missing = **log ERROR loud** (`chat_pubsub.go:23-27`). Fallback conservé pour single-pod dev. |
|
||||
| Livestream : dépendance cachée `--profile live` | 🟡 | ✅ | Health endpoint + banner UI (`64fa0c9ac`, `live_health_handler.go:78-96`). |
|
||||
| Maintenance mode in-memory | 🟡 | ✅ | Persisté `platform_settings` + TTL 10s. `3a95e38fd`, `middleware/maintenance.go:16-100`. |
|
||||
| Tracks orphelines `Processing` indéfiniment | 🟡 | ✅ | Cleanup hourly worker. `553026728`, `jobs/cleanup_orphan_tracks.go`. |
|
||||
| RabbitMQ silent drop | 🟡 | ✅ | Log ERROR sur publish failure. `bf688af35`. |
|
||||
| Upload size limits désalignés front/back | 🟠 | ✅ | SSOT `config/upload_limits.go` + hook `useUploadLimits`. `5848c2e40`. |
|
||||
| Stripe Connect reversal inexistant | 🔵 | ✅ | Async worker + state machine `reversal_pending`. v1.0.7 items A+B. |
|
||||
| Reconciliation Hyperswitch (stuck orders) | 🔵 | ✅ | `reconcile_hyperswitch.go:55-150`. v1.0.7 item C. |
|
||||
| Webhook raw payload audit log | 🔵 | ✅ | `webhook_log.go` + cleanup 90j. v1.0.7 item E. |
|
||||
| Ledger-health metrics + alerts | 🔵 | ✅ | 5 gauges Prometheus + 3 alert rules + Grafana dashboard. v1.0.7 item F. |
|
||||
| Idempotency-key Hyperswitch | 🔵 | ✅ | Sur CreatePayment + CreateRefund. v1.0.7 item D (`4f15cfbd9`). |
|
||||
| Self-service creator upgrade | 🔵 | ✅ | `POST /users/me/upgrade-creator`, email-verified gate. `c32278dc1`. |
|
||||
| WebRTC sans STUN/TURN | 🟡 | 🟡 | **Toujours pas fixé.** Signaling ok, NAT traversal non. |
|
||||
| Stockage uploads sur disque local | 🟡 | 🟡 | **Toujours pas fixé.** Code S3 présent, non wiré. |
|
||||
| HLS `HLSEnabled=false` par défaut | 🔴 | 🟢 | Plus bloquant grâce au fallback direct stream, mais flag toujours off. |
|
||||
|
||||
Légende : 🔵 = finding absent de v1 mais identifié ici, 🟢 = non-bloquant en v2, 🟠 = doc/cleanup.
|
||||
|
||||
**Bilan** : **18 findings v1 résolus**, **2 subsistants** (WebRTC TURN, stockage local). **7 nouvelles capacités ajoutées** (reconcil, audit log, ledger metrics, reversal, upgrade creator, upload SSOT, RTMP health). Le "chemin critique v1.0.5 public-ready" listé en v1 est **intégralement réalisé** par v1.0.5 → v1.0.7-rc1.
|
||||
|
||||
---
|
||||
|
||||
## 7. Cleanup session post-rc1 (2026-04-23)
|
||||
|
||||
Une session cleanup + BFG a été exécutée 4 jours après cet audit. Cross-référence avec [AUDIT_REPORT.md §9](AUDIT_REPORT.md) :
|
||||
|
||||
- ✅ **10/15 items Top-15 traités** (cleanup #1/#2/#3/#6/#7/#9/#11/#12/#13, BFG inclus)
|
||||
- ⚠️ **3 false-positives identifiés** (#4 context propagation, #5 security headers, #10 `RespondWithAppError`) — voir `AUDIT_REPORT.md §9.bis` pour les preuves
|
||||
- 📋 **2 deferrals v1.0.8** (#8 OpenAPI typegen, #14 E2E Playwright CI)
|
||||
- 📝 **1 item pending** (#15 `docs/ENV_VARIABLES.md` sync, 0.5j)
|
||||
- **Repo `.git` : 1.5 GB → 66 MB** (−97%) après 2 passes git-filter-repo + force-push stages 1+2
|
||||
|
||||
Les 2 findings fonctionnels subsistants (WebRTC STUN/TURN + stockage uploads disque local) restent **post-v1.0.7-final** dans le scope v1.0.8 (2-3j chacun).
|
||||
|
||||
---
|
||||
|
||||
*Généré par Claude Code Opus 4.7 (1M context, /effort max, /plan) — 5 agents Explore parallèles + vérifications ponctuelles directes (`routes_tracks.go:118`, `core/auth/service.go:200`, `config.go:355/907-910`, `marketplace/service.go:522-586`). Cross-référencé avec `docs/audit-2026-04/v107-plan.md` et `CHANGELOG.md` v1.0.5 → v1.0.7-rc1. Une correction par rapport à v1 : le Player n'est plus 🔴 — la v1 avait loupé l'endpoint `/stream` (fallback direct avec Range support). §7 ajouté 2026-04-23 post-session cleanup.*
|
||||
661
LICENSE
Normal file
661
LICENSE
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
6
Makefile
6
Makefile
|
|
@ -34,7 +34,7 @@ include make/help.mk
|
|||
# Add new services in make/config.mk (SERVICES, SERVICE_DIR_*, PORT_*).
|
||||
# ==============================================================================
|
||||
|
||||
.PHONY: dev-web dev-backend-api dev-stream-server
|
||||
.PHONY: test-web test-backend-api test-stream-server
|
||||
.PHONY: lint-web lint-backend-api lint-stream-server
|
||||
.PHONY: dev-web dev-backend-api dev-chat-server dev-stream-server
|
||||
.PHONY: test-web test-backend-api test-chat-server test-stream-server
|
||||
.PHONY: lint-web lint-backend-api lint-chat-server lint-stream-server
|
||||
# (targets defined in make/dev.mk and make/test.mk)
|
||||
|
|
|
|||
127
Makefile.old
Normal file
127
Makefile.old
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Veza Platform - Root Makefile
|
||||
# Test Coverage targets (T0043)
|
||||
|
||||
.PHONY: test-coverage coverage-html help
|
||||
|
||||
help: ## Show this help message
|
||||
@echo 'Usage: make [target]'
|
||||
@echo ''
|
||||
@echo 'Test Coverage targets:'
|
||||
@echo ' test-coverage - Run tests and generate coverage report (T0043)'
|
||||
@echo ' coverage-html - Generate HTML coverage report from existing coverage.out (T0043)'
|
||||
|
||||
test-coverage: ## Run tests and generate coverage report (T0043)
|
||||
@echo "📊 Generating test coverage report..."
|
||||
@bash scripts/test-coverage.sh
|
||||
|
||||
coverage-html: ## Generate HTML coverage report from existing coverage.out (T0043)
|
||||
@echo "📊 Generating HTML coverage report..."
|
||||
@cd veza-backend-api && go tool cover -html=coverage/coverage.out -o coverage/coverage.html
|
||||
@echo "✅ Coverage report generated: veza-backend-api/coverage/coverage.html"
|
||||
|
||||
# >>> VEZA:BEGIN QA TARGETS
|
||||
.PHONY: smoke e2e postman lighthouse load qa-all visual backstop-ref backstop-test loki lh a11y start-services
|
||||
|
||||
smoke: ## Run API smoke tests (curl + httpie)
|
||||
@echo "🔥 Running API smoke tests..."
|
||||
@bash .veza/qa/scripts/wait_for_http.sh "$${VEZA_API_BASE_URL:-http://localhost:8080}/health" 90
|
||||
@bash .veza/qa/scripts/smoke_curl.sh
|
||||
@bash .veza/qa/scripts/smoke_httpie.sh || true
|
||||
|
||||
start-services: ## Start services required for QA tests
|
||||
@echo "🚀 Starting services for QA tests..."
|
||||
@bash .veza/qa/scripts/start-services-for-tests.sh
|
||||
|
||||
e2e: ## Run E2E tests with Playwright
|
||||
@echo "🎭 Running E2E tests..."
|
||||
@cd .veza/qa/playwright && \
|
||||
if [ ! -d "node_modules" ] || [ ! -f "node_modules/@playwright/test/package.json" ]; then \
|
||||
echo "📦 Installing Playwright dependencies..."; \
|
||||
npm install --silent; \
|
||||
fi && \
|
||||
npx playwright test --config=playwright.config.ts
|
||||
|
||||
postman: ## Run Postman/Newman tests
|
||||
@echo "📮 Running Postman/Newman tests..."
|
||||
@newman run .veza/qa/postman/veza_api_collection.json \
|
||||
-e .veza/qa/data/postman_env_local.json \
|
||||
--reporters cli,junit \
|
||||
--reporter-junit-export reports/newman.xml || true
|
||||
|
||||
lighthouse: ## Run Lighthouse CI
|
||||
@echo "💡 Running Lighthouse CI..."
|
||||
@npx lhci autorun --config=.veza/qa/lighthouse/lighthouserc.json || true
|
||||
|
||||
load: ## Run k6 load tests
|
||||
@echo "⚡ Running k6 load tests..."
|
||||
@k6 run .veza/qa/k6/smoke.js || true
|
||||
|
||||
visual: ## Run Playwright visual regression tests
|
||||
@echo "🖼️ Running Playwright visual regression tests..."
|
||||
@cd .veza/qa/playwright && \
|
||||
if [ ! -d "node_modules" ] || [ ! -f "node_modules/@playwright/test/package.json" ]; then \
|
||||
echo "📦 Installing Playwright dependencies..."; \
|
||||
npm install --silent; \
|
||||
fi && \
|
||||
npx playwright test tests/visual/ --config=playwright.config.ts
|
||||
|
||||
visual-update: ## Generate/update Playwright visual snapshots
|
||||
@echo "📸 Generating Playwright visual snapshots..."
|
||||
@cd .veza/qa/playwright && \
|
||||
if [ ! -d "node_modules" ] || [ ! -f "node_modules/@playwright/test/package.json" ]; then \
|
||||
echo "📦 Installing Playwright dependencies..."; \
|
||||
npm install --silent; \
|
||||
fi && \
|
||||
npx playwright test tests/visual/ --config=playwright.config.ts --update-snapshots
|
||||
|
||||
backstop-ref: ## Generate BackstopJS reference images
|
||||
@echo "📸 Generating BackstopJS reference images..."
|
||||
@cd .veza/qa/backstop && npx backstop reference --config=backstop.json || true
|
||||
|
||||
backstop-test: ## Run BackstopJS visual regression tests
|
||||
@echo "🔍 Running BackstopJS visual regression tests..."
|
||||
@cd .veza/qa/backstop && npx backstop test --config=backstop.json || true
|
||||
|
||||
loki: ## Run Loki visual regression tests (requires Storybook)
|
||||
@echo "📚 Running Loki visual regression tests..."
|
||||
@echo "⚠️ Loki requires Storybook to be set up. See .veza/qa/README.md for setup instructions."
|
||||
@if [ -d ".storybook" ] || [ -d "apps/web/.storybook" ]; then \
|
||||
npx loki test || true; \
|
||||
else \
|
||||
echo "❌ Storybook not found. Install Storybook first to use Loki."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
lh: lighthouse ## Alias for lighthouse
|
||||
|
||||
a11y: ## Run Pa11y accessibility tests
|
||||
@echo "♿ Running Pa11y accessibility tests..."
|
||||
@npx pa11y-ci --config .veza/qa/pa11y/.pa11yci.json || true
|
||||
|
||||
qa-all: smoke e2e postman lighthouse load visual a11y ## Run all QA tests
|
||||
@echo "✅ All QA tests completed!"
|
||||
# <<< VEZA:END QA TARGETS
|
||||
|
||||
# >>> VEZA:BEGIN LAB ORCHESTRATION
|
||||
.PHONY: infra-up infra-check migrate-all services-up health-all dev-lab
|
||||
|
||||
infra-up: ## Start Lab Infrastructure (Postgres, Redis, RabbitMQ)
|
||||
@bash scripts/lab/start_infra.sh
|
||||
|
||||
infra-check: ## Check Lab Infrastructure Health
|
||||
@bash scripts/lab/check_infra.sh
|
||||
|
||||
migrate-all: ## Apply migrations for all services
|
||||
@bash scripts/lab/apply_all_migrations.sh
|
||||
|
||||
services-up: ## Start all services (Backend, Chat, Stream, Web)
|
||||
@bash scripts/lab/start_all_services.sh
|
||||
|
||||
services-down: ## Stop all services
|
||||
@bash scripts/lab/stop_all_services.sh
|
||||
|
||||
health-all: ## Check health of all services
|
||||
@bash scripts/lab/check_all_health.sh
|
||||
|
||||
dev-lab: infra-up infra-check migrate-all services-down services-up health-all ## Start full Lab Environment (Clean Restart)
|
||||
# <<< VEZA:END LAB ORCHESTRATION
|
||||
64
README.md
64
README.md
|
|
@ -1,54 +1,32 @@
|
|||
# Veza Monorepo
|
||||
|
||||
[](https://github.com/okinrev/veza/actions/workflows/ci.yml)
|
||||
|
||||
**Version courante** : v1.0.4 (cleanup + consolidation post-audit). Voir [CHANGELOG.md](CHANGELOG.md) et [docs/PROJECT_STATE.md](docs/PROJECT_STATE.md).
|
||||
**Version cible** : v0.101 (stabilisation en cours). Voir [docs/V0_101_RELEASE_SCOPE.md](docs/V0_101_RELEASE_SCOPE.md) pour le périmètre.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- **`apps/web`** — Frontend React 18 + Vite 5 + TypeScript strict (source of truth for the UI)
|
||||
- **`veza-backend-api`** — Main Go 1.25 API service (Gin, GORM, Postgres, Redis, RabbitMQ, Elasticsearch). Handles REST, WebSocket, and chat (chat server was merged into this service in v0.502).
|
||||
- **`veza-stream-server`** — Rust streaming server (Axum 0.8, Tokio 1.35, Symphonia) — HLS, HTTP Range, WebSocket, gRPC
|
||||
- **`veza-common`** — Shared Rust types and logging
|
||||
- **`packages/design-system`** — Shared design tokens
|
||||
|
||||
See [CLAUDE.md](CLAUDE.md) for the full architecture map.
|
||||
|
||||
## Development Setup
|
||||
|
||||
Prerequisites: Node 20 (see `.nvmrc`), Go, Rust, Docker. Configure `.env` from `.env.example`.
|
||||
|
||||
```bash
|
||||
# Verify environment
|
||||
make doctor
|
||||
./scripts/validate-env.sh development
|
||||
|
||||
# Install dependencies
|
||||
make install-deps
|
||||
|
||||
# Option A — Backend in Docker + Web local
|
||||
make dev
|
||||
|
||||
# Option B — All apps local with hot reload (infra from docker-compose.dev.yml)
|
||||
make dev-full
|
||||
|
||||
# Option C — Infra only, then run services manually
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
make dev-web # or make dev-backend-api, make dev-stream-server
|
||||
```
|
||||
|
||||
See [docs/ENV_VARIABLES.md](docs/ENV_VARIABLES.md) for required variables. `make build` builds all services.
|
||||
- **`apps/web`**: The main frontend application (React + Vite). **This is the single source of truth for the UI.**
|
||||
- **`veza-desktop`**: A thin Electron wrapper that loads `apps/web`. It creates the native desktop experience.
|
||||
- **`veza-backend-api`**: Main Go API service.
|
||||
- **`veza-stream-server`**: Rust streaming server.
|
||||
- **`veza-chat-server`**: Rust chat server.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Frontend only
|
||||
|
||||
### Frontend
|
||||
```bash
|
||||
cd apps/web
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Desktop (Optional)
|
||||
Requires `apps/web` to be running.
|
||||
```bash
|
||||
cd veza-desktop
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Docker Production
|
||||
|
||||
**Canonical production compose file**: `docker-compose.prod.yml`
|
||||
|
|
@ -57,16 +35,12 @@ npm run dev
|
|||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
**Deprecated** (use docker-compose.prod.yml):
|
||||
- `docker-compose.production.yml` — legacy, may be removed
|
||||
- `config/docker/docker-compose.production.yml` — legacy config
|
||||
|
||||
See `make/config.mk` for COMPOSE_PROD and deployment docs.
|
||||
|
||||
## CI/CD
|
||||
|
||||
- **Badge** : CI status above. Set `SLACK_WEBHOOK_URL` (Incoming Webhook) in repo secrets to receive Slack notifications on failure.
|
||||
|
||||
### Disabled workflows
|
||||
|
||||
- **Storybook** (`chromatic.yml.disabled`, `storybook-audit.yml.disabled`, `visual-regression.yml.disabled`): deferred until MSW is wired up for `/api/v1/auth/me` and `/api/v1/logs/frontend`, which currently causes ~1 400 network errors in the Storybook build. The npm scripts (`storybook`, `build-storybook`) still work locally for one-off component inspection. To reactivate in CI, fix the MSW handlers and rename the three files back to `.yml`.
|
||||
|
||||
## Documentation
|
||||
|
||||
- **[Developer Onboarding](docs/ONBOARDING.md)** — Setup, architecture, conventions, troubleshooting
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
# Release Notes — Veza v1.0.0
|
||||
|
||||
**Date de release** : 2026-03-03
|
||||
**Version précédente** : v0.803 (2026-02-25)
|
||||
|
||||
---
|
||||
|
||||
## Résumé
|
||||
|
||||
Veza v1.0.0 est la première release commerciale de la plateforme audio collaborative. Cette version consolide les corrections de sécurité, les améliorations de qualité, et les fonctionnalités livrées entre v0.803 et v1.0.0.
|
||||
|
||||
---
|
||||
|
||||
## Nouvelles fonctionnalités depuis v0.803
|
||||
|
||||
### Sécurité (v0.901–v0.903)
|
||||
- OAuth : génération JWT corrigée via JWTService/SessionService
|
||||
- Webhook Hyperswitch : vérification de signature obligatoire
|
||||
- TokenBlacklist intégré au middleware auth (tokens révoqués rejetés)
|
||||
- ValidateExecPath sur les appels exec (waveform_service)
|
||||
- Rate limiter : login/register inclus dans le global limit
|
||||
- Harmonisation Go 1.24, VERSION synchronisé
|
||||
|
||||
### Auth & Commerce (v0.911–v0.912)
|
||||
- Tests d'intégration OAuth Google/GitHub E2E
|
||||
- Tests E2E paiement Hyperswitch, webhook idempotence, refund flow
|
||||
|
||||
### Qualité (v0.921–v0.923)
|
||||
- Couverture tests Rust > 30%
|
||||
- Réduction des skips Go, tests de contrat API
|
||||
- OpenAPI spec générée et validée
|
||||
|
||||
### Performance (v0.931)
|
||||
- Pagination cursor-based : tracks, messages, feed social
|
||||
- Profiling P50/P95/P99 documenté
|
||||
|
||||
### Consolidation (v0.941–v0.943)
|
||||
- Nettoyage code mort, migrations dédupliquées
|
||||
- Schéma consolidé (000_full_schema.sql)
|
||||
- Refactoring fichiers > 1000 lignes
|
||||
|
||||
### Hardening (v0.951–v0.952)
|
||||
- Load tests : 500 req/s API, 1000 WebSocket, 50 uploads
|
||||
- Dashboard Grafana, alertes Prometheus
|
||||
- Health check deep (DB, Redis, S3, RabbitMQ)
|
||||
|
||||
### Documentation & Ops (v0.961–v0.962)
|
||||
- Runbooks : déploiement, rollback, incident, rotation secrets, graceful degradation Redis
|
||||
- API Reference, guide onboarding < 30 min
|
||||
|
||||
### Features cleanup (v0.971)
|
||||
- Feature flag WebRTC_CALLS avec badge "Beta"
|
||||
- Gamification fantôme supprimée
|
||||
- docs/V1_LIMITATIONS.md, docs/API_VERSIONING_POLICY.md
|
||||
|
||||
### Beta & Polish (v0.981–v0.982)
|
||||
- Bug bash complet (Auth, Commerce, Média, Social)
|
||||
- Lighthouse ≥ 90 (Performance, Accessibility)
|
||||
- PWA offline vérifiée
|
||||
- RGPD/CCPA : export, suppression, opt-out documentés
|
||||
|
||||
---
|
||||
|
||||
## Corrections de sécurité majeures
|
||||
|
||||
| ID | Description | Version |
|
||||
|----|-------------|---------|
|
||||
| VEZA-SEC-001 | OAuth generateJWT invalide | v0.901 |
|
||||
| VEZA-SEC-002 | PasswordService.GenerateJWT sans contrôles | v0.901 |
|
||||
| VEZA-SEC-005 | Webhook Hyperswitch vérification optionnelle | v0.901 |
|
||||
| VEZA-SEC-006 | TokenBlacklist déconnecté du middleware | v0.901 |
|
||||
| VEZA-SEC-007 | waveform_service sans ValidateExecPath | v0.901 |
|
||||
| VEZA-SEC-003 | PKCE OAuth | v0.902 |
|
||||
| VEZA-SEC-004 | Tokens OAuth chiffrés au repos | v0.902 |
|
||||
| VEZA-SEC-008 | ORDER BY dynamique (whitelist) | v0.903 |
|
||||
| VEZA-SEC-009 | Login/register exclus du rate limiter | v0.903 |
|
||||
|
||||
---
|
||||
|
||||
## Améliorations
|
||||
|
||||
- **Performance** : Pagination cursor-based, P99 < 500ms cible
|
||||
- **Observabilité** : Request ID propagé, métriques Prometheus, Dashboard Grafana
|
||||
- **RGPD/CCPA** : Export données, suppression compte, opt-out documentés et vérifiés
|
||||
- **Accessibilité** : Lighthouse Accessibility ≥ 90, PWA mode offline
|
||||
- **Documentation** : Runbooks opérationnels, checklist V1_SIGNOFF
|
||||
|
||||
---
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### Pagination
|
||||
- `GET /tracks`, `GET /conversations/:id/history`, `GET /social/feed` : support de `cursor` et `limit` en plus de `page`/`limit`. La pagination OFFSET reste en fallback pour rétro-compatibilité.
|
||||
|
||||
### API
|
||||
- Aucun breaking change sur les signatures de réponse. Voir [docs/API_VERSIONING_POLICY.md](docs/API_VERSIONING_POLICY.md).
|
||||
|
||||
---
|
||||
|
||||
## Migration guide (v0.803 → v1.0.0)
|
||||
|
||||
1. **Variables d'environnement** : Vérifier `OAUTH_ENCRYPTION_KEY`, `CHAT_JWT_SECRET` séparé de `JWT_SECRET` en production, `HYPERSWITCH_WEBHOOK_SECRET` obligatoire.
|
||||
|
||||
2. **Migrations** : Appliquer les migrations depuis la dernière version. Si base existante post-consolidation (v0.942), le marqueur `000_mark_consolidated.sql` peut être requis. Voir [docs/MIGRATION_CONSOLIDATION.md](docs/MIGRATION_CONSOLIDATION.md).
|
||||
|
||||
3. **Tokens OAuth** : Si des tokens OAuth existants sont en clair, exécuter le script `cmd/tools/encrypt_oauth_tokens` avant mise à jour.
|
||||
|
||||
4. **Frontend** : Aucune action spécifique. Les curseurs de pagination sont optionnels.
|
||||
|
||||
---
|
||||
|
||||
## Limitations connues (v1.0)
|
||||
|
||||
Voir [docs/V1_LIMITATIONS.md](docs/V1_LIMITATIONS.md) pour la liste complète : WebRTC TURN/STUN (v1.1), 2FA SMS (v1.1), Redis HA (v1.1), etc.
|
||||
|
||||
---
|
||||
|
||||
## Liens
|
||||
|
||||
- [CHANGELOG.md](CHANGELOG.md) — Historique détaillé des versions
|
||||
- [docs/ROADMAP_V09XX_TO_V1.md](docs/ROADMAP_V09XX_TO_V1.md) — Roadmap complète
|
||||
- [docs/V1_SIGNOFF.md](docs/V1_SIGNOFF.md) — Checklist de validation
|
||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.0.8
|
||||
0.101.0
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -16,9 +16,9 @@ VITE_BACKEND_PORT=18080
|
|||
VITE_API_URL=/api/v1
|
||||
|
||||
# WebSocket Configuration
|
||||
# Chat WebSocket URL. If omitted, auto-derived from VITE_API_URL + /ws
|
||||
# v0.502: Chat WS is now served by the Go backend at /api/v1/ws
|
||||
# VITE_WS_URL=/api/v1/ws
|
||||
# WebSocket URL for real-time features (can be absolute URL or path starting with /)
|
||||
# If omitted, auto-derived from VITE_DOMAIN: ws://<domain>:8081/ws
|
||||
VITE_WS_URL=/ws
|
||||
|
||||
# Stream Server Configuration
|
||||
# Stream server URL for audio streaming (can be absolute URL or path starting with /)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
# Point API to a relative path so MSW can intercept it easily (same-origin)
|
||||
VITE_API_URL=/api/v1
|
||||
VITE_WS_URL=/ws
|
||||
VITE_IS_STORYBOOK=true
|
||||
VITE_USE_MSW=true
|
||||
VITE_FEATURE_HLS_STREAMING=true
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
[
|
||||
{
|
||||
"path": "dist/assets/index-*.js",
|
||||
"limit": "300 KB",
|
||||
"gzip": true
|
||||
},
|
||||
{
|
||||
"path": "dist/assets/*.css",
|
||||
"limit": "80 KB",
|
||||
"gzip": true
|
||||
}
|
||||
]
|
||||
|
|
@ -29,8 +29,7 @@ const queryClient = new QueryClient({
|
|||
});
|
||||
|
||||
export const StorybookDecorator: Decorator = (Story, context) => {
|
||||
const bgValue = context.globals?.backgrounds?.value;
|
||||
const isDark = bgValue !== 'light'; // only 'light' triggers light mode, everything else = dark
|
||||
const isDark = context.globals?.backgrounds?.value !== '#ffffff';
|
||||
const initialEntries =
|
||||
(context.parameters?.router as { initialEntries?: string[] } | undefined)?.initialEntries ?? ['/'];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
// This file has been automatically migrated to valid ESM format by Storybook.
|
||||
import { createRequire } from "node:module";
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
import { dirname, join } from "path"
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
function getAbsolutePath(value: string) {
|
||||
return dirname(require.resolve(join(value, "package.json")))
|
||||
}
|
||||
|
|
@ -16,14 +12,16 @@ const config: StorybookConfig = {
|
|||
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
|
||||
],
|
||||
"addons": [
|
||||
getAbsolutePath('@storybook/addon-essentials'),
|
||||
getAbsolutePath('@storybook/addon-a11y'),
|
||||
getAbsolutePath("@storybook/addon-docs"),
|
||||
getAbsolutePath("@storybook/addon-mcp"),
|
||||
getAbsolutePath('@storybook/addon-interactions'),
|
||||
getAbsolutePath('msw-storybook-addon'),
|
||||
],
|
||||
"staticDirs": ['../public'],
|
||||
"framework": getAbsolutePath('@storybook/react-vite'),
|
||||
"docs": {
|
||||
defaultName: "Documentation"
|
||||
"defaultName": "Documentation",
|
||||
"autodocs": true
|
||||
},
|
||||
"typescript": {
|
||||
"reactDocgen": "react-docgen-typescript",
|
||||
|
|
|
|||
|
|
@ -56,33 +56,27 @@ const preview: Preview = {
|
|||
expanded: true,
|
||||
},
|
||||
a11y: {
|
||||
test: 'error-on-violation',
|
||||
test: 'todo',
|
||||
},
|
||||
viewport: {
|
||||
options: customViewports,
|
||||
viewports: customViewports,
|
||||
},
|
||||
backgrounds: {
|
||||
options: {
|
||||
dark: { name: 'dark', value: '#121215' },
|
||||
light: { name: 'light', value: '#faf9f6' },
|
||||
raised: { name: 'raised', value: '#1a1a1f' }
|
||||
}
|
||||
default: 'dark',
|
||||
values: [
|
||||
{ name: 'dark', value: '#121215' },
|
||||
{ name: 'light', value: '#faf9f6' },
|
||||
{ name: 'raised', value: '#1a1a1f' },
|
||||
],
|
||||
},
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
toc: true, // Enable table of contents in docs
|
||||
},
|
||||
},
|
||||
|
||||
decorators: [StorybookDecorator],
|
||||
tags: ['autodocs'],
|
||||
loaders: [mswLoader],
|
||||
|
||||
initialGlobals: {
|
||||
backgrounds: {
|
||||
value: 'dark'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default preview;
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ La page `/library` utilise actuellement des données mockées ou des appels API
|
|||
### FRONT-004: WebSocket Chat Partiellement Connecté
|
||||
|
||||
**Description:**
|
||||
Le système de chat WebSocket est implémenté mais le serveur chat Rust a été retiré du projet. Une alternative (stream-server ou backend) devra être mise en place si le chat temps réel est requis.
|
||||
Le système de chat WebSocket est implémenté mais peut ne pas être complètement connecté au serveur Rust (`veza-chat-server`).
|
||||
|
||||
**Fichiers Concernés:**
|
||||
- `src/features/chat/hooks/useChat.ts` - Hook de connexion WebSocket
|
||||
|
|
@ -224,7 +224,7 @@ Le système de chat WebSocket est implémenté mais le serveur chat Rust a été
|
|||
- Nécessite validation avec le serveur Rust
|
||||
|
||||
**Recommandation:**
|
||||
- Définir une stratégie de chat (backend ou stream-server)
|
||||
- Tester la connexion avec `veza-chat-server` en développement
|
||||
- Valider le protocole de messages WebSocket
|
||||
- Implémenter des tests d'intégration E2E pour le chat
|
||||
|
||||
|
|
@ -284,16 +284,16 @@ Le système de streaming audio est implémenté côté frontend mais n'est pas c
|
|||
---
|
||||
|
||||
#### 1.2. Connecter le WebSocket Chat
|
||||
**Objectif:** Définir et implémenter une stratégie de chat temps réel (le serveur chat Rust a été retiré)
|
||||
**Objectif:** Valider et finaliser la connexion au `veza-chat-server`
|
||||
**Tâches:**
|
||||
- Choisir une approche (backend WebSocket, stream-server, ou service tiers)
|
||||
- Tester la connexion WebSocket avec le serveur Rust
|
||||
- Valider le format des messages (JSON)
|
||||
- Implémenter la gestion des erreurs de connexion
|
||||
- Ajouter des indicateurs visuels de statut (connecté/déconnecté)
|
||||
- Tests E2E pour le chat en temps réel
|
||||
|
||||
**Estimation:** 4-6 heures
|
||||
**Dépendances:** Service de chat à définir
|
||||
**Dépendances:** `veza-chat-server` opérationnel et accessible
|
||||
|
||||
---
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue