Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apply_control_panel_patch_and_pr.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@echo off
REM Wrapper batch pour lancer le script PowerShell depuis CMD
SETLOCAL
SET SCRIPT_DIR=%~dp0
REM Si aucun argument fourni, lancer en mode non interactif avec la branche par défaut et -ForceSSH
IF "%~1"=="" (
echo Aucun argument fourni -> exécution automatique non interactive avec ForceSSH
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%apply_control_panel_patch_and_pr.ps1" -BranchName "feature/control-panel-compose-fix" -NonInteractive -ForceSSH
) ELSE (
REM Transmettre tous les arguments fournis au script PowerShell
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%apply_control_panel_patch_and_pr.ps1" %*
)
ENDLOCAL
188 changes: 188 additions & 0 deletions apply_control_panel_patch_and_pr.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<#
Enhanced apply_control_panel_patch_and_pr.ps1
Features:
- Branch name prompt (or non-interactive via -NonInteractive)
- Dry-run mode (-DryRun)
- Attempt gh auth login automatically on push failure
- Optional forced SSH remote before retrying push (-ForceSSH)
- Open the exact PR URL returned by gh when possible
#>

param(
[string]$BranchName = 'feature/control-panel-compose-fix',
[switch]$DryRun,
[switch]$NonInteractive,
[switch]$ForceSSH
)

$ErrorActionPreference = 'Stop'

# Determine repo root (script path)
$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location $repoRoot

$patch = Join-Path $repoRoot 'control-panel-compose-fix.patch'

if (-not (Test-Path $patch)) {
Write-Host "Patch not found at: $patch" -ForegroundColor Red
Write-Host "Place control-panel-compose-fix.patch in the repository root and re-run." -ForegroundColor Yellow
exit 1
}

# Determine branch name
if ($NonInteractive) {
$branch = $BranchName
Write-Host "Non-interactive mode : branch = $branch"
} else {
$branch = Read-Host -Prompt "Nom de la branche à créer/utiliser (appuie sur Entrée pour '$BranchName')"
if ([string]::IsNullOrWhiteSpace($branch)) { $branch = $BranchName }
Write-Host "Branch choisie : $branch"
}

if ($DryRun) { Write-Host "Mode dry-run activé : aucune modification ne sera apportée." -ForegroundColor Yellow; Show-Plan; exit 0 }

function Show-Plan {
Write-Host "Plan d'action :" -ForegroundColor Cyan
Write-Host " 1) Checkout/creer la branche: $branch"
Write-Host " 2) Appliquer le patch: $patch (git am -> git apply fallback)"
Write-Host " 3) Pousser la branche sur origin (avec tentative d'auth if needed)"
Write-Host " 4) Créer la PR via gh (si disponible) ou ouvrir la page de comparaison"
}

Show-Plan

# Ensure branch exists and check it out
try {
git rev-parse --verify $branch 2>$null | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host "Checkout branche existante : $branch"
git checkout $branch
} else {
Write-Host "Création et checkout de la branche : $branch"
git checkout -b $branch
}
} catch {
Write-Error "Échec lors de la vérification/création de la branche: $_"
exit 1
}

# Apply patch preserving commits if possible
$applied = $false
try {
Write-Host "Tentative d'application via 'git am'..."
cmd /c "git am < \"$patch\""
if ($LASTEXITCODE -eq 0) {
Write-Host "Patch appliqué avec succès via git am."
$applied = $true
} else {
Write-Host "git am a retourné un code non nul. Passage à git apply..." -ForegroundColor Yellow
}
} catch {
Write-Host "git am a échoué : $_" -ForegroundColor Yellow
}

if (-not $applied) {
try {
Write-Host "Application via 'git apply --index'..."
git apply --index $patch
git add -A
git commit -m "infra: fix compose contexts & add control-panel seed`n`nAdds a targeted docker-compose build.context fix for Windows and a versioned control-panel seed.`n`nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
Write-Host "Patch appliqué et commit créé."
$applied = $true
} catch {
Write-Error "Échec de l'application du patch: $_"
Write-Host "Si des conflits apparaissent, résoudre puis exécuter 'git am --continue' ou 'git apply' manuellement." -ForegroundColor Yellow
exit 1
}
}

# Push branch to origin with retry logic. Optionally switch to SSH before retry.
function Try-Push {
try {
Write-Host "Pushing branch $branch to origin..."
git push -u origin $branch
Write-Host "Push succeeded." -ForegroundColor Green
return $true
} catch {
Write-Warning "git push failed: $_"
return $false
}
}

