Compare commits

..

No commits in common. "main" and "v6.1" have entirely different histories.
main ... v6.1

23 changed files with 730 additions and 2241 deletions

1
.envrc
View file

@ -1 +0,0 @@
use_flake

2
.gitignore vendored
View file

@ -8,5 +8,3 @@ vendor/
pages pages
certs.sqlite certs.sqlite
.bash_history .bash_history
pkg/
.direnv/

View file

@ -5,22 +5,22 @@ when:
steps: steps:
# use vendor to cache dependencies # use vendor to cache dependencies
vendor: vendor:
image: golang:1.24 image: golang:1.23
commands: commands:
- go mod vendor - go mod vendor
build: build:
depends_on: vendor depends_on: vendor
image: codeberg.org/6543/docker-images/golang_just:go-1.24 image: codeberg.org/6543/docker-images/golang_just:go-1.22
commands: commands:
- go version - go version
- just build - just build
when: when:
- event: [push, pull_request, tag] - event: [push, pull_request]
docker-dryrun: docker-dryrun:
depends_on: vendor depends_on: vendor
image: woodpeckerci/plugin-docker-buildx:5.2.1 image: woodpeckerci/plugin-docker-buildx:5.0.0
settings: settings:
dockerfile: Dockerfile dockerfile: Dockerfile
platforms: linux/amd64 platforms: linux/amd64
@ -32,7 +32,7 @@ steps:
build-tag: build-tag:
depends_on: vendor depends_on: vendor
image: codeberg.org/6543/docker-images/golang_just:go-1.24 image: codeberg.org/6543/docker-images/golang_just:go-1.22
commands: commands:
- go version - go version
- just build-tag ${CI_COMMIT_TAG##v} - just build-tag ${CI_COMMIT_TAG##v}
@ -41,29 +41,28 @@ steps:
test: test:
depends_on: build depends_on: build
image: codeberg.org/6543/docker-images/golang_just:go-1.24 image: codeberg.org/6543/docker-images/golang_just:go-1.22
commands: commands:
- just test - just test
when: when:
- event: [pull_request] - event: [pull_request]
integration-tests: integration-tests:
failure: ignore
depends_on: build depends_on: build
image: codeberg.org/6543/docker-images/golang_just:go-1.24 image: codeberg.org/6543/docker-images/golang_just:go-1.22
commands: commands:
- just integration - just integration
environment: environment:
ACME_API: https://acme.mock.directory - ACME_API=https://acme.mock.directory
PAGES_DOMAIN: localhost.mock.directory - PAGES_DOMAIN=localhost.mock.directory
RAW_DOMAIN: raw.localhost.mock.directory - RAW_DOMAIN=raw.localhost.mock.directory
PORT: 4430 - PORT=4430
when: when:
- event: [pull_request] - event: [pull_request]
release: release:
depends_on: build depends_on: build
image: woodpeckerci/plugin-release:0.2.5 image: woodpeckerci/plugin-release:0.2.1
settings: settings:
base_url: https://codeberg.org base_url: https://codeberg.org
file_exists: overwrite file_exists: overwrite
@ -75,7 +74,7 @@ steps:
docker-next: docker-next:
depends_on: vendor depends_on: vendor
image: woodpeckerci/plugin-docker-buildx:5.2.1 image: woodpeckerci/plugin-docker-buildx:5.0.0
settings: settings:
registry: codeberg.org registry: codeberg.org
dockerfile: Dockerfile dockerfile: Dockerfile
@ -90,7 +89,7 @@ steps:
- event: [push] - event: [push]
'Publish PR image': 'Publish PR image':
image: woodpeckerci/plugin-docker-buildx:5.2.1 image: woodpeckerci/plugin-docker-buildx:5.0.0
depends_on: test depends_on: test
settings: settings:
registry: codeberg.org registry: codeberg.org
@ -108,7 +107,7 @@ steps:
docker-release: docker-release:
depends_on: vendor depends_on: vendor
image: woodpeckerci/plugin-docker-buildx:5.2.1 image: woodpeckerci/plugin-docker-buildx:5.0.0
settings: settings:
registry: codeberg.org registry: codeberg.org
dockerfile: Dockerfile dockerfile: Dockerfile

View file

@ -6,25 +6,25 @@ when:
steps: steps:
lint: lint:
depends_on: [] depends_on: []
image: golangci/golangci-lint:v1.64.8 image: golangci/golangci-lint:v1.61.0
commands: commands:
- go version - go version
- go install mvdan.cc/gofumpt@latest - go install mvdan.cc/gofumpt@latest
- "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }"
- golangci-lint run --timeout 10m --build-tags integration - golangci-lint run --timeout 5m --build-tags integration
editor-config: editor-config:
depends_on: [] depends_on: []
image: mstruebing/editorconfig-checker:v3.2.0 image: mstruebing/editorconfig-checker:v3.0.3
yamllint: yamllint:
image: pipelinecomponents/yamllint:0.33.0 image: pipelinecomponents/yamllint:0.32.1
depends_on: [] depends_on: []
commands: commands:
- yamllint . - yamllint .
prettier: prettier:
image: docker.io/woodpeckerci/plugin-prettier:1.3.0 image: docker.io/woodpeckerci/plugin-prettier:0.2.0
depends_on: [] depends_on: []
settings: settings:
version: 3.2.5 version: 3.2.5

View file

@ -1,8 +1,8 @@
# Set the default Go version as a build argument # Set the default Go version as a build argument
ARG XGO="go-1.24.x" ARG XGO="go-1.21.x"
# Use xgo (a Go cross-compiler tool) as build image # Use xgo (a Go cross-compiler tool) as build image
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} AS build FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} as build
# Set the working directory and copy the source code # Set the working directory and copy the source code
WORKDIR /go/src/codeberg.org/codeberg/pages WORKDIR /go/src/codeberg.org/codeberg/pages

