Compare commits
9 Commits
v2023.7.5-
...
v2023.7.6-
Author | SHA1 | Date | |
---|---|---|---|
33a9787081 | |||
553513d647 | |||
60487c11d6 | |||
e6f18e7cd8 | |||
a207291c04 | |||
64b5182f8b | |||
ce2c19f9b3 | |||
1ffec1f173 | |||
aab5452fa2 |
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@ -34,6 +34,7 @@ pipeline {
|
|||||||
anyOf {
|
anyOf {
|
||||||
branch 'master'
|
branch 'master'
|
||||||
branch 'develop'
|
branch 'develop'
|
||||||
|
branch 'ci-docker-release'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
@ -46,6 +47,9 @@ pipeline {
|
|||||||
])
|
])
|
||||||
]) {
|
]) {
|
||||||
sh 'make gitea-release'
|
sh 'make gitea-release'
|
||||||
|
sh '''
|
||||||
|
make docker-build docker-release
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,15 @@
|
|||||||
## Référence
|
## Référence
|
||||||
|
|
||||||
- [(FR) - Layers](./fr/references/layers/README.md)
|
- [(FR) - Layers](./fr/references/layers/README.md)
|
||||||
- [Fichier de configuration](../misc/packaging/common/config.yml)
|
- [(FR) - Fichier de configuration](../misc/packaging/common/config.yml)
|
||||||
|
|
||||||
## Tutoriels
|
## Tutoriels
|
||||||
|
|
||||||
### Utilisation
|
### Utilisation
|
||||||
|
|
||||||
- [(FR) - Ajouter un calque de type "file d'attente"](./fr/tutorials/add-queue-layer.md)
|
- [(FR) - Ajouter un layer de type "file d'attente"](./fr/tutorials/add-queue-layer.md)
|
||||||
|
|
||||||
### Développement
|
### Développement
|
||||||
|
|
||||||
- [(FR) - Démarrer avec les sources](./fr/tutorials/getting-start-with-sources.md)
|
- [(FR) - Démarrer avec les sources](./fr/tutorials/getting-started-with-sources.md)
|
||||||
- [(FR) - Créer son propre layer](./fr/tutorials/create-custom-layer.md)
|
- [(FR) - Créer son propre layer](./fr/tutorials/create-custom-layer.md)
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
Vous trouverez ci-dessous la liste des entités "Layer" activables sur vos entité "Proxy":
|
Vous trouverez ci-dessous la liste des entités "Layer" activables sur vos entité "Proxy":
|
||||||
|
|
||||||
- [Queue](./queue.md) - File d'attente dynamique
|
- [Queue](./queue.md) - File d'attente dynamique
|
||||||
|
- [Circuit Breaker](./circuitbreaker.md) - Coupure d'accès à un site ou une sous section de celui ci
|
37
doc/fr/references/layers/circuitbreaker.md
Normal file
37
doc/fr/references/layers/circuitbreaker.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Layer "Circuit Breaker"
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Ce layer permet de bloquer l'accès à un site (ou une section de celui ci) ciblé par un proxy.
|
||||||
|
|
||||||
|
## Type
|
||||||
|
|
||||||
|
`circuitbreaker`
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### `authorizedCIDRs`
|
||||||
|
|
||||||
|
- **Type:** `[]string`
|
||||||
|
- **Valeur par défaut:** `[]`
|
||||||
|
- **Description:** Autoriser les adresses distantes contenues dans un des masques réseau (en notation ["CIDR"](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) définis à contourner la restriction d'accès.
|
||||||
|
|
||||||
|
### `matchURLs`
|
||||||
|
|
||||||
|
- **Type:** `[]string`
|
||||||
|
- **Valeur par défaut:** `["*"]`
|
||||||
|
- **Description:** Limiter l'action du layer à cette liste de patrons d'URLs.
|
||||||
|
|
||||||
|
Par exemple, si vous souhaitez limiter votre restriction d'accès à l'ensemble d'une section "`/blog`" d'un site, vous pouvez déclarer la valeur `["*/blog*"]`. Les autres URLs du site ne seront pas affectées par la restriction.
|
||||||
|
|
||||||
|
### `templateBlock`
|
||||||
|
|
||||||
|
- **Type:** `string`
|
||||||
|
- **Valeur par défaut:** `"default"`
|
||||||
|
- **Description:** Bloc du template HTML pour effectuer le rendu de la page indiquant la restriction d'accès.
|
||||||
|
|
||||||
|
Voir le [fichier de configuration de référence](../../../../misc/packaging/common/config.yml), section `layers.circuitbreaker` pour voir les options permettant de personnaliser le chemin du répertoire contenant les templates.
|
||||||
|
|
||||||
|
### Schéma
|
||||||
|
|
||||||
|
Voir le [schéma JSON](../../../../internal/proxy/director/layer/circuitbreaker/layer-options.json).
|
@ -22,6 +22,14 @@ Ce layer permet d'ajouter un mécanisme de file d'attente dynamique au proxy ass
|
|||||||
- **Valeur par défaut:** `1m`
|
- **Valeur par défaut:** `1m`
|
||||||
- **Description:** Durée de vie d'une session dans la file d'attente sans activité avant expiration.
|
- **Description:** Durée de vie d'une session dans la file d'attente sans activité avant expiration.
|
||||||
|
|
||||||
|
### `matchURLs`
|
||||||
|
|
||||||
|
- **Type:** `[]string`
|
||||||
|
- **Valeur par défaut:** `["*"]`
|
||||||
|
- **Description:** Limiter l'action de la file d'attente à cette liste de patrons d'URLs.
|
||||||
|
|
||||||
|
Par exemple, si vous souhaitez limiter votre file à l'ensemble d'une section "`/blog`" d'un site, vous pouvez déclarer la valeur `["*/blog*"]`. Les autres URLs du site ne seront pas affectées par cette file d'attente.
|
||||||
|
|
||||||
### Schéma
|
### Schéma
|
||||||
|
|
||||||
Voir le [schéma JSON](../../../../internal/proxy/director/layer/queue/schema/layer-options.json).
|
Voir le [schéma JSON](../../../../internal/proxy/director/layer/queue/schema/layer-options.json).
|
11
go.mod
11
go.mod
@ -6,7 +6,7 @@ require (
|
|||||||
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6
|
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.3
|
github.com/btcsuite/btcd/btcutil v1.1.3
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/getsentry/sentry-go v0.22.0
|
||||||
github.com/go-chi/chi/v5 v5.0.8
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/jedib0t/go-pretty/v6 v6.4.6
|
github.com/jedib0t/go-pretty/v6 v6.4.6
|
||||||
github.com/mitchellh/mapstructure v1.4.1
|
github.com/mitchellh/mapstructure v1.4.1
|
||||||
@ -53,11 +53,12 @@ require (
|
|||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
golang.org/x/text v0.9.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
|
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
@ -71,12 +72,12 @@ require (
|
|||||||
github.com/dlclark/regexp2 v1.9.0 // indirect
|
github.com/dlclark/regexp2 v1.9.0 // indirect
|
||||||
github.com/fatih/color v1.15.0 // indirect
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-playground/locales v0.12.1 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/leodido/go-urn v1.1.0 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
||||||
|
18
go.sum
18
go.sum
@ -178,19 +178,24 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD
|
|||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/getsentry/sentry-go v0.22.0 h1:XNX9zKbv7baSEI65l+H1GEJgSeIC1c7EN5kluWaP6dM=
|
||||||
|
github.com/getsentry/sentry-go v0.22.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
|
||||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||||
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||||
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
@ -314,8 +319,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
|
||||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||||
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
|
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
|
||||||
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||||
@ -382,6 +388,7 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
|
|||||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||||
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@ -419,8 +426,9 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
|
|||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
@ -667,6 +675,7 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -695,6 +704,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/schema"
|
"forge.cadoles.com/cadoles/bouncer/internal/schema"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ErrCodeAlreadyExist api.ErrorCode = "already-exist"
|
const ErrCodeAlreadyExist api.ErrorCode = "already-exist"
|
||||||
@ -29,3 +32,8 @@ func invalidDataErrorResponse(w http.ResponseWriter, r *http.Request, err *schem
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logAndCaptureError(ctx context.Context, message string, err error) {
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
logger.Error(ctx, message, logger.E(err))
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@ -10,7 +11,6 @@ import (
|
|||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type QueryLayerResponse struct {
|
type QueryLayerResponse struct {
|
||||||
@ -38,7 +38,7 @@ func (s *Server) queryLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "could not list layers", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not list layers", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -85,7 +85,7 @@ func (s *Server) getLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not get layer", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not get layer", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -120,7 +120,7 @@ func (s *Server) deleteLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not delete layer", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not delete layer", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -156,7 +156,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
layerName, err := store.ValidateName(createLayerReq.Name)
|
layerName, err := store.ValidateName(createLayerReq.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "invalid 'name' parameter", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "invalid 'name' parameter", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -165,7 +165,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
layerType := store.LayerType(createLayerReq.Type)
|
layerType := store.LayerType(createLayerReq.Type)
|
||||||
|
|
||||||
if !setup.LayerTypeExists(layerType) {
|
if !setup.LayerTypeExists(layerType) {
|
||||||
logger.Error(r.Context(), "unknown layer type", logger.E(errors.WithStack(err)), logger.F("layerType", layerType))
|
logAndCaptureError(ctx, fmt.Sprintf("unknown layer type '%s'", layerType), errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -179,7 +179,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not create layer", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not create layer", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -223,7 +223,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not get layer", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not get layer", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -247,7 +247,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
if updateLayerReq.Options != nil {
|
if updateLayerReq.Options != nil {
|
||||||
layerOptionsSchema, err := setup.GetLayerOptionsSchema(layer.Type)
|
layerOptionsSchema, err := setup.GetLayerOptionsSchema(layer.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not retrieve layer options schema", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not retrieve layer options schema", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -258,7 +258,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}(updateLayerReq.Options)
|
}(updateLayerReq.Options)
|
||||||
|
|
||||||
if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil {
|
if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil {
|
||||||
logger.Error(r.Context(), "could not validate layer options", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not validate layer options", errors.WithStack(err))
|
||||||
|
|
||||||
var invalidDataErr *schema.InvalidDataError
|
var invalidDataErr *schema.InvalidDataError
|
||||||
if errors.As(err, &invalidDataErr) {
|
if errors.As(err, &invalidDataErr) {
|
||||||
@ -286,7 +286,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not update layer", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not update layer", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -300,21 +300,7 @@ func getLayerName(w http.ResponseWriter, r *http.Request) (store.LayerName, bool
|
|||||||
|
|
||||||
name, err := store.ValidateName(rawLayerName)
|
name, err := store.ValidateName(rawLayerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse layer name", logger.E(errors.WithStack(err)))
|
logAndCaptureError(r.Context(), "could not parse layer name", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.LayerName(name), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func geLayerName(w http.ResponseWriter, r *http.Request) (store.LayerName, bool) {
|
|
||||||
rawLayerName := chi.URLParam(r, "layerName")
|
|
||||||
|
|
||||||
name, err := store.ValidateName(rawLayerName)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(r.Context(), "could not parse layer name", logger.E(errors.WithStack(err)))
|
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return "", false
|
return "", false
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type QueryProxyResponse struct {
|
type QueryProxyResponse struct {
|
||||||
@ -37,7 +36,7 @@ func (s *Server) queryProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "could not list proxies", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not list proxies", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -79,7 +78,7 @@ func (s *Server) getProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not get proxy", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not get proxy", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -109,7 +108,7 @@ func (s *Server) deleteProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not delete proxy", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not delete proxy", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -140,14 +139,14 @@ func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
name, err := store.ValidateName(createProxyReq.Name)
|
name, err := store.ValidateName(createProxyReq.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse 'name' parameter", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not parse 'name' parameter", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := url.Parse(createProxyReq.To); err != nil {
|
if _, err := url.Parse(createProxyReq.To); err != nil {
|
||||||
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not parse 'to' parameter", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -161,7 +160,7 @@ func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not create proxy", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not create proxy", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -207,7 +206,7 @@ func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
if updateProxyReq.To != nil {
|
if updateProxyReq.To != nil {
|
||||||
_, err := url.Parse(*updateProxyReq.To)
|
_, err := url.Parse(*updateProxyReq.To)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not parse 'to' parameter", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -235,7 +234,7 @@ func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(ctx, "could not update proxy", logger.E(errors.WithStack(err)))
|
logAndCaptureError(ctx, "could not update proxy", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -249,7 +248,7 @@ func getProxyName(w http.ResponseWriter, r *http.Request) (store.ProxyName, bool
|
|||||||
|
|
||||||
name, err := store.ValidateName(rawProxyName)
|
name, err := store.ValidateName(rawProxyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse proxy name", logger.E(errors.WithStack(err)))
|
logAndCaptureError(r.Context(), "could not parse proxy name", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return "", false
|
return "", false
|
||||||
@ -263,7 +262,7 @@ func getIntQueryParam(w http.ResponseWriter, r *http.Request, param string, defa
|
|||||||
if rawValue != "" {
|
if rawValue != "" {
|
||||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse int param", logger.F("param", param), logger.E(errors.WithStack(err)))
|
logAndCaptureError(r.Context(), "could not parse int param", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return 0, false
|
return 0, false
|
||||||
@ -296,7 +295,7 @@ func getStringableSliceValues[T ~string](w http.ResponseWriter, r *http.Request,
|
|||||||
for _, rv := range rawValues {
|
for _, rv := range rawValues {
|
||||||
v, err := validate(rv)
|
v, err := validate(rv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(r.Context(), "could not parse ids slice param", logger.F("param", param), logger.E(errors.WithStack(err)))
|
logAndCaptureError(r.Context(), "could not parse ids slice param", errors.WithStack(err))
|
||||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -9,9 +9,11 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/auth"
|
"forge.cadoles.com/cadoles/bouncer/internal/auth"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
|
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
|
||||||
|
bouncerChi "forge.cadoles.com/cadoles/bouncer/internal/chi"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
|
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
@ -90,7 +92,21 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
|
|
||||||
router := chi.NewRouter()
|
router := chi.NewRouter()
|
||||||
|
|
||||||
router.Use(middleware.Logger)
|
if s.serverConfig.HTTP.UseRealIP {
|
||||||
|
router.Use(middleware.RealIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
|
||||||
|
|
||||||
|
if s.serverConfig.Sentry.DSN != "" {
|
||||||
|
logger.Info(ctx, "enabling sentry http middleware")
|
||||||
|
|
||||||
|
sentryMiddleware := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Use(sentryMiddleware.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
corsMiddleware := cors.New(cors.Options{
|
corsMiddleware := cors.New(cors.Options{
|
||||||
AllowedOrigins: s.serverConfig.CORS.AllowedOrigins,
|
AllowedOrigins: s.serverConfig.CORS.AllowedOrigins,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -90,6 +91,8 @@ func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
|
||||||
debug := ctx.Bool("debug")
|
debug := ctx.Bool("debug")
|
||||||
|
|
||||||
if !debug {
|
if !debug {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/admin"
|
"forge.cadoles.com/cadoles/bouncer/internal/admin"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/command/common"
|
"forge.cadoles.com/cadoles/bouncer/internal/command/common"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/setup"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
@ -27,6 +28,14 @@ func RunCommand() *cli.Command {
|
|||||||
logger.SetFormat(logger.Format(conf.Logger.Format))
|
logger.SetFormat(logger.Format(conf.Logger.Format))
|
||||||
logger.SetLevel(logger.Level(conf.Logger.Level))
|
logger.SetLevel(logger.Level(conf.Logger.Level))
|
||||||
|
|
||||||
|
projectVersion := ctx.String("projectVersion")
|
||||||
|
flushSentry, err := setup.SetupSentry(ctx.Context, conf.Admin.Sentry, projectVersion)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not initialize sentry client")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer flushSentry()
|
||||||
|
|
||||||
srv := admin.NewServer(
|
srv := admin.NewServer(
|
||||||
admin.WithServerConfig(conf.Admin),
|
admin.WithServerConfig(conf.Admin),
|
||||||
admin.WithRedisConfig(conf.Redis),
|
admin.WithRedisConfig(conf.Redis),
|
||||||
|
@ -28,6 +28,14 @@ func RunCommand() *cli.Command {
|
|||||||
logger.SetFormat(logger.Format(conf.Logger.Format))
|
logger.SetFormat(logger.Format(conf.Logger.Format))
|
||||||
logger.SetLevel(logger.Level(conf.Logger.Level))
|
logger.SetLevel(logger.Level(conf.Logger.Level))
|
||||||
|
|
||||||
|
projectVersion := ctx.String("projectVersion")
|
||||||
|
flushSentry, err := setup.SetupSentry(ctx.Context, conf.Proxy.Sentry, projectVersion)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not initialize sentry client")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer flushSentry()
|
||||||
|
|
||||||
layers, err := setup.GetLayers(ctx.Context, conf)
|
layers, err := setup.GetLayers(ctx.Context, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not initialize director layers")
|
return errors.Wrap(err, "could not initialize director layers")
|
||||||
|
@ -5,6 +5,7 @@ type AdminServerConfig struct {
|
|||||||
CORS CORSConfig `yaml:"cors"`
|
CORS CORSConfig `yaml:"cors"`
|
||||||
Auth AuthConfig `yaml:"auth"`
|
Auth AuthConfig `yaml:"auth"`
|
||||||
Metrics MetricsConfig `yaml:"metrics"`
|
Metrics MetricsConfig `yaml:"metrics"`
|
||||||
|
Sentry SentryConfig `yaml:"sentry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultAdminServerConfig() AdminServerConfig {
|
func NewDefaultAdminServerConfig() AdminServerConfig {
|
||||||
@ -13,6 +14,7 @@ func NewDefaultAdminServerConfig() AdminServerConfig {
|
|||||||
CORS: NewDefaultCORSConfig(),
|
CORS: NewDefaultCORSConfig(),
|
||||||
Auth: NewDefaultAuthConfig(),
|
Auth: NewDefaultAuthConfig(),
|
||||||
Metrics: NewDefaultMetricsConfig(),
|
Metrics: NewDefaultMetricsConfig(),
|
||||||
|
Sentry: NewDefaultSentryConfig(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,29 @@ func (ii *InterpolatedInt) UnmarshalYAML(value *yaml.Node) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InterpolatedFloat float64
|
||||||
|
|
||||||
|
func (ifl *InterpolatedFloat) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
var str string
|
||||||
|
|
||||||
|
if err := value.Decode(&str); err != nil {
|
||||||
|
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||||
|
str = os.Getenv(match[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
floatVal, err := strconv.ParseFloat(str, 10)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not parse float '%v', line '%d'", str, value.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
*ifl = InterpolatedFloat(floatVal)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type InterpolatedBool bool
|
type InterpolatedBool bool
|
||||||
|
|
||||||
func (ib *InterpolatedBool) UnmarshalYAML(value *yaml.Node) error {
|
func (ib *InterpolatedBool) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type HTTPConfig struct {
|
type HTTPConfig struct {
|
||||||
Host InterpolatedString `yaml:"host"`
|
Host InterpolatedString `yaml:"host"`
|
||||||
Port InterpolatedInt `yaml:"port"`
|
Port InterpolatedInt `yaml:"port"`
|
||||||
|
UseRealIP InterpolatedBool `yaml:"useRealIP"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPConfig(host string, port int) HTTPConfig {
|
func NewHTTPConfig(host string, port int) HTTPConfig {
|
||||||
return HTTPConfig{
|
return HTTPConfig{
|
||||||
Host: InterpolatedString(host),
|
Host: InterpolatedString(host),
|
||||||
Port: InterpolatedInt(port),
|
Port: InterpolatedInt(port),
|
||||||
|
UseRealIP: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ package config
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type LayersConfig struct {
|
type LayersConfig struct {
|
||||||
Queue QueueLayerConfig `yaml:"queue"`
|
Queue QueueLayerConfig `yaml:"queue"`
|
||||||
|
CircuitBreaker CircuitBreakerLayerConfig `yaml:"circuitbreaker"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultLayersConfig() LayersConfig {
|
func NewDefaultLayersConfig() LayersConfig {
|
||||||
@ -12,6 +13,9 @@ func NewDefaultLayersConfig() LayersConfig {
|
|||||||
TemplateDir: "./layers/queue/templates",
|
TemplateDir: "./layers/queue/templates",
|
||||||
DefaultKeepAlive: NewInterpolatedDuration(time.Minute),
|
DefaultKeepAlive: NewInterpolatedDuration(time.Minute),
|
||||||
},
|
},
|
||||||
|
CircuitBreaker: CircuitBreakerLayerConfig{
|
||||||
|
TemplateDir: "./layers/circuitbreaker/templates",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,3 +23,7 @@ type QueueLayerConfig struct {
|
|||||||
TemplateDir InterpolatedString `yaml:"templateDir"`
|
TemplateDir InterpolatedString `yaml:"templateDir"`
|
||||||
DefaultKeepAlive *InterpolatedDuration `yaml:"defaultKeepAlive"`
|
DefaultKeepAlive *InterpolatedDuration `yaml:"defaultKeepAlive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CircuitBreakerLayerConfig struct {
|
||||||
|
TemplateDir InterpolatedString `yaml:"templateDir"`
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ type ProxyServerConfig struct {
|
|||||||
Metrics MetricsConfig `yaml:"metrics"`
|
Metrics MetricsConfig `yaml:"metrics"`
|
||||||
Transport TransportConfig `yaml:"transport"`
|
Transport TransportConfig `yaml:"transport"`
|
||||||
Dial DialConfig `yaml:"dial"`
|
Dial DialConfig `yaml:"dial"`
|
||||||
|
Sentry SentryConfig `yaml:"sentry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://pkg.go.dev/net/http#Transport
|
// See https://pkg.go.dev/net/http#Transport
|
||||||
@ -32,6 +33,7 @@ func NewDefaultProxyServerConfig() ProxyServerConfig {
|
|||||||
Metrics: NewDefaultMetricsConfig(),
|
Metrics: NewDefaultMetricsConfig(),
|
||||||
Transport: NewDefaultTransportConfig(),
|
Transport: NewDefaultTransportConfig(),
|
||||||
Dial: NewDefaultDialConfig(),
|
Dial: NewDefaultDialConfig(),
|
||||||
|
Sentry: NewDefaultSentryConfig(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
internal/config/sentry.go
Normal file
43
internal/config/sentry.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Sentry configuration
|
||||||
|
// See https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
|
||||||
|
type SentryConfig struct {
|
||||||
|
DSN InterpolatedString `yaml:"dsn"`
|
||||||
|
Debug InterpolatedBool `yaml:"debug"`
|
||||||
|
FlushTimeout *InterpolatedDuration `yaml:"flushTimeout"`
|
||||||
|
AttachStacktrace InterpolatedBool `yaml:"attachStacktrace"`
|
||||||
|
SampleRate InterpolatedFloat `yaml:"sampleRate"`
|
||||||
|
EnableTracing InterpolatedBool `yaml:"enableTracing"`
|
||||||
|
TracesSampleRate InterpolatedFloat `yaml:"tracesSampleRate"`
|
||||||
|
ProfilesSampleRate InterpolatedFloat `yaml:"profilesSampleRate"`
|
||||||
|
IgnoreErrors InterpolatedStringSlice `yaml:"ignoreErrors"`
|
||||||
|
SendDefaultPII InterpolatedBool `yaml:"sendDefaultPII"`
|
||||||
|
ServerName InterpolatedString `yaml:"serverName"`
|
||||||
|
Environment InterpolatedString `yaml:"environment"`
|
||||||
|
MaxBreadcrumbs InterpolatedInt `yaml:"maxBreadcrumbs"`
|
||||||
|
MaxSpans InterpolatedInt `yaml:"maxSpans"`
|
||||||
|
MaxErrorDepth InterpolatedInt `yaml:"maxErrorDepth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultSentryConfig() SentryConfig {
|
||||||
|
return SentryConfig{
|
||||||
|
DSN: "",
|
||||||
|
Debug: false,
|
||||||
|
FlushTimeout: NewInterpolatedDuration(2 * time.Second),
|
||||||
|
AttachStacktrace: true,
|
||||||
|
SampleRate: 1,
|
||||||
|
EnableTracing: true,
|
||||||
|
TracesSampleRate: 0.2,
|
||||||
|
ProfilesSampleRate: 1,
|
||||||
|
IgnoreErrors: []string{},
|
||||||
|
SendDefaultPII: false,
|
||||||
|
ServerName: "",
|
||||||
|
Environment: "",
|
||||||
|
MaxBreadcrumbs: 0,
|
||||||
|
MaxSpans: 1000,
|
||||||
|
MaxErrorDepth: 10,
|
||||||
|
}
|
||||||
|
}
|
43
internal/logger/writer.go
Normal file
43
internal/logger/writer.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
ctx context.Context
|
||||||
|
level logger.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements io.Writer.
|
||||||
|
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||||
|
w.log(string(p))
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) log(message string) {
|
||||||
|
switch w.level {
|
||||||
|
case logger.LevelDebug:
|
||||||
|
logger.Debug(w.ctx, message)
|
||||||
|
case logger.LevelInfo:
|
||||||
|
logger.Info(w.ctx, message)
|
||||||
|
case logger.LevelWarn:
|
||||||
|
logger.Warn(w.ctx, message)
|
||||||
|
case logger.LevelError:
|
||||||
|
logger.Error(w.ctx, message)
|
||||||
|
case logger.LevelCritical:
|
||||||
|
logger.Critical(w.ctx, message)
|
||||||
|
default:
|
||||||
|
logger.Debug(w.ctx, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(ctx context.Context, level logger.Level) *Writer {
|
||||||
|
return &Writer{ctx, level}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.Writer = &Writer{}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$id": "https://forge.cadoles.com/cadoles/bouncer/schemas/circuitbreaker-layer-options",
|
||||||
|
"title": "Circuit breaker layer options",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"matchURLs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authorizedCIDRs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"templateBlock": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
151
internal/proxy/director/layer/circuitbreaker/layer.go
Normal file
151
internal/proxy/director/layer/circuitbreaker/layer.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package circuitbreaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"html/template"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/go-proxy"
|
||||||
|
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
|
"github.com/Masterminds/sprig/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LayerType store.LayerType = "circuitbreaker"
|
||||||
|
|
||||||
|
type Layer struct {
|
||||||
|
templateDir string
|
||||||
|
loadOnce sync.Once
|
||||||
|
tmpl *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType implements director.MiddlewareLayer
|
||||||
|
func (l *Layer) LayerType() store.LayerType {
|
||||||
|
return LayerType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware implements director.MiddlewareLayer
|
||||||
|
func (l *Layer) Middleware(layer *store.Layer) proxy.Middleware {
|
||||||
|
return func(h http.Handler) http.Handler {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
options, err := fromStoreOptions(layer.Options)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "could not parse layer options", logger.E(errors.WithStack(err)))
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := l.matchAnyAuthorizedCIDRs(ctx, r.RemoteAddr, options.AuthorizedCIDRs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "could not match authorized cidrs", logger.E(errors.WithStack(err)))
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = wildcard.MatchAny(r.URL.String(), options.MatchURLs...)
|
||||||
|
if !matches {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.renderCircuitBreakerPage(w, r, layer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Layer) matchAnyAuthorizedCIDRs(ctx context.Context, remoteHostPort string, CIDRs []string) (bool, error) {
|
||||||
|
remoteHost, _, err := net.SplitHostPort(remoteHostPort)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteAddr := net.ParseIP(remoteHost)
|
||||||
|
if remoteAddr == nil {
|
||||||
|
return false, errors.Errorf("remote host '%s' is not a valid ip address", remoteHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rawCIDR := range CIDRs {
|
||||||
|
_, net, err := net.ParseCIDR(rawCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
match := net.Contains(remoteAddr)
|
||||||
|
if !match {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(ctx, "comparing remote host with authorized cidrs", logger.F("remoteAddr", remoteAddr))
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Layer) renderCircuitBreakerPage(w http.ResponseWriter, r *http.Request, layer *store.Layer, options *LayerOptions) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
pattern := filepath.Join(l.templateDir, "*.gohtml")
|
||||||
|
|
||||||
|
logger.Info(ctx, "loading circuit breaker page templates", logger.F("pattern", pattern))
|
||||||
|
|
||||||
|
tmpl, err := template.New("").Funcs(sprig.FuncMap()).ParseGlob(pattern)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, "could not load circuit breaker templates", logger.E(errors.WithStack(err)))
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
templateData := struct {
|
||||||
|
Layer *store.Layer
|
||||||
|
LayerOptions *LayerOptions
|
||||||
|
}{
|
||||||
|
Layer: layer,
|
||||||
|
LayerOptions: options,
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Cache-Control", "no-cache")
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
if err := tmpl.ExecuteTemplate(w, options.TemplateBlock, templateData); err != nil {
|
||||||
|
logger.Error(ctx, "could not render circuit breaker page", logger.E(errors.WithStack(err)))
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(funcs ...OptionFunc) *Layer {
|
||||||
|
opts := defaultOptions()
|
||||||
|
for _, fn := range funcs {
|
||||||
|
fn(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Layer{
|
||||||
|
templateDir: opts.TemplateDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ director.MiddlewareLayer = &Layer{}
|
@ -0,0 +1,36 @@
|
|||||||
|
package circuitbreaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LayerOptions struct {
|
||||||
|
MatchURLs []string `mapstructure:"matchURLs"`
|
||||||
|
AuthorizedCIDRs []string `mapstructure:"authorizedCIDRs"`
|
||||||
|
TemplateBlock string `mapstructure:"templateBlock"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromStoreOptions(storeOptions store.LayerOptions) (*LayerOptions, error) {
|
||||||
|
layerOptions := LayerOptions{
|
||||||
|
MatchURLs: []string{"*"},
|
||||||
|
AuthorizedCIDRs: []string{},
|
||||||
|
TemplateBlock: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
config := mapstructure.DecoderConfig{
|
||||||
|
Result: &layerOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := mapstructure.NewDecoder(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := decoder.Decode(storeOptions); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &layerOptions, nil
|
||||||
|
}
|
19
internal/proxy/director/layer/circuitbreaker/options.go
Normal file
19
internal/proxy/director/layer/circuitbreaker/options.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package circuitbreaker
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
TemplateDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OptionFunc func(*Options)
|
||||||
|
|
||||||
|
func defaultOptions() *Options {
|
||||||
|
return &Options{
|
||||||
|
TemplateDir: "./templates",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTemplateDir(templateDir string) OptionFunc {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.TemplateDir = templateDir
|
||||||
|
}
|
||||||
|
}
|
8
internal/proxy/director/layer/circuitbreaker/schema.go
Normal file
8
internal/proxy/director/layer/circuitbreaker/schema.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package circuitbreaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed layer-options.json
|
||||||
|
var RawLayerOptionsSchema []byte
|
@ -11,15 +11,15 @@ import (
|
|||||||
|
|
||||||
type LayerOptions struct {
|
type LayerOptions struct {
|
||||||
Capacity int64 `mapstructure:"capacity"`
|
Capacity int64 `mapstructure:"capacity"`
|
||||||
Matchers []string `mapstructure:"matchers"`
|
|
||||||
KeepAlive time.Duration `mapstructure:"keepAlive"`
|
KeepAlive time.Duration `mapstructure:"keepAlive"`
|
||||||
|
MatchURLs []string `mapstructure:"matchURLs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromStoreOptions(storeOptions store.LayerOptions, defaultKeepAlive time.Duration) (*LayerOptions, error) {
|
func fromStoreOptions(storeOptions store.LayerOptions, defaultKeepAlive time.Duration) (*LayerOptions, error) {
|
||||||
layerOptions := LayerOptions{
|
layerOptions := LayerOptions{
|
||||||
Capacity: 1000,
|
Capacity: 1000,
|
||||||
Matchers: []string{"*"},
|
|
||||||
KeepAlive: defaultKeepAlive,
|
KeepAlive: defaultKeepAlive,
|
||||||
|
MatchURLs: []string{"*"},
|
||||||
}
|
}
|
||||||
|
|
||||||
config := mapstructure.DecoderConfig{
|
config := mapstructure.DecoderConfig{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package queue
|
package queue
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
TemplateDir string
|
TemplateDir string
|
||||||
|
@ -7,11 +7,13 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/go-proxy"
|
"forge.cadoles.com/Cadoles/go-proxy"
|
||||||
|
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
@ -56,6 +58,13 @@ func (q *Queue) Middleware(layer *store.Layer) proxy.Middleware {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matches := wildcard.MatchAny(r.URL.String(), options.MatchURLs...)
|
||||||
|
if !matches {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer q.updateMetrics(ctx, layer.Proxy, layer.Name, options)
|
defer q.updateMetrics(ctx, layer.Proxy, layer.Name, options)
|
||||||
|
|
||||||
cookieName := q.getCookieName(layer.Name)
|
cookieName := q.getCookieName(layer.Name)
|
||||||
@ -181,6 +190,8 @@ func (q *Queue) renderQueuePage(w http.ResponseWriter, r *http.Request, queueNam
|
|||||||
RefreshRate: refreshRate,
|
RefreshRate: refreshRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Cache-Control", "no-cache")
|
||||||
|
w.Header().Add("Retry-After", strconv.FormatInt(int64(refreshRate.Seconds()), 10))
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
|
||||||
if err := q.tmpl.ExecuteTemplate(w, "queue", templateData); err != nil {
|
if err := q.tmpl.ExecuteTemplate(w, "queue", templateData); err != nil {
|
||||||
|
@ -9,6 +9,12 @@
|
|||||||
},
|
},
|
||||||
"keepAlive": {
|
"keepAlive": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"matchURLs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
@ -15,6 +15,8 @@ import (
|
|||||||
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -87,8 +89,22 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
s.directorLayers...,
|
s.directorLayers...,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if s.serverConfig.HTTP.UseRealIP {
|
||||||
|
router.Use(middleware.RealIP)
|
||||||
|
}
|
||||||
|
|
||||||
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
|
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
|
||||||
|
|
||||||
|
if s.serverConfig.Sentry.DSN != "" {
|
||||||
|
logger.Info(ctx, "enabling sentry http middleware")
|
||||||
|
|
||||||
|
sentryMiddleware := sentryhttp.New(sentryhttp.Options{
|
||||||
|
Repanic: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Use(sentryMiddleware.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
if s.serverConfig.Metrics.Enabled {
|
if s.serverConfig.Metrics.Enabled {
|
||||||
metrics := s.serverConfig.Metrics
|
metrics := s.serverConfig.Metrics
|
||||||
|
|
||||||
@ -169,7 +185,10 @@ func (s *Server) createReverseProxy(ctx context.Context, target *url.URL) *httpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) errorHandler(w http.ResponseWriter, r *http.Request, err error) {
|
func (s *Server) errorHandler(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
logger.Error(r.Context(), "proxy error", logger.E(errors.WithStack(err)))
|
err = errors.WithStack(err)
|
||||||
|
|
||||||
|
logger.Error(r.Context(), "proxy error", logger.E(err))
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
21
internal/setup/circuitbreaker_layer.go
Normal file
21
internal/setup/circuitbreaker_layer.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/circuitbreaker"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterLayer(circuitbreaker.LayerType, setupCircuitBreakerLayer, circuitbreaker.RawLayerOptionsSchema)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCircuitBreakerLayer(conf *config.Config) (director.Layer, error) {
|
||||||
|
options := []circuitbreaker.OptionFunc{
|
||||||
|
circuitbreaker.WithTemplateDir(string(conf.Layers.CircuitBreaker.TemplateDir)),
|
||||||
|
}
|
||||||
|
|
||||||
|
return circuitbreaker.New(
|
||||||
|
options...,
|
||||||
|
), nil
|
||||||
|
}
|
42
internal/setup/sentry.go
Normal file
42
internal/setup/sentry.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
|
loggerWriter "forge.cadoles.com/cadoles/bouncer/internal/logger"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupSentry(ctx context.Context, conf config.SentryConfig, release string) (func(), error) {
|
||||||
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: string(conf.DSN),
|
||||||
|
Debug: bool(conf.Debug),
|
||||||
|
AttachStacktrace: bool(conf.AttachStacktrace),
|
||||||
|
SampleRate: float64(conf.SampleRate),
|
||||||
|
EnableTracing: bool(conf.EnableTracing),
|
||||||
|
TracesSampleRate: float64(conf.TracesSampleRate),
|
||||||
|
ProfilesSampleRate: float64(conf.ProfilesSampleRate),
|
||||||
|
IgnoreErrors: conf.IgnoreErrors,
|
||||||
|
SendDefaultPII: bool(conf.SendDefaultPII),
|
||||||
|
ServerName: string(conf.ServerName),
|
||||||
|
Release: release,
|
||||||
|
Environment: string(conf.Environment),
|
||||||
|
MaxBreadcrumbs: int(conf.MaxBreadcrumbs),
|
||||||
|
MaxSpans: int(conf.MaxSpans),
|
||||||
|
MaxErrorDepth: int(conf.MaxErrorDepth),
|
||||||
|
DebugWriter: loggerWriter.NewWriter(ctx, logger.LevelDebug),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flush := func() {
|
||||||
|
sentry.Flush(time.Duration(*conf.FlushTimeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
return flush, nil
|
||||||
|
}
|
73
layers/circuitbreaker/templates/default.gohtml
Normal file
73
layers/circuitbreaker/templates/default.gohtml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{{ define "default" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Accès bloqué - {{ .Layer.Name }}</title>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, h1, h2, h3, h4, h5, h6, p, ol, ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#card {
|
||||||
|
padding: 1.5em 1em;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 2px 2px #cccccc1c;
|
||||||
|
color: #333333 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 0.7em;
|
||||||
|
margin-top: 2em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="card">
|
||||||
|
<h2 class="title">Page indisponible</h2>
|
||||||
|
<p>La page à laquelle vous souhaitez accéder est actuellement indisponible.</p>
|
||||||
|
<p class="footer">Propulsé par <a href="https://forge.cadoles.com/Cadoles/bouncer">Bouncer</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
@ -6,6 +6,9 @@ admin:
|
|||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
# Port d'écoute du service
|
# Port d'écoute du service
|
||||||
port: 8081
|
port: 8081
|
||||||
|
# Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For
|
||||||
|
# pour le calcul de l'adresse distante à l'origine des requêtes
|
||||||
|
useRealIP: true
|
||||||
|
|
||||||
# Configuration CORS du service
|
# Configuration CORS du service
|
||||||
# Uniquement nécessaire si un frontend web
|
# Uniquement nécessaire si un frontend web
|
||||||
@ -45,6 +48,25 @@ admin:
|
|||||||
# de publication
|
# de publication
|
||||||
# Mettre à null pour désactiver l'authentification
|
# Mettre à null pour désactiver l'authentification
|
||||||
basicAuth: null
|
basicAuth: null
|
||||||
|
|
||||||
|
# Configuration de l'intégration Sentry
|
||||||
|
# Voir https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
|
||||||
|
sentry:
|
||||||
|
dsn: ""
|
||||||
|
debug: false
|
||||||
|
flushTimeout: 2s
|
||||||
|
attachStacktrace: true
|
||||||
|
sampleRate: 1
|
||||||
|
enableTracing: true
|
||||||
|
tracesSampleRate: 0.2
|
||||||
|
profilesSampleRate: 1
|
||||||
|
ignoreErrors: []
|
||||||
|
sendDefaultPII: false
|
||||||
|
serverName: ""
|
||||||
|
environment: ""
|
||||||
|
maxBreadcrumbs: 0
|
||||||
|
maxSpans: 1000
|
||||||
|
maxErrorDepth: 10
|
||||||
|
|
||||||
# Configuration du service "proxy"
|
# Configuration du service "proxy"
|
||||||
proxy:
|
proxy:
|
||||||
@ -54,6 +76,9 @@ proxy:
|
|||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
# Port d'écoute du service
|
# Port d'écoute du service
|
||||||
port: 8080
|
port: 8080
|
||||||
|
# Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For
|
||||||
|
# pour le calcul de l'adresse distante à l'origine des requêtes
|
||||||
|
useRealIP: true
|
||||||
|
|
||||||
# Métriques Prometheus
|
# Métriques Prometheus
|
||||||
metrics:
|
metrics:
|
||||||
@ -85,6 +110,25 @@ proxy:
|
|||||||
readBufferSize: 4096
|
readBufferSize: 4096
|
||||||
maxResponseHeaderBytes: 0
|
maxResponseHeaderBytes: 0
|
||||||
|
|
||||||
|
# Configuration de l'intégration Sentry
|
||||||
|
# Voir https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
|
||||||
|
sentry:
|
||||||
|
dsn: ""
|
||||||
|
debug: false
|
||||||
|
flushTimeout: 2s
|
||||||
|
attachStacktrace: true
|
||||||
|
sampleRate: 1
|
||||||
|
enableTracing: true
|
||||||
|
tracesSampleRate: 0.2
|
||||||
|
profilesSampleRate: 1
|
||||||
|
ignoreErrors: []
|
||||||
|
sendDefaultPII: false
|
||||||
|
serverName: ""
|
||||||
|
environment: ""
|
||||||
|
maxBreadcrumbs: 0
|
||||||
|
maxSpans: 1000
|
||||||
|
maxErrorDepth: 10
|
||||||
|
|
||||||
# Configuration des connexions TCP
|
# Configuration des connexions TCP
|
||||||
# Voir https://pkg.go.dev/net#Dialer
|
# Voir https://pkg.go.dev/net#Dialer
|
||||||
dial:
|
dial:
|
||||||
@ -123,4 +167,10 @@ layers:
|
|||||||
# Répertoire contenant les templates
|
# Répertoire contenant les templates
|
||||||
templateDir: "/etc/bouncer/layers/queue/templates"
|
templateDir: "/etc/bouncer/layers/queue/templates"
|
||||||
# Temps de vie par défaut d'une session
|
# Temps de vie par défaut d'une session
|
||||||
defaultKeepAlive: 1m
|
defaultKeepAlive: 1m
|
||||||
|
|
||||||
|
# Configuration du layer "circuitbreaker"
|
||||||
|
circuitbreaker:
|
||||||
|
# Répertoire contenant les templates
|
||||||
|
templateDir: "/etc/bouncer/layers/circuitbreaker/templates"
|
||||||
|
|
Reference in New Issue
Block a user