diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 6779975..9f94786 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -12,7 +12,6 @@ on: - main jobs: - # 1️⃣ Build & tests build-and-test: runs-on: ubuntu-latest @@ -43,7 +42,47 @@ jobs: - name: Build app run: pnpm build - # 2️⃣ Build & push Docker image + sonar-scan: + needs: build-and-test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma Client + run: pnpm prisma generate + + - name: Tests (with coverage report) + run: pnpm exec jest --runInBand --coverage --coverage-reporters=lcov --passWithNoTests + + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + with: + args: > + -Dsonar.projectKey=WPT + -Dsonar.javascript.lcov.reportPaths=./coverage/lcov.info + docker-build-push: runs-on: ubuntu-latest needs: build-and-test @@ -85,7 +124,6 @@ jobs: build-args: | NEXT_PUBLIC_ENV=develop - # 3️⃣ Deploy DEV deploy-dev: runs-on: self-hosted needs: docker-build-push @@ -109,8 +147,6 @@ jobs: echo "🔧 Deploying on DEV..." cd /home/baptiste/Dev/WPT/dev docker-compose pull - # stop + remove DEV containers ONLY (keeps volumes) docker-compose down - # recreate containers, reusing existing volumes docker-compose up -d --remove-orphans echo "🚀 Deployed in DEV!" \ No newline at end of file diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 8761177..2baec03 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -6,7 +6,6 @@ on: - 'v*.*.*' jobs: - # 1️⃣ Build & tests build-and-test: runs-on: ubuntu-latest @@ -37,7 +36,6 @@ jobs: - name: Build app run: pnpm build - # 2️⃣ Build & push Docker image docker-build-push: runs-on: ubuntu-latest needs: build-and-test @@ -81,7 +79,6 @@ jobs: build-args: | NEXT_PUBLIC_ENV=production - # 3️⃣ Deploy PROD deploy-prod: runs-on: self-hosted needs: docker-build-push diff --git a/JulienPolycarpe-CICD.md b/JulienPolycarpe-CICD.md new file mode 100644 index 0000000..b4bf435 --- /dev/null +++ b/JulienPolycarpe-CICD.md @@ -0,0 +1,197 @@ +# CICD + +## CodeQL + +Added an automatic pipeline for CodeQL analysis on every push and merge-request to any branch. + +## Dev +[Pipeline for dev (.github/workflows/dev.yml)](./.github/workflows/dev.yml) + +On merge-request or push to `dev` and `main` branch: +- Build the application +- Run tests on the most important features [See tests (tests)](./tests) +- Run code quality checks on a private sonarqube server +- Build image using the Dockerfile and push Docker image to registry +- Deploy to staging environment on private server + +Test account for sonarqube: +- link: https://sonarqube.baptiiiste.com/dashboard?id=WPT&codeScope=overall +- user: user +- password: MySuperUser1234@ + +Docker compose on my server: +```yml +version: '3.8' +services: + wpt-dev_postgres: + image: postgres:16 + container_name: wpt-dev_postgres + restart: always + environment: + POSTGRES_USER: $POSTGRES_USER + POSTGRES_PASSWORD: $POSTGRES_PASSWORD + POSTGRES_DB: $POSTGRES_DB + volumes: + - wpt-dev_data:/var/lib/postgresql/data + networks: + - web + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + wpt-dev_website: + image: ghcr.io/baptiiiiste/webhookpersistenttester:dev-latest + container_name: wpt-dev_website + restart: always + environment: + NODE_ENV: production + NEXTAUTH_URL: https://wpt-dev.baptiiiste.com + API_URL: https://wpt-dev.baptiiiste.com/api + NEXTAUTH_SECRET: $NEXTAUTH_SECRET + SALT_ROUNDS: "10" + AUTH_TRUST_HOST: "true" + GOOGLE_CLIENT_ID: $GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET: $GOOGLE_CLIENT_SECRET + DATABASE_URL: postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@wpt-dev_postgres:5432/$POSTGRES_db + networks: + - web + depends_on: + wpt-dev_postgres: + condition: service_healthy + labels: + - "traefik.enable=true" + - "traefik.http.routers.wpt-dev-ws.rule=Host(`wpt-dev.baptiiiste.com`)" + - "traefik.http.routers.wpt-dev-ws.entrypoints=websecure" + - "traefik.http.routers.wpt-dev-ws.tls.certresolver=letsencrypt" + - "traefik.http.services.wpt-dev-ws.loadbalancer.server.port=3000" + + adminer: + image: adminer + container_name: wpt-dev_adminer + restart: always + depends_on: + - wpt-dev_postgres + networks: + - web + environment: + ADMINER_DEFAULT_SERVER: wpt-dev_postgres + labels: + - "traefik.enable=true" + - "traefik.http.routers.wpt-dev-adminer.rule=Host(`adminer-wpt-dev.baptiiiste.com`)" + - "traefik.http.routers.wpt-dev-adminer.entrypoints=websecure" + - "traefik.http.routers.wpt-dev-adminer.tls.certresolver=letsencrypt" + - "traefik.http.services.wpt-dev-adminer.loadbalancer.server.port=8080" + +networks: + web: + external: true +volumes: + wpt-dev_data: +``` + +What is deployed here on my server: +- PostgreSQL database container +- Next.js application container +- Adminer container for database management + +Access the application here: +- link: https://wpt-dev.baptiiiste.com +- user: test@test.com +- password: Test@1234 + +## Prod +[Pipeline for prod (.github/workflows/prod.yml)](./.github/workflows/prod.yml) + +On create tag `v*.*.*`: +- Build the application +- Run tests on the most important features [See tests (./tests)](tests) +- Build image using the Dockerfile and push Docker image to registry +- Deploy to production environment on private server + +Docker compose on my server: +```yml +version: '3.8' +services: + wpt_postgres: + image: postgres:16 + container_name: wpt_postgres + restart: always + environment: + POSTGRES_USER: $POSTGRES_USER + POSTGRES_PASSWORD: $POSTGRES_PASSWORD + POSTGRES_DB: $POSTGRES_DB + volumes: + - wpt_data:/var/lib/postgresql/data + networks: + - web + ports: + - "5433:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + wpt_website: + image: ghcr.io/baptiiiiste/webhookpersistenttester:latest + container_name: wpt_website + restart: always + environment: + NODE_ENV: production + NEXTAUTH_URL: https://wpt.baptiiiste.com + API_URL: https://wpt.baptiiiste.com/api + NEXTAUTH_SECRET: $NEXTAUTH_SECRET + SALT_ROUNDS: "10" + AUTH_TRUST_HOST: "true" + GOOGLE_CLIENT_ID: $GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET: $GOOGLE_CLIENT_SECRET + DATABASE_URL: postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@wpt_postgres:5432/$POSTGRES_db + networks: + - web + ports: + - "3001:3000" + depends_on: + wpt_postgres: + condition: service_healthy + labels: + - "traefik.enable=true" + - "traefik.http.routers.wpt-ws.rule=Host(`wpt.baptiiiste.com`)" + - "traefik.http.routers.wpt-ws.entrypoints=websecure" + - "traefik.http.routers.wpt-ws.tls.certresolver=letsencrypt" + - "traefik.http.services.wpt-ws.loadbalancer.server.port=3000" + +networks: + web: + external: true +volumes: + wpt_data: +``` + +What is deployed here on my server: +- PostgreSQL database container +- Next.js application container + +Access the application here: +- link: https://wpt-dev.baptiiiste.com +- create your own account to test, with not sensitive information + +## Deployment on Private Server + +- Setup Github runners on private server +- Created a `docker-compose.yml` file for each environment (staging and production) +- Used the runner to pull the latest Docker image and restart the container + +Server architecture: +- Ubuntu server +- Portainer for container management +- Traefik as reverse proxy (application are using the `web` network that is linked to my domain) +- Docker and Docker Compose for containerization +- Let's Encrypt for SSL certificates + + diff --git a/TODO.md b/TODO.md index ec5ab5b..17837bc 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,7 @@ - Handle stripe integration - Handle billing page - Handle plan selection +- Fix missing translations (Sidebar, disconnect button) # Future features - Auto deletion of requests after a certain period of time diff --git a/lib/i18n/routing.ts b/lib/i18n/routing.ts index bc8f0b1..6471c55 100644 --- a/lib/i18n/routing.ts +++ b/lib/i18n/routing.ts @@ -1,7 +1,7 @@ import { defineRouting } from 'next-intl/routing' export const routing = defineRouting({ - locales: ['fr'], - defaultLocale: 'fr', + locales: ['fr', 'en'], + defaultLocale: 'en', localePrefix: 'always', }) diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..e52be6d --- /dev/null +++ b/messages/en.json @@ -0,0 +1,247 @@ +{ + "Configuration": { + "Plans": { + "FREE": "Free", + "PRO": "Pro", + "ADMIN": "Administrator", + "Upgrade": "Upgrade" + } + }, + "CopyButton": { + "Copy": "Copy", + "Copied": "Copied" + }, + "PricingPage": { + "Title": "Pricing", + "Description": "Plans and pricing", + "TODO": "The payment system has been disabled for the moment. The free plan has been temporarily upgraded to the Pro plan for all users." + }, + "LandingPage": { + "Buttons": { + "SignIn": "Sign in", + "CTA": "Get your webhook URL" + }, + "Slogan": { + "Part1": "Debug your webhooks with", + "Part2": "persistent storage" + }, + "Description": "Inspect payloads, replay requests, and monitor your webhooks in real-time. Never lose a webhook event again thanks to automatic persistence.", + "FeatureCards": { + "Feature1": { + "Title": "Inspect payloads", + "Description": "View full webhook payloads with syntax highlighting and search functionality." + }, + "Feature2": { + "Title": "Replay requests", + "Description": "Replay any webhook to your endpoint for easy testing and debugging." + }, + "Feature3": { + "Title": "Monitor in real-time", + "Description": "Track webhook delivery, response times, and success rates with live analytics." + } + }, + "Trusted": { + "Badge": "Trusted by developers", + "Title": "Built for developer workflows", + "Description": "Thousands of developers trust WPT to debug and monitor their webhook integrations." + }, + "Documentation": { + "Badge": "Platform features", + "Title": "Everything you need to test your webhooks", + "Description": "Powerful features designed for developers looking for reliable webhook testing and debugging tools.", + "Features": { + "Feature1": { + "Title": "Schedule your times", + "Description": "Explore your data, build your dashboard, and bring your team together." + }, + "Feature2": { + "Title": "From data to insights in minutes", + "Description": "Turn your raw data into actionable insights with powerful analytics tools." + }, + "Feature3": { + "Title": "Collaborate seamlessly", + "Description": "Work together in real-time with your team and share insights instantly." + } + } + }, + "Pricing": { + "Badge": "Plans and pricing", + "Title": "Simple and transparent pricing", + "Description": "Start testing your webhooks for free. Upgrade to Pro for advanced features and higher limits when you need them.", + "Billing": { + "Monthly": "Monthly", + "Annually": "Yearly", + "BilledMonthly": "per month", + "BilledAnnually": "per month, billed annually", + "Forever": "forever" + }, + "Plans": { + "FREE": "Perfect for testing and small projects.", + "PRO": "Advanced features.", + "Popular": "POPULAR" + }, + "Advantages": { + "RequestPerMonth": "requests per month", + "WebhookCount": "webhook", + "History": "days of request history", + "ReplayRequest": "Replay your requests" + }, + "CTA": { + "FREE": "Start for free", + "PRO": "Get started" + } + }, + "CTA": { + "FREE": "Start for free", + "Title": "Ready to transform your business?", + "Description": "Join thousands of companies optimizing their operations, managing their schedules, and growing with data-driven insights." + }, + "Footer": { + "Legal": "Legal Notice", + "AllRightsReserved": "All rights reserved", + "Terms": "Terms of Service", + "Privacy": "Privacy Policy" + }, + "FAQ": { + "Title": "Frequently Asked Questions", + "Description": "Explore your data, build your dashboard, and bring your team together." + } + }, + "Datatable": { + "LinesPerPage": "Rows per page", + "Amount": "{count, plural, one {# item} other {# items}}", + "PageNumber": "Page {page} of {pages}", + "NoData": "No results", + "NA": "N/A" + }, + "DeleteConfirmationModal": { + "Confirmation": "Confirmation", + "Cancel": "Cancel", + "Confirm": "Confirm", + "Error": "An error occurred" + }, + "Auth": { + "SignInPage": { + "Title": "Welcome back", + "Subtitle": "Sign in with your credentials", + "Methods": { + "Google": "Sign in with Google", + "Github": "Sign in with Github", + "Other": "Or sign in with" + }, + "Form": { + "Email": "Email address", + "Password": "Password", + "Submit": "Sign in", + "Errors": { + "InvalidCredentials": "Invalid credentials", + "GenericError": "An error occurred, please try again later" + } + }, + "Footer": { + "NoAccount": "Don't have an account? ", + "SignUp": "Sign up" + } + }, + "SignUpPage": { + "Title": "Welcome", + "Subtitle": "Sign up with your credentials", + "Methods": { + "Google": "Sign up with Google", + "Github": "Sign up with Github", + "Other": "Or sign up with" + }, + "Form": { + "Username": "Username", + "Email": "Email address", + "Password": "Password", + "Submit": "Sign up", + "Errors": { + "EmailAlreadyTaken": "This email address is already taken", + "GenericError": "An error occurred, please try again later" + } + }, + "Footer": { + "AlreadyHaveAccount": "Already have an account? ", + "SignIn": "Sign in" + } + }, + "Footer": { + "Title": "By signing in, you agree to our", + "Terms": "Terms of Service", + "AndConditions": " and our ", + "Privacy": "Privacy Policy" + } + }, + "DashboardPage": { + "Title": "Dashboard", + "Description": "General information", + "InfoCard": { + "RequestsCount": { + "Title": "Total Requests", + "Description": "Total number of requests made" + }, + "WebhooksCount": { + "Title": "Total Webhooks", + "Description": "Total number of webhooks created" + }, + "Plan": { + "Title": "Your Plan", + "Description": "Details of your current plan" + }, + "Webhooks": { + "Title": "My Webhooks", + "Description": "List of your active webhooks" + }, + "Requests": { + "Title": "Recent Request Activity", + "Description": "Number of requests over the last 7 days" + } + } + }, + "WebhookPage": { + "Title": "Webhooks", + "Description": "Webhook management", + "MaxWebhooks": "Number of webhooks for your plan", + "Usage": { + "Title": "How to use the webhook?", + "Description": "Copy your webhook URL below and use it in your application to send requests to it. Every request sent will be logged and displayed in the table below.", + "CurlIndication": "Usage example with curl" + }, + "Datatable": { + "Name": "Name", + "URL": "URL", + "RequestsCount": "Request count", + "DeleteMessage": "Are you sure you want to delete this webhook? This action is irreversible and will delete all related requests." + }, + "Errors": { + "404": "The webhook was not found", + "400": "Cannot delete the webhook, it is being used elsewhere", + "UNKNOWN": "An error occurred" + }, + "WebhookDetail": { + "Datatable": { + "Method": "Method", + "Origin": "Origin", + "Ip": "IP Address", + "DeleteMessage": "Are you sure you want to delete this request? This action is irreversible." + } + } + }, + "AdminPage": { + "Description": "Data management & statistics" + }, + "RequestDetailSheet": { + "Title": "Request Details #{requestId}", + "GlobalInfo": { + "Title": "General Information", + "Ip": "IP Address", + "Size": "Content size", + "UserAgent": "User-Agent", + "Origin": "Origin" + }, + "Headers": "Headers", + "Body": "Body", + "QueryParams": "Query Params" + } +} \ No newline at end of file diff --git a/messages/fr-old.json b/messages/fr-old.json deleted file mode 100644 index 489f7b0..0000000 --- a/messages/fr-old.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "Configuration": { - "Plans": { - "FREE": "Gratuit", - "PRO": "Pro", - "ADMIN": "Administrateur", - "Upgrade": "Améliorer" - } - }, - "CopyButton": { - "Copy": "Copier", - "Copied": "Copié" - }, - "LandingPage": { - "Buttons": { - "SignIn": "Sign in", - "CTA": "Get your webhook URL" - }, - "Slogan": { - "Part1": "Debug webhooks with", - "Part2": "persistent storage" - }, - "Description": { - "Part1": "Inspect payloads, replay requests, and monitor webhooks in real-time.", - "Part2": "Never lose a webhook event again with automatic persistence." - }, - "FeatureCards": { - "Feature1": { - "Title": "Inspect payloads", - "Description": "View complete webhook payloads with syntax highlighting and search capabilities." - }, - "Feature2": { - "Title": "Replay requests", - "Description": "Replay any webhook to your endpoint for testing and debugging purposes." - }, - "Feature3": { - "Title": "Monitor in real-time", - "Description": "Track webhook delivery, response times, and success rates with live analytics." - } - }, - "Trusted": { - "Badge": "Trusted by developers", - "Title": "Built for developer workflows", - "Description": { - "Part1": "Thousands of developers rely on", - "Part2": "to debug and monitor their webhook integrations." - } - }, - "Documentation": { - "Badge": "Platform features", - "Title": "Everything you need to test webhooks", - "Description": { - "Part1": "Powerful features designed for developers who need", - "Part2": "reliable webhook testing and debugging tools." - }, - "Features": { - "Feature1": { - "Title": "Plan your schedules", - "Description": "Explore your data, build your dashboard, bring your team together." - }, - "Feature2": { - "Title": "Data to insights in minutes", - "Description": "Transform raw data into actionable insights with powerful analytics tools." - }, - "Feature3": { - "Title": "Collaborate seamlessly", - "Description": "Work together in real-time with your team and share insights instantly." - } - } - }, - "Pricing": { - "Badge": "Plans & Pricing", - "Title": "Simple, transparent pricing", - "Description": { - "Part1": "Start testing webhooks for free. Upgrade to Pro for advanced features", - "Part2": "and higher limits when you need them." - }, - "Billing": { - "Monthly": "Monthly", - "Annually" : "Annualy", - "BilledMonthly": "per month", - "BilledAnnually": "per month, billed annually", - "Forever": "forever" - }, - "Plans": { - "FREE" : "Perfect for testing and small projects.", - "PRO": "Advanced features for production use.", - "Popular": "POPULAR" - }, - "Advantages": { - "RequestPerMonth": "requests per month", - "WebhookCount": "webhook", - "History": "day request history", - "ReplayRequest": "Replay your requests" - }, - "CTA": { - "FREE": "Start for free", - "PRO": "Get started" - } - }, - "CTA": { - "FREE": "Start for free", - "Title": "Ready to transform your business?", - "Description": { - "Part1": "Join thousands of businesses streamlining their operations", - "Part2": "managing schedules, and growing with data-driven insights." - } - }, - "Footer": { - "Legal": "Legal", - "AllRightsReserved": "All rights reserved", - "Terms": "Conditions d'utilisation", - "Privacy": "Politique de confidentialité" - }, - "FAQ": { - "Title": "Frequently Asked Questions", - "Description": { - "Part1": "Explore your data, build your dashboard,", - "Part2": "bring your team together." - } - } - }, - "Datatable" : { - "LinesPerPage": "Lignes par page", - "Amount": "{count, plural, one {# élément} other {# éléments}}", - "PageNumber": "Page {page} sur {pages}", - "NoData": "Aucun résultat", - "NA": "N/A" - }, - "DeleteConfirmationModal": { - "Confirmation": "Confirmation", - "Cancel": "Annuler", - "Confirm": "Confirmer", - "Error": "Une erreur est survenue" - }, - "Auth": { - "SignInPage": { - "Title": "Vous revoilà", - "Subtitle": "Connectez-vous avec vos identifiants", - "Methods": { - "Google": "Se connecter avec Google", - "Github": "Se connecter avec Github", - "Other": "Ou se connecter avec" - }, - "Form": { - "Email": "Adresse e-mail", - "Password": "Mot de passe", - "Submit": "Se connecter", - "Errors": { - "InvalidCredentials": "Identifiants invalides", - "GenericError": "Une erreur est survenue, veuillez réessayer plus tard" - } - }, - "Footer": { - "NoAccount": "Vous n'avez pas de compte ? ", - "SignUp": "S'inscrire" - } - }, - "SignUpPage": { - "Title": "Bienvenue", - "Subtitle": "Inscrivez-vous avec vos identifiants", - "Methods": { - "Google": "Se connecter avec Google", - "Github": "Se connecter avec Github", - "Other": "Ou s'inscrire avec" - }, - "Form": { - "Username": "Pseudo", - "Email": "Adresse e-mail", - "Password": "Mot de passe", - "Submit": "S'inscrire", - "Errors": { - "EmailAlreadyTaken": "Cette adresse e-mail est déjà utilisée", - "GenericError": "Une erreur est survenue, veuillez réessayer plus tard" - } - }, - "Footer": { - "AlreadyHaveAccount": "Vous avez déjà un compte ? ", - "SignIn": "Se connecter" - } - }, - "Footer": { - "Title": "En vous connectant, vous acceptez nos", - "Terms": "Conditions d'utilisation", - "AndConditions": " et notre ", - "Privacy": "Politique de confidentialité" - } - }, - "DashboardPage": { - "Description": "Informations générales", - "InfoCard": { - "RequestsCount": { - "Title": "Requêtes totales", - "Description": "Nombre total de requêtes effectuées" - }, - "WebhooksCount": { - "Title": "Webhooks totaux", - "Description": "Nombre total de webhooks créés" - }, - "Plan": { - "Title": "Votre plan", - "Description": "Détails de votre plan actuel" - }, - "Webhooks": { - "Title": "Mes webhooks", - "Description": "Liste de vos webhooks actifs" - }, - "Requests": { - "Title": "Activité récente des requêtes", - "Description": "Nombre de requêtes au cours des 7 derniers jours" - } - } - }, - "WebhookPage": { - "Description": "Gestion des webhooks", - "MaxWebhooks": "Nombre de webhooks pour votre plan", - "Usage" : { - "Title": "Comment utiliser le webhook ?", - "Description": "Copiez l'url de votre webhook ci-dessous et utilisez-la dans votre application pour y envoyer des requêtes. Chaque requête envoyée sera enregistrée et affichée dans le tableau ci-dessous.", - "CurlIndication": "Exemple d'utilisation avec curl" - }, - "Datatable" : { - "Name": "Nom", - "URL": "URL", - "RequestsCount": "Nombre de requêtes", - "DeleteMessage": "Êtes-vous sûr de vouloir supprimer ce webhook ? Cette action est irréversible et supprimera toutes les requêtes liées." - }, - "Errors": { - "404": "Le webhook n'a pas été trouvé" , - "400": "Impossible de supprimer le webhook, il est utilisé ailleurs", - "UNKNOWN": "Une erreur est survenue" - }, - "WebhookDetail": { - "Datatable": { - "Method": "Méthode", - "Origin": "Origine", - "Ip": "Adresse IP", - "DeleteMessage": "Êtes-vous sûr de vouloir supprimer cette requête ? Cette action est irréversible." - } - } - }, - "AdminPage": { - "Description": "Gestion des données & statistiques" - }, - "RequestDetailSheet": { - "Title": "Détails de la requête #{requestId}", - "GlobalInfo": { - "Title": "Informations générales", - "Ip": "Addresse IP", - "Size": "Taille du contenu", - "UserAgent": "User-Agent", - "Origin": "Origine" - }, - "Headers": "Headers", - "Body": "Body", - "QueryParams": "Query Params" - } -}