4
flake.lock generated
View file

@ -20,8 +20,8 @@
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 0, "lastModified": 0,
"narHash": "sha256-WFZDy4bG2RkkCQloIEG8BXEvzyKklFVJbAismOJsIp4=", "narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=",
"path": "/nix/store/c77dsgfxjywplw8bk8s8jlkdsr7a1bi9-source", "path": "/nix/store/2w8kz6zh3aq80f1dypiin222fry1rv51-source",
"type": "path" "type": "path"
}, },
"original": { "original": {

View file

@ -13,7 +13,7 @@
in { in {
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
glibc.static gcc
go go
gofumpt gofumpt
golangci-lint golangci-lint

291
go.mod
View file

@ -1,237 +1,144 @@
module codeberg.org/codeberg/pages module codeberg.org/codeberg/pages
go 1.24.0 go 1.22.0
toolchain go1.23.1
require ( require (
code.gitea.io/sdk/gitea v0.20.0 code.gitea.io/sdk/gitea v0.17.1
github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a
github.com/creasty/defaults v1.8.0 github.com/creasty/defaults v1.7.0
github.com/go-acme/lego/v4 v4.21.0 github.com/go-acme/lego/v4 v4.5.3
github.com/go-sql-driver/mysql v1.8.1 github.com/go-sql-driver/mysql v1.6.0
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.4.0
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.7
github.com/mattn/go-sqlite3 v1.14.24 github.com/mattn/go-sqlite3 v1.14.16
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.26
github.com/pelletier/go-toml/v2 v2.2.3 github.com/pelletier/go-toml/v2 v2.1.0
github.com/pires/go-proxyproto v0.8.0
github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad
github.com/rs/zerolog v1.33.0 github.com/rs/zerolog v1.27.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.27.5 github.com/urfave/cli/v2 v2.3.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
xorm.io/xorm v1.3.9 xorm.io/xorm v1.3.2
) )
require ( require (
cloud.google.com/go/auth v0.14.0 // indirect cloud.google.com/go v0.54.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/42wim/httpsig v1.2.2 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest v0.11.19 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
github.com/aws/aws-sdk-go-v2 v1.33.0 // indirect github.com/aws/aws-sdk-go v1.39.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.53 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.10 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.8 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/boombuler/barcode v1.0.2 // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cloudflare/cloudflare-go v0.20.0 // indirect
github.com/civo/civogo v0.3.92 // indirect
github.com/cloudflare/cloudflare-go v0.114.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpu/goacmedns v0.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/deepmap/oapi-codegen v1.6.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnsimple/dnsimple-go v1.7.0 // indirect github.com/dnsimple/dnsimple-go v0.70.1 // indirect
github.com/exoscale/egoscale/v3 v3.1.8 // indirect github.com/exoscale/egoscale v0.67.0 // indirect
github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-errors/errors v1.0.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/goccy/go-json v0.8.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/go-resty/resty/v2 v2.16.3 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/gophercloud/gophercloud v0.16.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gorilla/css v1.0.0 // indirect
github.com/gophercloud/gophercloud v1.14.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jarcoal/httpmock v1.0.6 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect github.com/labbsr0x/goh v1.0.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/linode/linodego v0.31.1 // indirect
github.com/linode/linodego v1.46.0 // indirect github.com/liquidweb/go-lwApi v0.0.5 // indirect
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.4 // indirect github.com/liquidweb/liquidweb-go v1.6.3 // indirect
github.com/magiconair/properties v1.8.9 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/miekg/dns v1.1.43 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/nrdcg/auroradns v1.1.0 // indirect github.com/nrdcg/auroradns v1.0.1 // indirect
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect github.com/nrdcg/desec v0.6.0 // indirect
github.com/nrdcg/desec v0.10.0 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.3.0 // indirect github.com/nrdcg/freemyip v0.2.0 // indirect
github.com/nrdcg/goinwx v0.10.0 // indirect github.com/nrdcg/goinwx v0.8.1 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/nrdcg/nodion v0.1.0 // indirect github.com/nrdcg/porkbun v0.1.1 // indirect
github.com/nrdcg/porkbun v0.4.0 // indirect github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/ovh/go-ovh v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.81.2 // indirect
github.com/ovh/go-ovh v1.6.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/peterhellberg/link v1.2.0 // indirect github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.4.0 // indirect github.com/pquerna/otp v1.3.0 // indirect
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sacloud/libsacloud v1.36.2 // indirect
github.com/sacloud/api-client-go v0.2.10 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect
github.com/sacloud/go-http v0.1.9 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sacloud/iaas-api-go v1.14.0 // indirect github.com/sirupsen/logrus v1.4.2 // indirect
github.com/sacloud/packages-go v0.0.11 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect
github.com/selectel/domains-go v1.1.0 // indirect
github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/softlayer/softlayer-go v1.1.7 // indirect github.com/softlayer/softlayer-go v1.0.3 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/sony/gobreaker v1.0.0 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/stretchr/objx v0.5.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084 // indirect github.com/transip/gotransip/v6 v6.6.1 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 // indirect github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/vultr/govultr/v2 v2.7.1 // indirect
github.com/transip/gotransip/v6 v6.26.0 // indirect go.opencensus.io v0.22.3 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect golang.org/x/crypto v0.21.0 // indirect
github.com/volcengine/volc-sdk-golang v1.0.193 // indirect golang.org/x/net v0.23.0 // indirect
github.com/vultr/govultr/v3 v3.14.1 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
github.com/x448/float16 v0.8.4 // indirect golang.org/x/sys v0.18.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/text v0.14.0 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect google.golang.org/api v0.20.0 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect google.golang.org/appengine v1.6.5 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect google.golang.org/genproto v0.0.0-20200305110556-506484158171 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect google.golang.org/grpc v1.27.1 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect google.golang.org/protobuf v1.26.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
go.uber.org/atomic v1.11.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.31.0 // indirect
google.golang.org/api v0.217.0 // indirect
google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/grpc v1.69.4 // indirect
google.golang.org/protobuf v1.36.3 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.32.1 // indirect xorm.io/builder v0.3.12 // indirect
k8s.io/apimachinery v0.32.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
xorm.io/builder v0.3.13 // indirect
) )

2403
go.sum

File diff suppressed because it is too large Load diff

View file

@ -16,11 +16,9 @@ import (
"github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/lego"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
"github.com/reugn/equalizer" "github.com/reugn/equalizer"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/cache"
psContext "codeberg.org/codeberg/pages/server/context"
"codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/database"
dnsutils "codeberg.org/codeberg/pages/server/dns" dnsutils "codeberg.org/codeberg/pages/server/dns"
"codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/gitea"
@ -45,11 +43,7 @@ func TLSConfig(mainDomainSuffix string,
return &tls.Config{ return &tls.Config{
// check DNS name & get certificate from Let's Encrypt // check DNS name & get certificate from Let's Encrypt
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
ctx := psContext.New(nil, nil)
log := log.With().Str("ReqId", ctx.ReqId).Logger()
domain := strings.ToLower(strings.TrimSpace(info.ServerName)) domain := strings.ToLower(strings.TrimSpace(info.ServerName))
log.Debug().Str("domain", domain).Msg("start: get tls certificate")
if len(domain) < 1 { if len(domain) < 1 {
return nil, errors.New("missing domain info via SNI (RFC 4366, Section 3.1)") return nil, errors.New("missing domain info via SNI (RFC 4366, Section 3.1)")
} }
@ -106,7 +100,7 @@ func TLSConfig(mainDomainSuffix string,
TargetRepo: targetRepo, TargetRepo: targetRepo,
TargetBranch: targetBranch, TargetBranch: targetBranch,
} }
_, valid := targetOpt.CheckCanonicalDomain(ctx, giteaClient, domain, mainDomainSuffix, canonicalDomainCache) _, valid := targetOpt.CheckCanonicalDomain(giteaClient, domain, mainDomainSuffix, canonicalDomainCache)
if !valid { if !valid {
// We shouldn't obtain a certificate when we cannot check if the // We shouldn't obtain a certificate when we cannot check if the
// repository has specified this domain in the `.domains` file. // repository has specified this domain in the `.domains` file.
@ -122,7 +116,7 @@ func TLSConfig(mainDomainSuffix string,
var tlsCertificate *tls.Certificate var tlsCertificate *tls.Certificate
var err error var err error
if tlsCertificate, err = acmeClient.retrieveCertFromDB(log, domain, mainDomainSuffix, false, certDB); err != nil { if tlsCertificate, err = acmeClient.retrieveCertFromDB(domain, mainDomainSuffix, false, certDB); err != nil {
if !errors.Is(err, database.ErrNotFound) { if !errors.Is(err, database.ErrNotFound) {
return nil, err return nil, err
} }
@ -136,7 +130,7 @@ func TLSConfig(mainDomainSuffix string,
return nil, fmt.Errorf("won't request certificate for %q", domain) return nil, fmt.Errorf("won't request certificate for %q", domain)
} }
tlsCertificate, err = acmeClient.obtainCert(log, acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB) tlsCertificate, err = acmeClient.obtainCert(acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -179,7 +173,7 @@ func (c *AcmeClient) checkUserLimit(user string) error {
return nil return nil
} }
func (c *AcmeClient) retrieveCertFromDB(log zerolog.Logger, sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) { func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) {
// parse certificate from database // parse certificate from database
res, err := certDB.Get(sni) res, err := certDB.Get(sni)
if err != nil { if err != nil {
@ -212,7 +206,7 @@ func (c *AcmeClient) retrieveCertFromDB(log zerolog.Logger, sni, mainDomainSuffi
// TODO: make a queue ? // TODO: make a queue ?
go (func() { go (func() {
res.CSR = nil // acme client doesn't like CSR to be set res.CSR = nil // acme client doesn't like CSR to be set
if _, err := c.obtainCert(log, c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil { if _, err := c.obtainCert(c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil {
log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err)
} }
})() })()
@ -222,7 +216,7 @@ func (c *AcmeClient) retrieveCertFromDB(log zerolog.Logger, sni, mainDomainSuffi
return &tlsCertificate, nil return &tlsCertificate, nil
} }
func (c *AcmeClient) obtainCert(log zerolog.Logger, acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) {
name := strings.TrimPrefix(domains[0], "*") name := strings.TrimPrefix(domains[0], "*")
// lock to avoid simultaneous requests // lock to avoid simultaneous requests
@ -232,7 +226,7 @@ func (c *AcmeClient) obtainCert(log zerolog.Logger, acmeClient *lego.Client, dom
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
_, working = c.obtainLocks.Load(name) _, working = c.obtainLocks.Load(name)
} }
cert, err := c.retrieveCertFromDB(log, name, mainDomainSuffix, useDnsProvider, keyDatabase) cert, err := c.retrieveCertFromDB(name, mainDomainSuffix, useDnsProvider, keyDatabase)
if err != nil { if err != nil {
return nil, fmt.Errorf("certificate failed in synchronous request: %w", err) return nil, fmt.Errorf("certificate failed in synchronous request: %w", err)
} }
@ -326,7 +320,7 @@ func (c *AcmeClient) obtainCert(log zerolog.Logger, acmeClient *lego.Client, dom
return &tlsCertificate, nil return &tlsCertificate, nil
} }
func SetupMainDomainCertificates(log zerolog.Logger, mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error { func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error {
// getting main cert before ACME account so that we can fail here without hitting rate limits // getting main cert before ACME account so that we can fail here without hitting rate limits
mainCertBytes, err := certDB.Get(mainDomainSuffix) mainCertBytes, err := certDB.Get(mainDomainSuffix)
if err != nil && !errors.Is(err, database.ErrNotFound) { if err != nil && !errors.Is(err, database.ErrNotFound) {
@ -334,7 +328,7 @@ func SetupMainDomainCertificates(log zerolog.Logger, mainDomainSuffix string, ac
} }
if mainCertBytes == nil { if mainCertBytes == nil {
_, err = acmeClient.obtainCert(log, acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB) _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only") log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only")
} }
@ -343,7 +337,7 @@ func SetupMainDomainCertificates(log zerolog.Logger, mainDomainSuffix string, ac
return nil return nil
} }
func MaintainCertDB(log zerolog.Logger, ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) { func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) {
for { for {
// delete expired certs that will be invalid until next clean up // delete expired certs that will be invalid until next clean up
threshold := time.Now().Add(interval) threshold := time.Now().Add(interval)
@ -381,7 +375,7 @@ func MaintainCertDB(log zerolog.Logger, ctx context.Context, interval time.Durat
} else if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { } else if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) {
// renew main certificate 30 days before it expires // renew main certificate 30 days before it expires
go (func() { go (func() {
_, err = acmeClient.obtainCert(log, acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB) _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Couldn't renew certificate for main domain") log.Error().Err(err).Msg("Couldn't renew certificate for main domain")
} }

View file

@ -5,29 +5,19 @@ import (
"net/http" "net/http"
"codeberg.org/codeberg/pages/server/utils" "codeberg.org/codeberg/pages/server/utils"
"github.com/hashicorp/go-uuid"
"github.com/rs/zerolog/log"
) )
type Context struct { type Context struct {
RespWriter http.ResponseWriter RespWriter http.ResponseWriter
Req *http.Request Req *http.Request
StatusCode int StatusCode int
ReqId string
} }
func New(w http.ResponseWriter, r *http.Request) *Context { func New(w http.ResponseWriter, r *http.Request) *Context {
req_uuid, err := uuid.GenerateUUID()
if err != nil {
log.Error().Err(err).Msg("Failed to generate request id, assigning error value")
req_uuid = "ERROR"
}
return &Context{ return &Context{
RespWriter: w, RespWriter: w,
Req: r, Req: r,
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
ReqId: req_uuid,
} }
} }

View file

@ -8,11 +8,9 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/cache"
"codeberg.org/codeberg/pages/server/context"
) )
const ( const (
@ -40,16 +38,15 @@ type FileResponse struct {
Exists bool `json:"exists"` Exists bool `json:"exists"`
IsSymlink bool `json:"isSymlink"` IsSymlink bool `json:"isSymlink"`
ETag string `json:"eTag"` ETag string `json:"eTag"`
MimeType string `json:"mimeType"` // uncompressed MIME type MimeType string `json:"mimeType"`
RawMime string `json:"rawMime"` // raw MIME type (if compressed, type of compression) Body []byte `json:"-"` // saved separately
Body []byte `json:"-"` // saved separately
} }
func (f FileResponse) IsEmpty() bool { func (f FileResponse) IsEmpty() bool {
return len(f.Body) == 0 return len(f.Body) == 0
} }
func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (header http.Header, statusCode int) { func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) {
header = make(http.Header) header = make(http.Header)
if f.Exists { if f.Exists {
@ -62,13 +59,7 @@ func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (head
header.Set(giteaObjectTypeHeader, objTypeSymlink) header.Set(giteaObjectTypeHeader, objTypeSymlink)
} }
header.Set(ETagHeader, f.ETag) header.Set(ETagHeader, f.ETag)
header.Set(ContentTypeHeader, f.MimeType)
if decompress {
header.Set(ContentTypeHeader, f.MimeType)
} else {
header.Set(ContentTypeHeader, f.RawMime)
}
header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body))) header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body)))
header.Set(PagesCacheIndicatorHeader, "true") header.Set(PagesCacheIndicatorHeader, "true")
@ -91,17 +82,16 @@ type writeCacheReader struct {
hasError bool hasError bool
doNotCache bool doNotCache bool
complete bool complete bool
log zerolog.Logger
} }
func (t *writeCacheReader) Read(p []byte) (n int, err error) { func (t *writeCacheReader) Read(p []byte) (n int, err error) {
t.log.Trace().Msgf("[cache] read %q", t.cacheKey) log.Trace().Msgf("[cache] read %q", t.cacheKey)
n, err = t.originalReader.Read(p) n, err = t.originalReader.Read(p)
if err == io.EOF { if err == io.EOF {
t.complete = true t.complete = true
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
t.log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey)
t.hasError = true t.hasError = true
} else if n > 0 { } else if n > 0 {
if t.buffer.Len()+n > int(fileCacheSizeLimit) { if t.buffer.Len()+n > int(fileCacheSizeLimit) {
@ -121,23 +111,22 @@ func (t *writeCacheReader) Close() error {
if doWrite { if doWrite {
jsonToCache, err := json.Marshal(fc) jsonToCache, err := json.Marshal(fc)
if err != nil { if err != nil {
t.log.Trace().Err(err).Msgf("[cache] marshaling json for %q has returned an error", t.cacheKey+"|Metadata") log.Trace().Err(err).Msgf("[cache] marshaling json for %q has returned an error", t.cacheKey+"|Metadata")
} }
err = t.cache.Set(t.cacheKey+"|Metadata", jsonToCache, fileCacheTimeout) err = t.cache.Set(t.cacheKey+"|Metadata", jsonToCache, fileCacheTimeout)
if err != nil { if err != nil {
t.log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Metadata") log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Metadata")
} }
err = t.cache.Set(t.cacheKey+"|Body", fc.Body, fileCacheTimeout) err = t.cache.Set(t.cacheKey+"|Body", fc.Body, fileCacheTimeout)
if err != nil { if err != nil {
t.log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body") log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body")
} }
} }
t.log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite) log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite)
return t.originalReader.Close() return t.originalReader.Close()
} }
func (f FileResponse) CreateCacheReader(ctx *context.Context, r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser { func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser {
log := log.With().Str("ReqId", ctx.ReqId).Logger()
if r == nil || cache == nil || cacheKey == "" { if r == nil || cache == nil || cacheKey == "" {
log.Error().Msg("could not create CacheReader") log.Error().Msg("could not create CacheReader")
return nil return nil
@ -149,6 +138,5 @@ func (f FileResponse) CreateCacheReader(ctx *context.Context, r io.ReadCloser, c
fileResponse: &f, fileResponse: &f,
cache: cache, cache: cache,
cacheKey: cacheKey, cacheKey: cacheKey,
log: log,
} }
} }

View file

@ -19,7 +19,6 @@ import (
"codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/config"
"codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/cache"
"codeberg.org/codeberg/pages/server/context"
"codeberg.org/codeberg/pages/server/version" "codeberg.org/codeberg/pages/server/version"
) )
@ -41,15 +40,13 @@ const (
objTypeSymlink = "symlink" objTypeSymlink = "symlink"
// std // std
ETagHeader = "ETag" ETagHeader = "ETag"
ContentTypeHeader = "Content-Type" ContentTypeHeader = "Content-Type"
ContentLengthHeader = "Content-Length" ContentLengthHeader = "Content-Length"
ContentEncodingHeader = "Content-Encoding"
) )
type Client struct { type Client struct {
sdkClient *gitea.Client sdkClient *gitea.Client
sdkFileClient *gitea.Client
responseCache cache.ICache responseCache cache.ICache
giteaRoot string giteaRoot string
@ -69,6 +66,8 @@ func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error)
} }
giteaRoot := strings.TrimSuffix(rootURL.String(), "/") giteaRoot := strings.TrimSuffix(rootURL.String(), "/")
stdClient := http.Client{Timeout: 10 * time.Second}
forbiddenMimeTypes := make(map[string]bool, len(cfg.ForbiddenMimeTypes)) forbiddenMimeTypes := make(map[string]bool, len(cfg.ForbiddenMimeTypes))
for _, mimeType := range cfg.ForbiddenMimeTypes { for _, mimeType := range cfg.ForbiddenMimeTypes {
forbiddenMimeTypes[mimeType] = true forbiddenMimeTypes[mimeType] = true
@ -79,26 +78,15 @@ func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error)
defaultMimeType = "application/octet-stream" defaultMimeType = "application/octet-stream"
} }
sdkClient, err := gitea.NewClient( sdk, err := gitea.NewClient(
giteaRoot, giteaRoot,
gitea.SetHTTPClient(&http.Client{Timeout: 10 * time.Second}), gitea.SetHTTPClient(&stdClient),
gitea.SetToken(cfg.Token),
gitea.SetUserAgent("pages-server/"+version.Version),
)
if err != nil {
return nil, err
}
sdkFileClient, err := gitea.NewClient(
giteaRoot,
gitea.SetHTTPClient(&http.Client{Timeout: 1 * time.Hour}),
gitea.SetToken(cfg.Token), gitea.SetToken(cfg.Token),
gitea.SetUserAgent("pages-server/"+version.Version), gitea.SetUserAgent("pages-server/"+version.Version),
) )
return &Client{ return &Client{
sdkClient: sdkClient, sdkClient: sdk,
sdkFileClient: sdkFileClient,
responseCache: respCache, responseCache: respCache,
giteaRoot: giteaRoot, giteaRoot: giteaRoot,
@ -115,8 +103,8 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s
return path.Join(client.giteaRoot, targetOwner, targetRepo, "src/branch", branch, resource) return path.Join(client.giteaRoot, targetOwner, targetRepo, "src/branch", branch, resource)
} }
func (client *Client) GiteaRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string) ([]byte, error) { func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) {
reader, _, _, err := client.ServeRawContent(ctx, targetOwner, targetRepo, ref, resource, false) reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -124,9 +112,9 @@ func (client *Client) GiteaRawContent(ctx *context.Context, targetOwner, targetR
return io.ReadAll(reader) return io.ReadAll(reader)
} }
func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) {
cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource)
log := log.With().Str("ReqId", ctx.ReqId).Str("cache_key", cacheKey).Logger() log := log.With().Str("cache_key", cacheKey).Logger()
log.Trace().Msg("try file in cache") log.Trace().Msg("try file in cache")
// handle if cache entry exist // handle if cache entry exist
if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok { if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok {
@ -148,12 +136,12 @@ func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetR
} }
cache.Body = body.([]byte) cache.Body = body.([]byte)
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress) cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey)
if cache.Exists { if cache.Exists {
if cache.IsSymlink { if cache.IsSymlink {
linkDest := string(cache.Body) linkDest := string(cache.Body)
log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest)
return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress) return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest)
} else { } else {
log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) log.Debug().Msgf("[cache] return %d bytes", len(cache.Body))
return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil
@ -164,7 +152,7 @@ func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetR
} }
log.Trace().Msg("file not in cache") log.Trace().Msg("file not in cache")
// not in cache, open reader via gitea api // not in cache, open reader via gitea api
reader, resp, err := client.sdkFileClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS) reader, resp, err := client.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS)
if resp != nil { if resp != nil {
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusOK: case http.StatusOK:
@ -205,27 +193,21 @@ func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetR
} }
log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest)
return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress) return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest)
} }
} }
// now we are sure it's content so set the MIME type // now we are sure it's content so set the MIME type
mimeType, rawType := client.getMimeTypeByExtension(resource) mimeType := client.getMimeTypeByExtension(resource)
resp.Response.Header.Set(ContentTypeHeader, mimeType) resp.Response.Header.Set(ContentTypeHeader, mimeType)
if decompress {
resp.Response.Header.Set(ContentTypeHeader, mimeType)
} else {
resp.Response.Header.Set(ContentTypeHeader, rawType)
}
// now we write to cache and respond at the same time // now we write to cache and respond at the same time
fileResp := FileResponse{ fileResp := FileResponse{
Exists: true, Exists: true,
ETag: resp.Header.Get(ETagHeader), ETag: resp.Header.Get(ETagHeader),
MimeType: mimeType, MimeType: mimeType,
RawMime: rawType,
} }
return fileResp.CreateCacheReader(ctx, reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
case http.StatusNotFound: case http.StatusNotFound:
jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)}) jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)})
@ -358,29 +340,13 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
return false, nil return false, nil
} }
func (client *Client) extToMime(ext string) string { func (client *Client) getMimeTypeByExtension(resource string) string {
mimeType := mime.TypeByExtension(path.Ext(ext)) mimeType := mime.TypeByExtension(path.Ext(resource))
mimeTypeSplit := strings.SplitN(mimeType, ";", 2) mimeTypeSplit := strings.SplitN(mimeType, ";", 2)
if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" {
mimeType = client.defaultMimeType mimeType = client.defaultMimeType
} }
log.Trace().Msgf("probe mime of extension '%q' is '%q'", ext, mimeType) log.Trace().Msgf("probe mime of %q is %q", resource, mimeType)
return mimeType return mimeType
} }
func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) {
rawExt := path.Ext(resource)
innerExt := rawExt
switch rawExt {
case ".gz", ".br", ".zst":
innerExt = path.Ext(resource[:len(resource)-len(rawExt)])
}
rawType = client.extToMime(rawExt)
mimeType = rawType
if innerExt != rawExt {
mimeType = client.extToMime(innerExt)
}
log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType)
return mimeType, rawType
}