$pushOk = Try-Push
if (-not $pushOk) {
if ($ForceSSH) {
Write-Host "-ForceSSH activé : on change origin en SSH et on retente le push..." -ForegroundColor Cyan
try {
git remote set-url origin git@github.com:Shamdon/openfoundry.git
Write-Host "Remote origin mis à jour en SSH. Retentative du push..."
$pushOk = Try-Push
if (-not $pushOk) { Write-Error "Push échoué même après passage en SSH."; exit 1 }
} catch {
Write-Error "Impossible de changer remote en SSH : $_"; exit 1
}
} else {
# Attempt to help the user by invoking gh auth login if available
if (Get-Command gh -ErrorAction SilentlyContinue) {
Write-Host "Tentative d'authentification via 'gh auth login --with-browser'..." -ForegroundColor Cyan
try {
Start-Process gh -ArgumentList 'auth','login','--with-browser' -NoNewWindow -Wait
} catch {
Write-Warning "Impossible d'exécuter 'gh auth login': $_"
}
Write-Host "Re-essai du push..."
$pushOk = Try-Push
if (-not $pushOk) { Write-Error "Le push a échoué après tentative d'authentification. Vérifie tes droits et réessaie."; exit 1 }
} else {
Write-Host "gh CLI introuvable. Conseils : authentifie-toi ou configure SSH pour origin." -ForegroundColor Yellow
Write-Host "Exemples : gh auth login --with-browser OU git remote set-url origin git@github.com:Shamdon/openfoundry.git" -ForegroundColor Yellow
if (-not (Try-Push)) { Write-Error "Le push a échoué (vérifie les permissions)."; exit 1 }
}
}
}

# Create PR using gh if available, otherwise open compare page in browser
$prTitle = 'infra: fix compose contexts & add control-panel seed'
$prBody = @"
This PR contains two small developer-facing changes to make the local dev stack and Control Panel seed easier to use:

- infra/compose/docker-compose.yml: adjust build.context for edge-gateway-service and identity-federation-service so Compose resolves correctly on Windows dev checkouts (.. → ../..). This is a targeted fix to avoid a larger refactor.
- apps/web/public/control-panel-seed.json + CONTROL_PANEL_SEED_README.md: add a versioned, documented seed for the Control Panel settings (streaming_profiles etc.) and instructions for applying it.

Why: these changes let developers bring up the gateway + identity locally and apply a durable control-panel seed (used in local development and CI). The changes were tested locally: gateway and identity started and the seed was successfully applied via the gateway's admin API.

Notes for reviewers:
- The compose change is intentionally narrow and should be safe for most developer workflows, but please review for CI implications before merging.
- Do not enable the seed in production; it's a dev convenience.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"@

if (Get-Command gh -ErrorAction SilentlyContinue) {
try {
Write-Host "Création de la PR via gh..."
# Use --json to get the URL where supported
$json = gh pr create --base main --head $branch --title $prTitle --body $prBody --json url 2>$null | Out-String
if ($json -and $json.Trim() -ne '') {
try {
$j = $json | ConvertFrom-Json
if ($j.url) { Write-Host "PR créée : $($j.url)"; Start-Process $j.url; exit 0 }
} catch {
# fall through
}
}
# Fallback: gh prints the URL on stdout; capture and open first https://github.com/... occurrence
$out = gh pr create --base main --head $branch --title $prTitle --body $prBody 2>&1
$link = ($out | Select-String -Pattern 'https://github.com/\S+' -AllMatches).Matches | Select-Object -First 1
if ($link) { $url = $link.Value; Write-Host "PR link: $url"; Start-Process $url } else { Write-Host "PR créée (consulte la sortie de gh pour l'URL)." }
} catch {
Write-Warning "gh pr create a échoué: $_";
Write-Host "Ouverture de la page de comparaison pour créer la PR manuellement..." -ForegroundColor Yellow
Start-Process "https://github.com/Shamdon/openfoundry/compare/main...$branch"
}
} else {
Write-Host "gh CLI introuvable. Ouverture de la page de comparaison pour créer la PR manuellement..." -ForegroundColor Yellow
Start-Process "https://github.com/Shamdon/openfoundry/compare/main...$branch"
}

Write-Host "Terminé." -ForegroundColor Green
72 changes: 72 additions & 0 deletions apps/web/public/CONTROL_PANEL_SEED_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
Control panel seed (control-panel-seed.json)

Purpose

This repository contains a versioned seed file at:

apps/web/public/control-panel-seed.json

It provides a durable, editable canonical Control Panel configuration (including example streaming profiles) that can be applied to a running local OpenFoundry API gateway / control-panel endpoint.

How to apply the seed to a running local API

1) Ensure the edge gateway (API) is running and accessible at http://127.0.0.1:8080. If your gateway is exposed on another host/port, update the URL below.

2) Apply with curl (recommended):

curl -X PUT "http://127.0.0.1:8080/api/v1/control-panel" \
-H "Content-Type: application/json" \
--data-binary @apps/web/public/control-panel-seed.json

If your environment uses the v2 admin API, use:

