Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
15916444e1 | ||
|
dd5124912e | ||
|
fffb8ffcb6 | ||
|
cbb2ce6d07 | ||
|
7f0a4e5ca9 |
@@ -1,7 +1,10 @@
|
|||||||
|
when:
|
||||||
|
branch: main
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# use vendor to cache dependencies
|
# use vendor to cache dependencies
|
||||||
vendor:
|
vendor:
|
||||||
image: golang:1.20
|
image: golang:1.21
|
||||||
commands:
|
commands:
|
||||||
- go mod vendor
|
- go mod vendor
|
||||||
|
|
||||||
|
14
FEATURES.md
14
FEATURES.md
@@ -2,13 +2,19 @@
|
|||||||
|
|
||||||
## Custom domains
|
## Custom domains
|
||||||
|
|
||||||
...
|
Custom domains can be used by creating a `.domains` file with the domain name, e.g.:
|
||||||
|
|
||||||
|
```text
|
||||||
|
codeberg.page
|
||||||
|
```
|
||||||
|
|
||||||
|
You also have to set some DNS records, see the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/using-custom-domain/).
|
||||||
|
|
||||||
## Redirects
|
## Redirects
|
||||||
|
|
||||||
Redirects can be created with a `_redirects` file with the following format:
|
Redirects can be created with a `_redirects` file with the following format:
|
||||||
|
|
||||||
```
|
```text
|
||||||
# Comment
|
# Comment
|
||||||
from to [status]
|
from to [status]
|
||||||
```
|
```
|
||||||
@@ -30,7 +36,7 @@ from to [status]
|
|||||||
|
|
||||||
Redirects all paths to `/index.html` for single-page apps.
|
Redirects all paths to `/index.html` for single-page apps.
|
||||||
|
|
||||||
```
|
```text
|
||||||
/* /index.html 200
|
/* /index.html 200
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -38,7 +44,7 @@ Redirects all paths to `/index.html` for single-page apps.
|
|||||||
|
|
||||||
Redirects every path under `/articles` to `/posts` while keeping the path.
|
Redirects every path under `/articles` to `/posts` while keeping the path.
|
||||||
|
|
||||||
```
|
```text
|
||||||
/articles/* /posts/:splat 302
|
/articles/* /posts/:splat 302
|
||||||
```
|
```
|
||||||
|
|
||||||
|
29
README.md
29
README.md
@@ -14,8 +14,7 @@ It is suitable to be deployed by other Gitea instances, too, to offer static pag
|
|||||||
**End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview)
|
**End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview)
|
||||||
and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/).
|
and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/).
|
||||||
|
|
||||||
|
<a href="https://codeberg.org/Codeberg/pages-server"> <img src="https://codeberg.org/Codeberg/GetItOnCodeberg/raw/branch/main/get-it-on-blue-on-white.svg" alt="Get It On Codeberg" width="250"/> </a>
|
||||||
<a href="https://codeberg.org/Codeberg/pages-server"> <img src="https://codeberg.org/Codeberg/GetItOnCodeberg/raw/branch/main/get-it-on-blue-on-white.svg" alt="Get It On Codeberg" width="250"/> <a/>
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@@ -61,18 +60,18 @@ but if you want to run it on a shared IP address (and not a standalone),
|
|||||||
you'll need to configure your reverse proxy not to terminate the TLS connections,
|
you'll need to configure your reverse proxy not to terminate the TLS connections,
|
||||||
but forward the requests on the IP level to the Pages Server.
|
but forward the requests on the IP level to the Pages Server.
|
||||||
|
|
||||||
You can check out a proof of concept in the `haproxy-sni` folder,
|
You can check out a proof of concept in the `examples/haproxy-sni` folder,
|
||||||
and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/haproxy-sni/haproxy.cfg#L38).
|
and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/examples/haproxy-sni/haproxy.cfg#L38).
|
||||||
|
|
||||||
### Environment
|
### Environment Variables
|
||||||
|
|
||||||
- `HOST` & `PORT` (default: `[::]` & `443`): listen address.
|
- `HOST` & `PORT` (default: `[::]` & `443`): listen address.
|
||||||
- `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages.
|
- `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages.
|
||||||
- `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources.
|
- `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources (must be subdomain of `PAGES_DOMAIN`).
|
||||||
- `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance.
|
- `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance.
|
||||||
- `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos.
|
- `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos.
|
||||||
- `RAW_INFO_PAGE` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided.
|
- `RAW_INFO_PAGE` (default: <https://docs.codeberg.org/pages/raw-content/>): info page for raw resources, shown if no resource is provided.
|
||||||
- `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging).
|
- `ACME_API` (default: <https://acme-v02.api.letsencrypt.org/directory>): set this to <https://acme.mock.director> to use invalid certificates without any verification (great for debugging).
|
||||||
ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet.
|
ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet.
|
||||||
- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders.
|
- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders.
|
||||||
- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL.
|
- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL.
|
||||||
@@ -80,10 +79,9 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg
|
|||||||
- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL.
|
- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL.
|
||||||
- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80.
|
- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80.
|
||||||
- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard.
|
- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard.
|
||||||
See https://go-acme.github.io/lego/dns/ for available values & additional environment variables.
|
See <https://go-acme.github.io/lego/dns/> for available values & additional environment variables.
|
||||||
- `LOG_LEVEL` (default: warn): Set this to specify the level of logging.
|
- `LOG_LEVEL` (default: warn): Set this to specify the level of logging.
|
||||||
|
|
||||||
|
|
||||||
## Contributing to the development
|
## Contributing to the development
|
||||||
|
|
||||||
The Codeberg team is very open to your contribution.
|
The Codeberg team is very open to your contribution.
|
||||||
@@ -115,11 +113,12 @@ Thank you very much.
|
|||||||
|
|
||||||
### Test Server
|
### Test Server
|
||||||
|
|
||||||
Make sure you have [golang](https://go.dev) v1.20 or newer and [just](https://just.systems/man/en/) installed.
|
Make sure you have [golang](https://go.dev) v1.21 or newer and [just](https://just.systems/man/en/) installed.
|
||||||
|
|
||||||
run `just dev`
|
run `just dev`
|
||||||
now this pages should work:
|
now this pages should work:
|
||||||
- https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg
|
|
||||||
- https://momar.localhost.mock.directory:4430/ci-testing/
|
- <https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg>
|
||||||
- https://momar.localhost.mock.directory:4430/pag/@master/
|
- <https://momar.localhost.mock.directory:4430/ci-testing/>
|
||||||
- https://mock-pages.codeberg-test.org:4430/README.md
|
- <https://momar.localhost.mock.directory:4430/pag/@master/>
|
||||||
|
- <https://mock-pages.codeberg-test.org:4430/README.md>
|
||||||
|
@@ -72,13 +72,6 @@ var (
|
|||||||
EnvVars: []string{"RAW_DOMAIN"},
|
EnvVars: []string{"RAW_DOMAIN"},
|
||||||
Value: "raw.codeberg.page",
|
Value: "raw.codeberg.page",
|
||||||
},
|
},
|
||||||
// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path).
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "raw-info-page",
|
|
||||||
Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)",
|
|
||||||
EnvVars: []string{"RAW_INFO_PAGE"},
|
|
||||||
Value: "https://docs.codeberg.org/codeberg-pages/raw-content/",
|
|
||||||
},
|
|
||||||
|
|
||||||
// #########################
|
// #########################
|
||||||
// ### Page Server Setup ###
|
// ### Page Server Setup ###
|
||||||
|
@@ -47,7 +47,6 @@ func Serve(ctx *cli.Context) error {
|
|||||||
rawDomain := ctx.String("raw-domain")
|
rawDomain := ctx.String("raw-domain")
|
||||||
defaultBranches := ctx.StringSlice("pages-branch")
|
defaultBranches := ctx.StringSlice("pages-branch")
|
||||||
mainDomainSuffix := ctx.String("pages-domain")
|
mainDomainSuffix := ctx.String("pages-domain")
|
||||||
rawInfoPage := ctx.String("raw-info-page")
|
|
||||||
listeningHost := ctx.String("host")
|
listeningHost := ctx.String("host")
|
||||||
listeningSSLPort := ctx.Uint("port")
|
listeningSSLPort := ctx.Uint("port")
|
||||||
listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort)
|
listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort)
|
||||||
@@ -137,7 +136,6 @@ func Serve(ctx *cli.Context) error {
|
|||||||
// Create ssl handler based on settings
|
// Create ssl handler based on settings
|
||||||
sslHandler := handler.Handler(mainDomainSuffix, rawDomain,
|
sslHandler := handler.Handler(mainDomainSuffix, rawDomain,
|
||||||
giteaClient,
|
giteaClient,
|
||||||
rawInfoPage,
|
|
||||||
BlacklistedPaths, allowedCorsDomains,
|
BlacklistedPaths, allowedCorsDomains,
|
||||||
defaultBranches,
|
defaultBranches,
|
||||||
dnsLookupCache, canonicalDomainCache, redirectsCache)
|
dnsLookupCache, canonicalDomainCache, redirectsCache)
|
||||||
|
15
go.mod
15
go.mod
@@ -1,6 +1,8 @@
|
|||||||
module codeberg.org/codeberg/pages
|
module codeberg.org/codeberg/pages
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f
|
code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f
|
||||||
@@ -10,6 +12,7 @@ require (
|
|||||||
github.com/joho/godotenv v1.4.0
|
github.com/joho/godotenv v1.4.0
|
||||||
github.com/lib/pq v1.10.7
|
github.com/lib/pq v1.10.7
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad
|
github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad
|
||||||
github.com/rs/zerolog v1.27.0
|
github.com/rs/zerolog v1.27.0
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
@@ -35,6 +38,7 @@ require (
|
|||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
||||||
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
|
||||||
@@ -61,6 +65,7 @@ require (
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
||||||
github.com/gophercloud/gophercloud v0.16.0 // indirect
|
github.com/gophercloud/gophercloud v0.16.0 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||||
|
github.com/gorilla/css v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
@@ -115,11 +120,11 @@ require (
|
|||||||
github.com/vultr/govultr/v2 v2.7.1 // indirect
|
github.com/vultr/govultr/v2 v2.7.1 // indirect
|
||||||
go.opencensus.io v0.22.3 // indirect
|
go.opencensus.io v0.22.3 // indirect
|
||||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
|
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
golang.org/x/text v0.3.6 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||||
google.golang.org/api v0.20.0 // indirect
|
google.golang.org/api v0.20.0 // indirect
|
||||||
google.golang.org/appengine v1.6.5 // indirect
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
|
30
go.sum
30
go.sum
@@ -88,6 +88,8 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
|
|||||||
github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo=
|
github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo=
|
||||||
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
@@ -251,6 +253,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
@@ -278,6 +281,8 @@ github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD
|
|||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
@@ -477,6 +482,8 @@ github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S
|
|||||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
@@ -756,8 +763,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
|
|||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -790,7 +797,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -826,8 +834,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -899,11 +908,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||||
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -911,8 +921,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-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=
|
||||||
@@ -963,7 +974,8 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
|
|||||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="codeberg-design">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<title>%status%</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
|
|
||||||
<link href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" rel="stylesheet" />
|
|
||||||
<link href="https://fonts.codeberg.org/dist/fontawesome5/css/all.min.css" rel="stylesheet" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0; padding: 1rem; box-sizing: border-box;
|
|
||||||
width: 100%; min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<i class="fa fa-search text-primary" style="font-size: 96px;"></i>
|
|
||||||
<h1 class="mb-0 text-primary">
|
|
||||||
Page not found!
|
|
||||||
</h1>
|
|
||||||
<h5 class="text-center" style="max-width: 25em;">
|
|
||||||
Sorry, but this page couldn't be found or is inaccessible (%status%).<br/>
|
|
||||||
We hope this isn't a problem on our end ;) - Make sure to check the <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank">troubleshooting section in the Docs</a>!
|
|
||||||
</h5>
|
|
||||||
<small class="text-muted">
|
|
||||||
<img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top">
|
|
||||||
Static pages made easy - <a href="https://codeberg.page">Codeberg Pages</a>
|
|
||||||
</small>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,50 +0,0 @@
|
|||||||
package html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"codeberg.org/codeberg/pages/server/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body,
|
|
||||||
// with "%status%" and %message% replaced with the provided statusCode and msg
|
|
||||||
func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) {
|
|
||||||
ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
||||||
ctx.RespWriter.WriteHeader(statusCode)
|
|
||||||
|
|
||||||
msg = generateResponse(msg, statusCode)
|
|
||||||
|
|
||||||
_, _ = ctx.RespWriter.Write([]byte(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use template engine
|
|
||||||
func generateResponse(msg string, statusCode int) string {
|
|
||||||
if msg == "" {
|
|
||||||
msg = strings.ReplaceAll(NotFoundPage,
|
|
||||||
"%status%",
|
|
||||||
strconv.Itoa(statusCode)+" "+errorMessage(statusCode))
|
|
||||||
} else {
|
|
||||||
msg = strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(ErrorPage, "%message%", template.HTMLEscapeString(msg)),
|
|
||||||
"%status%",
|
|
||||||
http.StatusText(statusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorMessage(statusCode int) string {
|
|
||||||
message := http.StatusText(statusCode)
|
|
||||||
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusMisdirectedRequest:
|
|
||||||
message += " - domain not specified in <code>.domains</code> file"
|
|
||||||
case http.StatusFailedDependency:
|
|
||||||
message += " - target repo/branch doesn't exist or is private"
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
@@ -1,38 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="codeberg-design">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<title>%status%</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
|
|
||||||
<link href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" rel="stylesheet" />
|
|
||||||
<link href="https://fonts.codeberg.org/dist/fontawesome5/css/all.min.css" rel="stylesheet" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0; padding: 1rem; box-sizing: border-box;
|
|
||||||
width: 100%; min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<i class="fa fa-search text-primary" style="font-size: 96px;"></i>
|
|
||||||
<h1 class="mb-0 text-primary">
|
|
||||||
%status%!
|
|
||||||
</h1>
|
|
||||||
<h5 class="text-center" style="max-width: 25em;">
|
|
||||||
Sorry, but this page couldn't be served.<br/>
|
|
||||||
We got an <b>"%message%"</b><br/>
|
|
||||||
We hope this isn't a problem on our end ;) - Make sure to check the <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank">troubleshooting section in the Docs</a>!
|
|
||||||
</h5>
|
|
||||||
<small class="text-muted">
|
|
||||||
<img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top">
|
|
||||||
Static pages made easy - <a href="https://codeberg.page">Codeberg Pages</a>
|
|
||||||
</small>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,38 +0,0 @@
|
|||||||
package html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValidMessage(t *testing.T) {
|
|
||||||
testString := "requested blacklisted path"
|
|
||||||
statusCode := http.StatusForbidden
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(ErrorPage, "%message%", testString),
|
|
||||||
"%status%",
|
|
||||||
http.StatusText(statusCode))
|
|
||||||
actual := generateResponse(testString, statusCode)
|
|
||||||
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMessageWithHtml(t *testing.T) {
|
|
||||||
testString := `abc<img src=1 onerror=alert("xss");`
|
|
||||||
escapedString := "abc<img src=1 onerror=alert("xss");"
|
|
||||||
statusCode := http.StatusNotFound
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(ErrorPage, "%message%", escapedString),
|
|
||||||
"%status%",
|
|
||||||
http.StatusText(statusCode))
|
|
||||||
actual := generateResponse(testString, statusCode)
|
|
||||||
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
54
html/html.go
54
html/html.go
@@ -1,9 +1,53 @@
|
|||||||
package html
|
package html
|
||||||
|
|
||||||
import _ "embed"
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"net/http"
|
||||||
|
"text/template" // do not use html/template here, we sanitize the message before passing it to the template
|
||||||
|
|
||||||
//go:embed 404.html
|
"codeberg.org/codeberg/pages/server/context"
|
||||||
var NotFoundPage string
|
"github.com/microcosm-cc/bluemonday"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
//go:embed error.html
|
//go:embed templates/error.html
|
||||||
var ErrorPage string
|
var errorPage string
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorTemplate = template.Must(template.New("error").Parse(errorPage))
|
||||||
|
sanitizer = createBlueMondayPolicy()
|
||||||
|
)
|
||||||
|
|
||||||
|
type TemplateContext struct {
|
||||||
|
StatusCode int
|
||||||
|
StatusText string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnErrorPage sets the response status code and writes the error page to the response body.
|
||||||
|
// The error page contains a sanitized version of the message and the statusCode both in text and numeric form.
|
||||||
|
//
|
||||||
|
// Currently, only the following html tags are supported: <code>
|
||||||
|
func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) {
|
||||||
|
ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
ctx.RespWriter.WriteHeader(statusCode)
|
||||||
|
|
||||||
|
templateContext := TemplateContext{
|
||||||
|
StatusCode: statusCode,
|
||||||
|
StatusText: http.StatusText(statusCode),
|
||||||
|
Message: sanitizer.Sanitize(msg),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := errorTemplate.Execute(ctx.RespWriter, templateContext)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Str("message", msg).Int("status", statusCode).Msg("could not write response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBlueMondayPolicy() *bluemonday.Policy {
|
||||||
|
p := bluemonday.NewPolicy()
|
||||||
|
|
||||||
|
p.AllowElements("code")
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
54
html/html_test.go
Normal file
54
html/html_test.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSanitizerSimpleString(t *testing.T) {
|
||||||
|
str := "simple text message without any html elements"
|
||||||
|
|
||||||
|
assert.Equal(t, str, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithCodeTag(t *testing.T) {
|
||||||
|
str := "simple text message with <code>html</code> tag"
|
||||||
|
|
||||||
|
assert.Equal(t, str, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithCodeTagWithAttribute(t *testing.T) {
|
||||||
|
str := "simple text message with <code id=\"code\">html</code> tag"
|
||||||
|
expected := "simple text message with <code>html</code> tag"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithATag(t *testing.T) {
|
||||||
|
str := "simple text message with <a>a link to another page</a>"
|
||||||
|
expected := "simple text message with a link to another page"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithATagAndHref(t *testing.T) {
|
||||||
|
str := "simple text message with <a href=\"http://evil.site\">a link to another page</a>"
|
||||||
|
expected := "simple text message with a link to another page"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithImgTag(t *testing.T) {
|
||||||
|
str := "simple text message with a <img alt=\"not found\" src=\"http://evil.site\">"
|
||||||
|
expected := "simple text message with a "
|
||||||
|
|
||||||
|
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizerStringWithImgTagAndOnerrorAttribute(t *testing.T) {
|
||||||
|
str := "simple text message with a <img alt=\"not found\" src=\"http://evil.site\" onerror=\"alert(secret)\">"
|
||||||
|
expected := "simple text message with a "
|
||||||
|
|
||||||
|
assert.Equal(t, expected, sanitizer.Sanitize(str))
|
||||||
|
}
|
69
html/templates/error.html
Normal file
69
html/templates/error.html
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="codeberg-design">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<title>{{.StatusText}}</title>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://design.codeberg.org/design-kit/codeberg.css"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
background-color: silver;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="10em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="var(--blue-color)"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<h1 class="mb-0 text-primary">{{.StatusText}} ({{.StatusCode}})!</h1>
|
||||||
|
<h5 class="text-center" style="max-width: 25em">
|
||||||
|
<p>Sorry, but this page couldn't be served.</p>
|
||||||
|
<p><b>"{{.Message}}"</b></p>
|
||||||
|
<p>
|
||||||
|
We hope this isn't a problem on our end ;) - Make sure to check the
|
||||||
|
<a
|
||||||
|
href="https://docs.codeberg.org/codeberg-pages/troubleshooting/"
|
||||||
|
target="_blank"
|
||||||
|
>troubleshooting section in the Docs</a
|
||||||
|
>!
|
||||||
|
</p>
|
||||||
|
</h5>
|
||||||
|
<small class="text-muted">
|
||||||
|
<img
|
||||||
|
src="https://design.codeberg.org/logo-kit/icon.svg"
|
||||||
|
class="align-top"
|
||||||
|
/>
|
||||||
|
Static pages made easy -
|
||||||
|
<a href="https://codeberg.page">Codeberg Pages</a>
|
||||||
|
</small>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -21,7 +21,6 @@ const (
|
|||||||
// Handler handles a single HTTP request to the web server.
|
// Handler handles a single HTTP request to the web server.
|
||||||
func Handler(mainDomainSuffix, rawDomain string,
|
func Handler(mainDomainSuffix, rawDomain string,
|
||||||
giteaClient *gitea.Client,
|
giteaClient *gitea.Client,
|
||||||
rawInfoPage string,
|
|
||||||
blacklistedPaths, allowedCorsDomains []string,
|
blacklistedPaths, allowedCorsDomains []string,
|
||||||
defaultPagesBranches []string,
|
defaultPagesBranches []string,
|
||||||
dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey,
|
dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||||
@@ -65,7 +64,7 @@ func Handler(mainDomainSuffix, rawDomain string,
|
|||||||
// Block blacklisted paths (like ACME challenges)
|
// Block blacklisted paths (like ACME challenges)
|
||||||
for _, blacklistedPath := range blacklistedPaths {
|
for _, blacklistedPath := range blacklistedPaths {
|
||||||
if strings.HasPrefix(ctx.Path(), blacklistedPath) {
|
if strings.HasPrefix(ctx.Path(), blacklistedPath) {
|
||||||
html.ReturnErrorPage(ctx, "requested blacklisted path", http.StatusForbidden)
|
html.ReturnErrorPage(ctx, "requested path is blacklisted", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ func Handler(mainDomainSuffix, rawDomain string,
|
|||||||
if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) {
|
if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) {
|
||||||
log.Debug().Msg("raw domain request detected")
|
log.Debug().Msg("raw domain request detected")
|
||||||
handleRaw(log, ctx, giteaClient,
|
handleRaw(log, ctx, giteaClient,
|
||||||
mainDomainSuffix, rawInfoPage,
|
mainDomainSuffix,
|
||||||
trimmedHost,
|
trimmedHost,
|
||||||
pathElements,
|
pathElements,
|
||||||
canonicalDomainCache, redirectsCache)
|
canonicalDomainCache, redirectsCache)
|
||||||
|
@@ -49,7 +49,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
|
|||||||
}, canonicalLink); works {
|
}, canonicalLink); works {
|
||||||
canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache)
|
canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache)
|
||||||
if !valid {
|
if !valid {
|
||||||
html.ReturnErrorPage(ctx, "", http.StatusMisdirectedRequest)
|
html.ReturnErrorPage(ctx, "domain not specified in <code>.domains</code> file", http.StatusMisdirectedRequest)
|
||||||
return
|
return
|
||||||
} else if canonicalDomain != trimmedHost {
|
} else if canonicalDomain != trimmedHost {
|
||||||
// only redirect if the target is also a codeberg page!
|
// only redirect if the target is also a codeberg page!
|
||||||
|
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client,
|
func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client,
|
||||||
mainDomainSuffix, rawInfoPage string,
|
mainDomainSuffix string,
|
||||||
trimmedHost string,
|
trimmedHost string,
|
||||||
pathElements []string,
|
pathElements []string,
|
||||||
canonicalDomainCache, redirectsCache cache.SetGetKey,
|
canonicalDomainCache, redirectsCache cache.SetGetKey,
|
||||||
@@ -25,8 +25,12 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
|
|||||||
log.Debug().Msg("raw domain")
|
log.Debug().Msg("raw domain")
|
||||||
|
|
||||||
if len(pathElements) < 2 {
|
if len(pathElements) < 2 {
|
||||||
// https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required
|
html.ReturnErrorPage(
|
||||||
ctx.Redirect(rawInfoPage, http.StatusTemporaryRedirect)
|
ctx,
|
||||||
|
"a url in the form of <code>https://{domain}/{owner}/{repo}[/@{branch}]/{path}</code> is required",
|
||||||
|
http.StatusBadRequest,
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +65,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
|
|||||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||||
} else {
|
} else {
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(ctx,
|
||||||
fmt.Sprintf("raw domain could not find repo '%s/%s' or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo),
|
fmt.Sprintf("raw domain could not find repo <code>%s/%s</code> or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,9 +55,11 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||||||
log.Trace().Msg("tryUpstream: serve with specified repo and branch")
|
log.Trace().Msg("tryUpstream: serve with specified repo and branch")
|
||||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||||
} else {
|
} else {
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(
|
||||||
fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo),
|
ctx,
|
||||||
http.StatusFailedDependency)
|
formatSetBranchNotFoundMessage(pathElements[1][1:], targetOwner, pathElements[0]),
|
||||||
|
http.StatusFailedDependency,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -85,9 +87,11 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||||||
log.Trace().Msg("tryUpstream: serve default pages repo with specified branch")
|
log.Trace().Msg("tryUpstream: serve default pages repo with specified branch")
|
||||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||||
} else {
|
} else {
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(
|
||||||
fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo),
|
ctx,
|
||||||
http.StatusFailedDependency)
|
formatSetBranchNotFoundMessage(targetBranch, targetOwner, defaultPagesRepo),
|
||||||
|
http.StatusFailedDependency,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -143,6 +147,10 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
|
|||||||
|
|
||||||
// Couldn't find a valid repo/branch
|
// Couldn't find a valid repo/branch
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(ctx,
|
||||||
fmt.Sprintf("could not find a valid repository[%s]", targetRepo),
|
fmt.Sprintf("could not find a valid repository or branch for repository: <code>%s</code>", targetRepo),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatSetBranchNotFoundMessage(branch, owner, repo string) string {
|
||||||
|
return fmt.Sprintf("explicitly set branch <code>%q</code> does not exist at <code>%s/%s</code>", branch, owner, repo)
|
||||||
|
}
|
||||||
|
@@ -16,7 +16,6 @@ func TestHandlerPerformance(t *testing.T) {
|
|||||||
testHandler := Handler(
|
testHandler := Handler(
|
||||||
"codeberg.page", "raw.codeberg.org",
|
"codeberg.page", "raw.codeberg.org",
|
||||||
giteaClient,
|
giteaClient,
|
||||||
"https://docs.codeberg.org/pages/raw-content/",
|
|
||||||
[]string{"/.well-known/acme-challenge/"},
|
[]string{"/.well-known/acme-challenge/"},
|
||||||
[]string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"},
|
[]string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"},
|
||||||
[]string{"pages"},
|
[]string{"pages"},
|
||||||
|
@@ -41,7 +41,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
|
|||||||
|
|
||||||
// Try to request the file from the Gitea API
|
// Try to request the file from the Gitea API
|
||||||
if !options.Upstream(ctx, giteaClient, redirectsCache) {
|
if !options.Upstream(ctx, giteaClient, redirectsCache) {
|
||||||
html.ReturnErrorPage(ctx, "", ctx.StatusCode)
|
html.ReturnErrorPage(ctx, "gitea client failed", ctx.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -53,11 +53,11 @@ type Options struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context.
|
// Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context.
|
||||||
func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) (final bool) {
|
func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) bool {
|
||||||
log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger()
|
log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger()
|
||||||
|
|
||||||
if o.TargetOwner == "" || o.TargetRepo == "" {
|
if o.TargetOwner == "" || o.TargetRepo == "" {
|
||||||
html.ReturnErrorPage(ctx, "either repo owner or name info is missing", http.StatusBadRequest)
|
html.ReturnErrorPage(ctx, "gitea client: either repo owner or name info is missing", http.StatusBadRequest)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
|
|||||||
// handle 404
|
// handle 404
|
||||||
if err != nil && errors.Is(err, gitea.ErrorNotFound) || !branchExist {
|
if err != nil && errors.Is(err, gitea.ErrorNotFound) || !branchExist {
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(ctx,
|
||||||
fmt.Sprintf("branch %q for '%s/%s' not found", o.TargetBranch, o.TargetOwner, o.TargetRepo),
|
fmt.Sprintf("branch <code>%q</code> for <code>%s/%s</code> not found", o.TargetBranch, o.TargetOwner, o.TargetRepo),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
|
|||||||
// handle unexpected errors
|
// handle unexpected errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
html.ReturnErrorPage(ctx,
|
html.ReturnErrorPage(ctx,
|
||||||
fmt.Sprintf("could not get timestamp of branch %q: %v", o.TargetBranch, err),
|
fmt.Sprintf("could not get timestamp of branch <code>%q</code>: '%v'", o.TargetBranch, err),
|
||||||
http.StatusFailedDependency)
|
http.StatusFailedDependency)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -153,16 +153,16 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
|
|||||||
var msg string
|
var msg string
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = "gitea client returned unexpected error"
|
msg = "gitea client: returned unexpected error"
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
msg = fmt.Sprintf("%s: %v", msg, err)
|
msg = fmt.Sprintf("%s: '%v'", msg, err)
|
||||||
}
|
}
|
||||||
if reader == nil {
|
if reader == nil {
|
||||||
msg = "gitea client returned no reader"
|
msg = "gitea client: returned no reader"
|
||||||
log.Error().Msg(msg)
|
log.Error().Msg(msg)
|
||||||
}
|
}
|
||||||
if statusCode != http.StatusOK {
|
if statusCode != http.StatusOK {
|
||||||
msg = fmt.Sprintf("Couldn't fetch contents (status code %d)", statusCode)
|
msg = fmt.Sprintf("gitea client: couldn't fetch contents: <code>%d - %s</code>", statusCode, http.StatusText(statusCode))
|
||||||
log.Error().Msg(msg)
|
log.Error().Msg(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user