View file

@ -26,9 +26,9 @@ func Handler(
canonicalDomainCache, redirectsCache cache.ICache, canonicalDomainCache, redirectsCache cache.ICache,
) http.HandlerFunc { ) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
ctx := context.New(w, req)
log := log.With().Str("ReqId", ctx.ReqId).Strs("Handler", []string{req.Host, req.RequestURI}).Logger()
log.Debug().Msg("\n----------------------------------------------------------") log.Debug().Msg("\n----------------------------------------------------------")
log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger()
ctx := context.New(w, req)
ctx.RespWriter.Header().Set("Server", "pages-server") ctx.RespWriter.Header().Set("Server", "pages-server")

View file

@ -47,7 +47,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
TargetBranch: targetBranch, TargetBranch: targetBranch,
TargetPath: path.Join(pathParts...), TargetPath: path.Join(pathParts...),
}, canonicalLink); works { }, canonicalLink); works {
canonicalDomain, valid := targetOpt.CheckCanonicalDomain(ctx, giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache)
if !valid { if !valid {
html.ReturnErrorPage(ctx, "domain not specified in <code>.domains</code> file", http.StatusMisdirectedRequest) html.ReturnErrorPage(ctx, "domain not specified in <code>.domains</code> file", http.StatusMisdirectedRequest)
return return
@ -63,8 +63,8 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
return return
} }
log.Debug().Str("url", trimmedHost).Msg("tryBranch, now trying upstream") log.Debug().Msg("tryBranch, now trying upstream 7")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
return return
} }