curl -X PUT "http://127.0.0.1:8080/api/v2/admin/control-panel" \
-H "Content-Type: application/json" \
--data-binary @apps/web/public/control-panel-seed.json

Troubleshooting — gateway not available (502 / connection refused)

If the API at 127.0.0.1:8080 is down or returns 502, the local dev stack may not be running or Docker Compose contexts may be misconfigured. Two safe options:

A) Start gateway via docker-compose (recommended)

From the repository root run:

# prefer starting app+data profiles for a fuller local dev stack
cd <repo-root>
docker compose -f infra\compose\docker-compose.yml --profile app --profile data up -d

If docker-compose fails during build with errors like:
resolve : GetFileAttributesEx C:\...\infra\services: The system cannot find the file specified

then the build context paths in infra/compose/docker-compose.yml are probably incorrect for this repository layout. A minimal fix is to change build.context entries that currently point to '..' (infra parent) to '../../' so that the build context resolves to the repository root. Example change (manual edit):

build:
context: ../..
dockerfile: services/edge-gateway-service/Dockerfile

Note: editing infra/compose/docker-compose.yml affects how Compose builds many services. Prefer to commit the change to a feature branch and open a PR so CI and teammates can review.

B) Run gateway locally as a Go process (requires Go installed)

# from repo root
set DATABASE_URL=postgres://openfoundry:openfoundry@127.0.0.1:5432/openfoundry_data_connector?sslmode=disable
set JWT_SECRET=openfoundry-dev-secret
set HOST=0.0.0.0
set PORT=8080
go run ./services/edge-gateway-service/cmd/edge-gateway-service

Note: The Go toolchain must be installed and on PATH. This avoids docker-compose build complexity.

What this repo change includes

- apps/web/public/control-panel-seed.json — seed JSON for the Control Panel settings (streaming profiles included).
- apps/web/public/CONTROL_PANEL_SEED_README.md — this document, with instructions on applying and troubleshooting.

Next steps I can take for you

- Attempt an automatic in-repo fix replacing build.context: .. -> ../.. in infra/compose/docker-compose.yml and re-run docker compose (I can do this and run the compose up, but it will change the compose file and should be reviewed).
- Retry starting only the gateway service after such a fix.
- Install Go (if you want me to run the gateway locally) — I can guide you or attempt to install if allowed.

Tell me which next step to perform (or say "no changes, just keep the files").
69 changes: 69 additions & 0 deletions apps/web/public/control-panel-seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"platform_name": "OpenFoundry Dev",
"support_email": "support@openfoundry.local",
"docs_url": "https://docs.openfoundry.local",
"status_page_url": "",
"announcement_banner": "",
"maintenance_mode": false,
"release_channel": "canary",
"default_region": "local",
"deployment_mode": "development",
"allow_self_signup": true,
"supported_locales": ["en", "es"],
"default_locale": "en",
"allowed_email_domains": ["example.com"],
"default_app_branding": {
"display_name": "OpenFoundry",
"primary_color": "#246",
"accent_color": "#60a5fa",
"logo_url": null,
"favicon_url": null,
"show_powered_by": false
},
"restricted_operations": [],
"identity_provider_mappings": [],
"resource_management_policies": [],
"upgrade_assistant": {
"current_version": "dev",
"target_version": "dev",
"maintenance_window": "",
"rollback_channel": "",
"preflight_checks": [],
"rollout_stages": [],
"rollback_steps": []
},
"streaming_profiles": [
{
"id": "default",
"name": "Default streaming",
"description": "Conservative defaults for local development.",
"max_throughput_msgs_per_sec": 100,
"max_payload_bytes": 65536,
"retention_ms": 86400000,
"enabled": true,
"rules": [
{
"match": "*",
"action": "allow"
}
]
},
{
"id": "high-throughput",
"name": "High throughput",
"description": "Used for performance tests; higher limits and shorter retention.",
"max_throughput_msgs_per_sec": 5000,
"max_payload_bytes": 262144,
"retention_ms": 3600000,
"enabled": false,
"rules": [
{
"match": "metrics.*",
"action": "allow"
}
]
}
],
"updated_by": "seed:control-panel-seed.json",
"updated_at": "2026-07-22T20:40:00Z"
}
22 changes: 22 additions & 0 deletions apps/web/src/lib/api/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest';

import { getMockAuthResponse } from './client';

describe('getMockAuthResponse', () => {
it('returns a mock authenticated session for login', () => {
const response = getMockAuthResponse('/auth/login', { email: 'demo@example.com', password: 'password123' });
expect(response).toMatchObject({
status: 'authenticated',
access_token: expect.any(String),
refresh_token: expect.any(String),
});
});

it('returns a mock profile for the current user endpoint', () => {
const response = getMockAuthResponse('/users/me');
expect(response).toMatchObject({
email: 'demo@example.com',
auth_source: 'local-dev',
});
});
});
Loading