View file

@ -45,7 +45,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
TargetPath: path.Join(pathElements[3:]...), TargetPath: path.Join(pathElements[3:]...),
}, true); works { }, true); works {
log.Trace().Msg("tryUpstream: serve raw domain with specified branch") log.Trace().Msg("tryUpstream: serve raw domain with specified branch")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
return return
} }
log.Debug().Msg("missing branch info") log.Debug().Msg("missing branch info")
@ -62,7 +62,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie
TargetPath: path.Join(pathElements[2:]...), TargetPath: path.Join(pathElements[2:]...),
}, true); works { }, true); works {
log.Trace().Msg("tryUpstream: serve raw domain with default branch") log.Trace().Msg("tryUpstream: serve raw domain with default branch")
tryUpstream(log, 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 <code>%s/%s</code> 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),

View file

@ -53,7 +53,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
TargetPath: path.Join(pathElements[2:]...), TargetPath: path.Join(pathElements[2:]...),
}, true); works { }, true); works {
log.Trace().Msg("tryUpstream: serve with specified repo and branch") log.Trace().Msg("tryUpstream: serve with specified repo and branch")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
} else { } else {
html.ReturnErrorPage( html.ReturnErrorPage(
ctx, ctx,
@ -85,7 +85,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
TargetPath: path.Join(pathElements[1:]...), TargetPath: path.Join(pathElements[1:]...),
}, true); works { }, true); works {
log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") log.Trace().Msg("tryUpstream: serve default pages repo with specified branch")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
} else { } else {
html.ReturnErrorPage( html.ReturnErrorPage(
ctx, ctx,
@ -110,7 +110,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
TargetPath: path.Join(pathElements[1:]...), TargetPath: path.Join(pathElements[1:]...),
}, false); works { }, false); works {
log.Debug().Msg("tryBranch, now trying upstream 5") log.Debug().Msg("tryBranch, now trying upstream 5")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
return return
} }
} }
@ -126,7 +126,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
TargetPath: path.Join(pathElements...), TargetPath: path.Join(pathElements...),
}, false); works { }, false); works {
log.Debug().Msg("tryBranch, now trying upstream 6") log.Debug().Msg("tryBranch, now trying upstream 6")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
return return
} }
} }
@ -141,7 +141,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite
TargetPath: path.Join(pathElements...), TargetPath: path.Join(pathElements...),
}, false); works { }, false); works {
log.Debug().Msg("tryBranch, now trying upstream 6") log.Debug().Msg("tryBranch, now trying upstream 6")
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
return return
} }

View file

@ -15,7 +15,7 @@ import (
) )
// tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure.
func tryUpstream(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
mainDomainSuffix, trimmedHost string, mainDomainSuffix, trimmedHost string,
options *upstream.Options, options *upstream.Options,
canonicalDomainCache cache.ICache, canonicalDomainCache cache.ICache,
@ -23,7 +23,7 @@ func tryUpstream(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Cl
) { ) {
// check if a canonical domain exists on a request on MainDomain // check if a canonical domain exists on a request on MainDomain
if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw { if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw {
canonicalDomain, _ := options.CheckCanonicalDomain(ctx, giteaClient, "", mainDomainSuffix, canonicalDomainCache) canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", mainDomainSuffix, canonicalDomainCache)
if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix) { if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix) {
canonicalPath := ctx.Req.RequestURI canonicalPath := ctx.Req.RequestURI
if options.TargetRepo != defaultPagesRepo { if options.TargetRepo != defaultPagesRepo {
@ -32,12 +32,7 @@ func tryUpstream(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Cl
canonicalPath = "/" + path[2] canonicalPath = "/" + path[2]
} }
} }
ctx.Redirect("https://"+canonicalDomain+canonicalPath, http.StatusTemporaryRedirect)
redirect_to := "https://" + canonicalDomain + canonicalPath
log.Debug().Str("to", redirect_to).Msg("redirecting")
ctx.Redirect(redirect_to, http.StatusTemporaryRedirect)
return return
} }
} }
@ -46,7 +41,6 @@ func tryUpstream(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Cl
options.Host = trimmedHost options.Host = trimmedHost
// Try to request the file from the Gitea API // Try to request the file from the Gitea API
log.Debug().Msg("requesting from upstream")
if !options.Upstream(ctx, giteaClient, redirectsCache) { if !options.Upstream(ctx, giteaClient, redirectsCache) {
html.ReturnErrorPage(ctx, fmt.Sprintf("Forge returned %d %s", ctx.StatusCode, http.StatusText(ctx.StatusCode)), ctx.StatusCode) html.ReturnErrorPage(ctx, fmt.Sprintf("Forge returned %d %s", ctx.StatusCode, http.StatusText(ctx.StatusCode)), ctx.StatusCode)
} }

View file

@ -27,7 +27,7 @@ import (
// Serve sets up and starts the web server. // Serve sets up and starts the web server.
func Serve(ctx *cli.Context) error { func Serve(ctx *cli.Context) error {
// initialize logger with Trace, overridden later with actual level // initialize logger with Trace, overridden later with actual level
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Caller().Logger().Level(zerolog.TraceLevel) log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(zerolog.TraceLevel)
cfg, err := config.ReadConfig(ctx) cfg, err := config.ReadConfig(ctx)
if err != nil { if err != nil {
@ -41,8 +41,7 @@ func Serve(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Setting log level to: %s\n", logLevel) log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel)
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Caller().Logger().Level(logLevel)
listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort) listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort)
@ -86,7 +85,7 @@ func Serve(ctx *cli.Context) error {
return err return err
} }
if err := certificates.SetupMainDomainCertificates(log.Logger, cfg.Server.MainDomain, acmeClient, certDB); err != nil { if err := certificates.SetupMainDomainCertificates(cfg.Server.MainDomain, acmeClient, certDB); err != nil {
return err return err
} }
@ -115,7 +114,7 @@ func Serve(ctx *cli.Context) error {
interval := 12 * time.Hour interval := 12 * time.Hour
certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background())
defer cancelCertMaintain() defer cancelCertMaintain()
go certificates.MaintainCertDB(log.Logger, certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB) go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB)
if cfg.Server.HttpServerEnabled { if cfg.Server.HttpServerEnabled {
// Create handler for http->https redirect and http acme challenges // Create handler for http->https redirect and http acme challenges

View file

@ -8,7 +8,6 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/cache"
"codeberg.org/codeberg/pages/server/context"
"codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/gitea"
) )
@ -18,7 +17,7 @@ var canonicalDomainCacheTimeout = 15 * time.Minute
const canonicalDomainConfig = ".domains" const canonicalDomainConfig = ".domains"
// CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file).
func (o *Options) CheckCanonicalDomain(ctx *context.Context, giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) { func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) {
// Check if this request is cached. // Check if this request is cached.
if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok {
domains := cachedValue.([]string) domains := cachedValue.([]string)
@ -31,7 +30,7 @@ func (o *Options) CheckCanonicalDomain(ctx *context.Context, giteaClient *gitea.
return domains[0], valid return domains[0], valid
} }
body, err := giteaClient.GiteaRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig)
if err != nil && !errors.Is(err, gitea.ErrorNotFound) { if err != nil && !errors.Is(err, gitea.ErrorNotFound) {
log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo)
} }

View file

@ -24,8 +24,5 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) {
} else { } else {
ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime)
} }
if encoding := header.Get(gitea.ContentEncodingHeader); encoding != "" && encoding != "identity" {
ctx.RespWriter.Header().Set(gitea.ContentEncodingHeader, encoding)
}
ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat)) ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat))
} }

View file

@ -44,7 +44,7 @@ var redirectsCacheTimeout = 10 * time.Minute
const redirectsConfig = "_redirects" const redirectsConfig = "_redirects"
// getRedirects returns redirects specified in the _redirects file. // getRedirects returns redirects specified in the _redirects file.
func (o *Options) getRedirects(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect { func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect {
var redirects []Redirect var redirects []Redirect
cacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch cacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch
@ -53,7 +53,7 @@ func (o *Options) getRedirects(ctx *context.Context, giteaClient *gitea.Client,
redirects = cachedValue.([]Redirect) redirects = cachedValue.([]Redirect)
} else { } else {
// Get _redirects file and parse // Get _redirects file and parse
body, err := giteaClient.GiteaRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig) body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig)
if err == nil { if err == nil {
for _, line := range strings.Split(string(body), "\n") { for _, line := range strings.Split(string(body), "\n") {
redirectArr := strings.Fields(line) redirectArr := strings.Fields(line)
@ -92,9 +92,8 @@ func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client
for _, redirect := range redirects { for _, redirect := range redirects {
if dstURL, ok := redirect.rewriteURL(reqURL); ok { if dstURL, ok := redirect.rewriteURL(reqURL); ok {
if o.TargetPath == dstURL { // recursion base case, rewrite directly when paths are the same // do rewrite if status code is 200
return true if redirect.StatusCode == 200 {
} else if redirect.StatusCode == 200 { // do rewrite if status code is 200
o.TargetPath = dstURL o.TargetPath = dstURL
o.Upstream(ctx, giteaClient, redirectsCache) o.Upstream(ctx, giteaClient, redirectsCache)
} else { } else {

View file

@ -125,7 +125,7 @@ func AcceptEncodings(header string) []string {
// 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.ICache) bool { func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) bool {
log := log.With().Str("ReqId", ctx.ReqId).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()
log.Debug().Msg("Start") log.Debug().Msg("Start")
@ -182,13 +182,10 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
// add extension for encoding // add extension for encoding
path := o.TargetPath + allowedEncodings[encoding] path := o.TargetPath + allowedEncodings[encoding]
reader, header, statusCode, err = giteaClient.ServeRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path)
if statusCode == http.StatusNotFound { if statusCode == 404 {
continue continue
} }
if err != nil {
break
}
log.Debug().Msgf("using %s encoding", encoding) log.Debug().Msgf("using %s encoding", encoding)
if encoding != "identity" { if encoding != "identity" {
header.Set(headerContentEncoding, encoding) header.Set(headerContentEncoding, encoding)
@ -206,7 +203,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
if err != nil && errors.Is(err, gitea.ErrorNotFound) { if err != nil && errors.Is(err, gitea.ErrorNotFound) {
log.Debug().Msg("Handling not found error") log.Debug().Msg("Handling not found error")
// Get and match redirects // Get and match redirects
redirects := o.getRedirects(ctx, giteaClient, redirectsCache) redirects := o.getRedirects(giteaClient, redirectsCache)
if o.matchRedirects(ctx, giteaClient, redirects, redirectsCache) { if o.matchRedirects(ctx, giteaClient, redirects, redirectsCache) {
log.Trace().Msg("redirect") log.Trace().Msg("redirect")
return true return true
@ -234,7 +231,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
} }
} }
log.Debug().Msg("not found") log.Trace().Msg("not found")
ctx.StatusCode = http.StatusNotFound ctx.StatusCode = http.StatusNotFound
if o.TryIndexPages { if o.TryIndexPages {