mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-18 19:16:58 +00:00
Compare commits
36 commits
Author | SHA1 | Date | |
---|---|---|---|
|
d7deecf5f9 | ||
|
0e873d9783 | ||
|
4672cdb54d | ||
|
a0d202ef55 | ||
|
e2395a0574 | ||
|
4efb5a7f3b | ||
|
4b1faa5ebb | ||
|
f123d226a1 | ||
|
a597e40a55 | ||
|
c7fddf202d | ||
|
91c4577a43 | ||
|
7f7ef63e84 | ||
|
a761f12849 | ||
|
0b7687684a | ||
|
9450415545 | ||
|
a77e2d9440 | ||
|
cce656ec4e | ||
|
b69d09e9e1 | ||
|
7ca77716bf | ||
|
ebc1444efb | ||
|
ead959adf7 | ||
|
7fad16b5d6 | ||
|
b62c6bdd2d | ||
|
079fd09c43 | ||
|
2f6960b88a | ||
|
e8f9ec9ce2 | ||
|
905e76deed | ||
|
6fd9474075 | ||
|
229851b75e | ||
|
6376bfd2e0 | ||
|
ef7e2cd7bb | ||
|
2438de0eb2 | ||
|
e079ce4cf4 | ||
|
c9f1624afd | ||
|
044c684a47 | ||
|
85059aa46e |
22 changed files with 2302 additions and 788 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use_flake
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,3 +8,5 @@ vendor/
|
||||||
pages
|
pages
|
||||||
certs.sqlite
|
certs.sqlite
|
||||||
.bash_history
|
.bash_history
|
||||||
|
pkg/
|
||||||
|
.direnv/
|
||||||
|
|
|
@ -5,22 +5,22 @@ when:
|
||||||
steps:
|
steps:
|
||||||
# use vendor to cache dependencies
|
# use vendor to cache dependencies
|
||||||
vendor:
|
vendor:
|
||||||
image: golang:1.23
|
image: golang:1.24
|
||||||
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.22
|
image: codeberg.org/6543/docker-images/golang_just:go-1.24
|
||||||
commands:
|
commands:
|
||||||
- go version
|
- go version
|
||||||
- just build
|
- just build
|
||||||
when:
|
when:
|
||||||
- event: [push, pull_request]
|
- event: [push, pull_request, tag]
|
||||||
|
|
||||||
docker-dryrun:
|
docker-dryrun:
|
||||||
depends_on: vendor
|
depends_on: vendor
|
||||||
image: woodpeckerci/plugin-docker-buildx:5.0.0
|
image: woodpeckerci/plugin-docker-buildx:5.2.1
|
||||||
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.22
|
image: codeberg.org/6543/docker-images/golang_just:go-1.24
|
||||||
commands:
|
commands:
|
||||||
- go version
|
- go version
|
||||||
- just build-tag ${CI_COMMIT_TAG##v}
|
- just build-tag ${CI_COMMIT_TAG##v}
|
||||||
|
@ -41,28 +41,29 @@ steps:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
depends_on: build
|
depends_on: build
|
||||||
image: codeberg.org/6543/docker-images/golang_just:go-1.22
|
image: codeberg.org/6543/docker-images/golang_just:go-1.24
|
||||||
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.22
|
image: codeberg.org/6543/docker-images/golang_just:go-1.24
|
||||||
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.1
|
image: woodpeckerci/plugin-release:0.2.5
|
||||||
settings:
|
settings:
|
||||||
base_url: https://codeberg.org
|
base_url: https://codeberg.org
|
||||||
file_exists: overwrite
|
file_exists: overwrite
|
||||||
|
@ -74,7 +75,7 @@ steps:
|
||||||
|
|
||||||
docker-next:
|
docker-next:
|
||||||
depends_on: vendor
|
depends_on: vendor
|
||||||
image: woodpeckerci/plugin-docker-buildx:5.0.0
|
image: woodpeckerci/plugin-docker-buildx:5.2.1
|
||||||
settings:
|
settings:
|
||||||
registry: codeberg.org
|
registry: codeberg.org
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
@ -89,7 +90,7 @@ steps:
|
||||||
- event: [push]
|
- event: [push]
|
||||||
|
|
||||||
'Publish PR image':
|
'Publish PR image':
|
||||||
image: woodpeckerci/plugin-docker-buildx:5.0.0
|
image: woodpeckerci/plugin-docker-buildx:5.2.1
|
||||||
depends_on: test
|
depends_on: test
|
||||||
settings:
|
settings:
|
||||||
registry: codeberg.org
|
registry: codeberg.org
|
||||||
|
@ -107,7 +108,7 @@ steps:
|
||||||
|
|
||||||
docker-release:
|
docker-release:
|
||||||
depends_on: vendor
|
depends_on: vendor
|
||||||
image: woodpeckerci/plugin-docker-buildx:5.0.0
|
image: woodpeckerci/plugin-docker-buildx:5.2.1
|
||||||
settings:
|
settings:
|
||||||
registry: codeberg.org
|
registry: codeberg.org
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|
|
@ -6,25 +6,25 @@ when:
|
||||||
steps:
|
steps:
|
||||||
lint:
|
lint:
|
||||||
depends_on: []
|
depends_on: []
|
||||||
image: golangci/golangci-lint:v1.61.0
|
image: golangci/golangci-lint:v1.64.8
|
||||||
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 5m --build-tags integration
|
- golangci-lint run --timeout 10m --build-tags integration
|
||||||
|
|
||||||
editor-config:
|
editor-config:
|
||||||
depends_on: []
|
depends_on: []
|
||||||
image: mstruebing/editorconfig-checker:v3.0.3
|
image: mstruebing/editorconfig-checker:v3.2.0
|
||||||
|
|
||||||
yamllint:
|
yamllint:
|
||||||
image: pipelinecomponents/yamllint:0.32.1
|
image: pipelinecomponents/yamllint:0.33.0
|
||||||
depends_on: []
|
depends_on: []
|
||||||
commands:
|
commands:
|
||||||
- yamllint .
|
- yamllint .
|
||||||
|
|
||||||
prettier:
|
prettier:
|
||||||
image: docker.io/woodpeckerci/plugin-prettier:0.2.0
|
image: docker.io/woodpeckerci/plugin-prettier:1.3.0
|
||||||
depends_on: []
|
depends_on: []
|
||||||
settings:
|
settings:
|
||||||
version: 3.2.5
|
version: 3.2.5
|
||||||
|
|
|
@ -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.21.x"
|
ARG XGO="go-1.24.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
4
flake.lock
generated
|
@ -20,8 +20,8 @@
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 0,
|
"lastModified": 0,
|
||||||
"narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=",
|
"narHash": "sha256-WFZDy4bG2RkkCQloIEG8BXEvzyKklFVJbAismOJsIp4=",
|
||||||
"path": "/nix/store/2w8kz6zh3aq80f1dypiin222fry1rv51-source",
|
"path": "/nix/store/c77dsgfxjywplw8bk8s8jlkdsr7a1bi9-source",
|
||||||
"type": "path"
|
"type": "path"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
in {
|
in {
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
gcc
|
glibc.static
|
||||||
go
|
go
|
||||||
gofumpt
|
gofumpt
|
||||||
golangci-lint
|
golangci-lint
|
||||||
|
|
291
go.mod
291
go.mod
|
@ -1,144 +1,237 @@
|
||||||
module codeberg.org/codeberg/pages
|
module codeberg.org/codeberg/pages
|
||||||
|
|
||||||
go 1.22.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.23.1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/sdk/gitea v0.17.1
|
code.gitea.io/sdk/gitea v0.20.0
|
||||||
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.7.0
|
github.com/creasty/defaults v1.8.0
|
||||||
github.com/go-acme/lego/v4 v4.5.3
|
github.com/go-acme/lego/v4 v4.21.0
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
|
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.4.0
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/lib/pq v1.10.7
|
github.com/lib/pq v1.10.9
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.24
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0
|
github.com/pelletier/go-toml/v2 v2.2.3
|
||||||
|
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.27.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.27.5
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||||
xorm.io/xorm v1.3.2
|
xorm.io/xorm v1.3.9
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.54.0 // indirect
|
cloud.google.com/go/auth v0.14.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.7 // 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.19 // indirect
|
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
|
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // 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.1.1 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.33.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/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.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.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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 v0.70.1 // indirect
|
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||||
github.com/exoscale/egoscale v0.67.0 // indirect
|
github.com/exoscale/egoscale/v3 v3.1.8 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-errors/errors v1.0.1 // indirect
|
github.com/fsnotify/fsnotify v1.8.0 // 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-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||||
github.com/goccy/go-json v0.8.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // 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/uuid v1.3.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/gophercloud/gophercloud v0.16.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
github.com/gorilla/css v1.0.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
github.com/gorilla/css v1.0.1 // 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-20200310150728-e0350524596b // indirect
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/kylelemons/godebug v1.1.0 // 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/linode/linodego v0.31.1 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
github.com/linode/linodego v1.46.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/magiconair/properties v1.8.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/miekg/dns v1.1.43 // indirect
|
github.com/mattn/go-isatty v0.0.20 // 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.4.1 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // 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.0.1 // indirect
|
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||||
github.com/nrdcg/desec v0.6.0 // indirect
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // 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.2.0 // indirect
|
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||||
github.com/nrdcg/goinwx v0.8.1 // indirect
|
github.com/nrdcg/goinwx v0.10.0 // 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/porkbun v0.1.1 // indirect
|
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
github.com/nzdjb/go-metaname v1.0.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/pires/go-proxyproto v0.8.0 // indirect
|
github.com/peterhellberg/link v1.2.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.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/pquerna/otp v1.3.0 // indirect
|
github.com/pquerna/otp v1.4.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
||||||
github.com/sacloud/libsacloud v1.36.2 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect
|
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/sacloud/go-http v0.1.9 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
github.com/sacloud/iaas-api-go v1.14.0 // 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.0.3 // indirect
|
github.com/softlayer/softlayer-go v1.1.7 // indirect
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/sony/gobreaker v1.0.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/sourcegraph/conc v0.3.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/transip/gotransip/v6 v6.6.1 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084 // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 // indirect
|
||||||
github.com/vultr/govultr/v2 v2.7.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
go.opencensus.io v0.22.3 // indirect
|
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
|
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
golang.org/x/net v0.23.0 // indirect
|
github.com/volcengine/volc-sdk-golang v1.0.193 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
github.com/vultr/govultr/v3 v3.14.1 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
|
||||||
google.golang.org/api v0.20.0 // indirect
|
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
|
||||||
google.golang.org/appengine v1.6.5 // indirect
|
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
google.golang.org/grpc v1.27.1 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||||
google.golang.org/protobuf v1.26.0 // indirect
|
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
|
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
go.uber.org/atomic v1.11.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
|
||||||
xorm.io/builder v0.3.12 // indirect
|
k8s.io/api v0.32.1 // 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
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,9 +16,11 @@ 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"
|
||||||
|
@ -43,7 +45,11 @@ 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)")
|
||||||
}
|
}
|
||||||
|
@ -100,7 +106,7 @@ func TLSConfig(mainDomainSuffix string,
|
||||||
TargetRepo: targetRepo,
|
TargetRepo: targetRepo,
|
||||||
TargetBranch: targetBranch,
|
TargetBranch: targetBranch,
|
||||||
}
|
}
|
||||||
_, valid := targetOpt.CheckCanonicalDomain(giteaClient, domain, mainDomainSuffix, canonicalDomainCache)
|
_, valid := targetOpt.CheckCanonicalDomain(ctx, 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.
|
||||||
|
@ -116,7 +122,7 @@ func TLSConfig(mainDomainSuffix string,
|
||||||
|
|
||||||
var tlsCertificate *tls.Certificate
|
var tlsCertificate *tls.Certificate
|
||||||
var err error
|
var err error
|
||||||
if tlsCertificate, err = acmeClient.retrieveCertFromDB(domain, mainDomainSuffix, false, certDB); err != nil {
|
if tlsCertificate, err = acmeClient.retrieveCertFromDB(log, domain, mainDomainSuffix, false, certDB); err != nil {
|
||||||
if !errors.Is(err, database.ErrNotFound) {
|
if !errors.Is(err, database.ErrNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -130,7 +136,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(acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB)
|
tlsCertificate, err = acmeClient.obtainCert(log, acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -173,7 +179,7 @@ func (c *AcmeClient) checkUserLimit(user string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) {
|
func (c *AcmeClient) retrieveCertFromDB(log zerolog.Logger, 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 {
|
||||||
|
@ -206,7 +212,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv
|
||||||
// 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(c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil {
|
if _, err := c.obtainCert(log, 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)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
@ -216,7 +222,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv
|
||||||
return &tlsCertificate, nil
|
return &tlsCertificate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) {
|
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) {
|
||||||
name := strings.TrimPrefix(domains[0], "*")
|
name := strings.TrimPrefix(domains[0], "*")
|
||||||
|
|
||||||
// lock to avoid simultaneous requests
|
// lock to avoid simultaneous requests
|
||||||
|
@ -226,7 +232,7 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
_, working = c.obtainLocks.Load(name)
|
_, working = c.obtainLocks.Load(name)
|
||||||
}
|
}
|
||||||
cert, err := c.retrieveCertFromDB(name, mainDomainSuffix, useDnsProvider, keyDatabase)
|
cert, err := c.retrieveCertFromDB(log, 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)
|
||||||
}
|
}
|
||||||
|
@ -320,7 +326,7 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew
|
||||||
return &tlsCertificate, nil
|
return &tlsCertificate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error {
|
func SetupMainDomainCertificates(log zerolog.Logger, 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) {
|
||||||
|
@ -328,7 +334,7 @@ func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient
|
||||||
}
|
}
|
||||||
|
|
||||||
if mainCertBytes == nil {
|
if mainCertBytes == nil {
|
||||||
_, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB)
|
_, err = acmeClient.obtainCert(log, 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")
|
||||||
}
|
}
|
||||||
|
@ -337,7 +343,7 @@ func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) {
|
func MaintainCertDB(log zerolog.Logger, 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)
|
||||||
|
@ -375,7 +381,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *Acm
|
||||||
} 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(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB)
|
_, err = acmeClient.obtainCert(log, 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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,29 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,17 @@ package gitea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"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 (
|
||||||
|
@ -34,16 +37,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileResponse struct {
|
type FileResponse struct {
|
||||||
Exists bool
|
Exists bool `json:"exists"`
|
||||||
IsSymlink bool
|
IsSymlink bool `json:"isSymlink"`
|
||||||
ETag string
|
ETag string `json:"eTag"`
|
||||||
|
MimeType string `json:"mimeType"` // uncompressed MIME type
|
||||||
// uncompressed MIME type
|
RawMime string `json:"rawMime"` // raw MIME type (if compressed, type of compression)
|
||||||
MimeType string
|
Body []byte `json:"-"` // saved separately
|
||||||
|
|
||||||
// raw MIME type (if compressed, type of compression)
|
|
||||||
RawMime string
|
|
||||||
Body []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileResponse) IsEmpty() bool {
|
func (f FileResponse) IsEmpty() bool {
|
||||||
|
@ -69,6 +68,7 @@ func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (head
|
||||||
} else {
|
} else {
|
||||||
header.Set(ContentTypeHeader, f.RawMime)
|
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")
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (head
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchTimestamp struct {
|
type BranchTimestamp struct {
|
||||||
Branch string
|
NotFound bool `json:"notFound"`
|
||||||
Timestamp time.Time
|
Branch string `json:"branch,omitempty"`
|
||||||
notFound bool
|
Timestamp time.Time `json:"timestamp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type writeCacheReader struct {
|
type writeCacheReader struct {
|
||||||
|
@ -89,39 +89,55 @@ type writeCacheReader struct {
|
||||||
cacheKey string
|
cacheKey string
|
||||||
cache cache.ICache
|
cache cache.ICache
|
||||||
hasError bool
|
hasError bool
|
||||||
|
doNotCache 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) {
|
||||||
log.Trace().Msgf("[cache] read %q", t.cacheKey)
|
t.log.Trace().Msgf("[cache] read %q", t.cacheKey)
|
||||||
n, err = t.originalReader.Read(p)
|
n, err = t.originalReader.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
t.complete = true
|
||||||
|
}
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey)
|
t.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 {
|
||||||
_, _ = t.buffer.Write(p[:n])
|
if t.buffer.Len()+n > int(fileCacheSizeLimit) {
|
||||||
|
t.doNotCache = true
|
||||||
|
t.buffer.Reset()
|
||||||
|
} else {
|
||||||
|
_, _ = t.buffer.Write(p[:n])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *writeCacheReader) Close() error {
|
func (t *writeCacheReader) Close() error {
|
||||||
doWrite := !t.hasError
|
doWrite := !t.hasError && !t.doNotCache && t.complete
|
||||||
fc := *t.fileResponse
|
fc := *t.fileResponse
|
||||||
fc.Body = t.buffer.Bytes()
|
fc.Body = t.buffer.Bytes()
|
||||||
if fc.IsEmpty() {
|
|
||||||
log.Trace().Msg("[cache] file response is empty")
|
|
||||||
doWrite = false
|
|
||||||
}
|
|
||||||
if doWrite {
|
if doWrite {
|
||||||
err := t.cache.Set(t.cacheKey, fc, fileCacheTimeout)
|
jsonToCache, err := json.Marshal(fc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey)
|
t.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)
|
||||||
|
if err != nil {
|
||||||
|
t.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)
|
||||||
|
if err != nil {
|
||||||
|
t.log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite)
|
t.log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite)
|
||||||
return t.originalReader.Close()
|
return t.originalReader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser {
|
func (f FileResponse) CreateCacheReader(ctx *context.Context, 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
|
||||||
|
@ -133,5 +149,6 @@ func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cac
|
||||||
fileResponse: &f,
|
fileResponse: &f,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
cacheKey: cacheKey,
|
cacheKey: cacheKey,
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gitea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -18,6 +19,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +49,7 @@ const (
|
||||||
|
|
||||||
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
|
||||||
|
@ -66,8 +69,6 @@ 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
|
||||||
|
@ -78,15 +79,26 @@ func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error)
|
||||||
defaultMimeType = "application/octet-stream"
|
defaultMimeType = "application/octet-stream"
|
||||||
}
|
}
|
||||||
|
|
||||||
sdk, err := gitea.NewClient(
|
sdkClient, err := gitea.NewClient(
|
||||||
giteaRoot,
|
giteaRoot,
|
||||||
gitea.SetHTTPClient(&stdClient),
|
gitea.SetHTTPClient(&http.Client{Timeout: 10 * time.Second}),
|
||||||
|
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: sdk,
|
sdkClient: sdkClient,
|
||||||
|
sdkFileClient: sdkFileClient,
|
||||||
responseCache: respCache,
|
responseCache: respCache,
|
||||||
|
|
||||||
giteaRoot: giteaRoot,
|
giteaRoot: giteaRoot,
|
||||||
|
@ -103,8 +115,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(targetOwner, targetRepo, ref, resource string) ([]byte, error) {
|
func (client *Client) GiteaRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string) ([]byte, error) {
|
||||||
reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false)
|
reader, _, _, err := client.ServeRawContent(ctx, targetOwner, targetRepo, ref, resource, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -112,32 +124,47 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str
|
||||||
return io.ReadAll(reader)
|
return io.ReadAll(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) {
|
func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string, decompress bool) (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("cache_key", cacheKey).Logger()
|
log := log.With().Str("ReqId", ctx.ReqId).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 cache, ok := client.responseCache.Get(cacheKey); ok {
|
if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok {
|
||||||
cache := cache.(FileResponse)
|
var cache FileResponse
|
||||||
|
err := json.Unmarshal(cacheMetadata.([]byte), &cache)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("[cache] failed to unmarshal metadata for: %s", cacheKey)
|
||||||
|
return nil, nil, http.StatusNotFound, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cache.Exists {
|
||||||
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
body, ok := client.responseCache.Get(cacheKey + "|Body")
|
||||||
|
if !ok {
|
||||||
|
log.Error().Msgf("[cache] failed to get body for: %s", cacheKey)
|
||||||
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
||||||
|
}
|
||||||
|
cache.Body = body.([]byte)
|
||||||
|
|
||||||
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress)
|
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress)
|
||||||
// TODO: check against some timestamp mismatch?!?
|
|
||||||
if cache.Exists {
|
if cache.Exists {
|
||||||
log.Debug().Msg("[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(targetOwner, targetRepo, ref, linkDest, decompress)
|
return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress)
|
||||||
} else if !cache.IsEmpty() {
|
} 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
|
||||||
} else if cache.IsEmpty() {
|
|
||||||
log.Debug().Msg("[cache] is empty")
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil, nil, http.StatusNotFound, ErrorNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS)
|
reader, resp, err := client.sdkFileClient.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:
|
||||||
|
@ -166,27 +193,31 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str
|
||||||
ETag: resp.Header.Get(ETagHeader),
|
ETag: resp.Header.Get(ETagHeader),
|
||||||
}
|
}
|
||||||
log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body))
|
log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body))
|
||||||
if err := client.responseCache.Set(cacheKey, fileResponse, fileCacheTimeout); err != nil {
|
jsonToCache, err := json.Marshal(fileResponse)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey)
|
||||||
|
}
|
||||||
|
if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil {
|
||||||
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
|
}
|
||||||
|
if err := client.responseCache.Set(cacheKey+"|Body", fileResponse.Body, fileCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
|
|
||||||
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(targetOwner, targetRepo, ref, linkDest, decompress)
|
return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, rawType := client.getMimeTypeByExtension(resource)
|
||||||
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
||||||
if decompress {
|
if decompress {
|
||||||
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
||||||
} else {
|
} else {
|
||||||
resp.Response.Header.Set(ContentTypeHeader, rawType)
|
resp.Response.Header.Set(ContentTypeHeader, rawType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !shouldRespBeSavedToCache(resp.Response) {
|
|
||||||
return reader, resp.Response.Header, resp.StatusCode, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -194,13 +225,14 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str
|
||||||
MimeType: mimeType,
|
MimeType: mimeType,
|
||||||
RawMime: rawType,
|
RawMime: rawType,
|
||||||
}
|
}
|
||||||
return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
|
return fileResp.CreateCacheReader(ctx, reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
|
||||||
|
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
if err := client.responseCache.Set(cacheKey, FileResponse{
|
jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)})
|
||||||
Exists: false,
|
if err != nil {
|
||||||
ETag: resp.Header.Get(ETagHeader),
|
log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey)
|
||||||
}, fileCacheTimeout); err != nil {
|
}
|
||||||
|
if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,21 +247,36 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str
|
||||||
func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) {
|
func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) {
|
||||||
cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName)
|
cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName)
|
||||||
|
|
||||||
if stamp, ok := client.responseCache.Get(cacheKey); ok && stamp != nil {
|
if stampRaw, ok := client.responseCache.Get(cacheKey); ok {
|
||||||
branchTimeStamp := stamp.(*BranchTimestamp)
|
var stamp BranchTimestamp
|
||||||
if branchTimeStamp.notFound {
|
err := json.Unmarshal(stampRaw.([]byte), &stamp)
|
||||||
log.Trace().Msgf("[cache] use branch %q not found", branchName)
|
if err != nil {
|
||||||
|
log.Error().Err(err).Bytes("stamp", stampRaw.([]byte)).Msgf("[cache] failed to unmarshal timestamp for: %s", cacheKey)
|
||||||
return &BranchTimestamp{}, ErrorNotFound
|
return &BranchTimestamp{}, ErrorNotFound
|
||||||
}
|
}
|
||||||
log.Trace().Msgf("[cache] use branch %q exist", branchName)
|
|
||||||
return branchTimeStamp, nil
|
if stamp.NotFound {
|
||||||
|
log.Trace().Msgf("[cache] branch %q does not exist", branchName)
|
||||||
|
|
||||||
|
return &BranchTimestamp{}, ErrorNotFound
|
||||||
|
} else {
|
||||||
|
log.Trace().Msgf("[cache] use branch %q exist", branchName)
|
||||||
|
// This comes from the refactoring of the caching library.
|
||||||
|
// The branch as reported by the API was stored in the cache, and I'm not sure if there are
|
||||||
|
// situations where it differs from the name in the request, hence this is left here.
|
||||||
|
return &stamp, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName)
|
branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
log.Trace().Msgf("[cache] set cache branch %q not found", branchName)
|
log.Trace().Msgf("[cache] set cache branch %q not found", branchName)
|
||||||
if err := client.responseCache.Set(cacheKey, &BranchTimestamp{Branch: branchName, notFound: true}, branchExistenceCacheTimeout); err != nil {
|
jsonToCache, err := json.Marshal(BranchTimestamp{NotFound: true})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("[cache] marshaling empty timestamp for '%s' has returned an error", cacheKey)
|
||||||
|
}
|
||||||
|
if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return &BranchTimestamp{}, ErrorNotFound
|
return &BranchTimestamp{}, ErrorNotFound
|
||||||
|
@ -246,7 +293,11 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().Msgf("set cache branch [%s] exist", branchName)
|
log.Trace().Msgf("set cache branch [%s] exist", branchName)
|
||||||
if err := client.responseCache.Set(cacheKey, stamp, branchExistenceCacheTimeout); err != nil {
|
jsonToCache, err := json.Marshal(stamp)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("[cache] marshaling timestamp for %q has returned an error", cacheKey)
|
||||||
|
}
|
||||||
|
if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return stamp, nil
|
return stamp, nil
|
||||||
|
@ -255,8 +306,8 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam
|
||||||
func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) {
|
func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) {
|
||||||
cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName)
|
cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName)
|
||||||
|
|
||||||
if branch, ok := client.responseCache.Get(cacheKey); ok && branch != nil {
|
if branch, ok := client.responseCache.Get(cacheKey); ok {
|
||||||
return branch.(string), nil
|
return string(branch.([]byte)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName)
|
repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName)
|
||||||
|
@ -268,7 +319,7 @@ func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (str
|
||||||
}
|
}
|
||||||
|
|
||||||
branch := repo.DefaultBranch
|
branch := repo.DefaultBranch
|
||||||
if err := client.responseCache.Set(cacheKey, branch, defaultBranchCacheTimeout); err != nil {
|
if err := client.responseCache.Set(cacheKey, []byte(branch), defaultBranchCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return branch, nil
|
return branch, nil
|
||||||
|
@ -277,13 +328,14 @@ func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (str
|
||||||
func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
|
func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
|
||||||
cacheKey := fmt.Sprintf("%s/%s", ownerExistenceKeyPrefix, owner)
|
cacheKey := fmt.Sprintf("%s/%s", ownerExistenceKeyPrefix, owner)
|
||||||
|
|
||||||
if exist, ok := client.responseCache.Get(cacheKey); ok && exist != nil {
|
if existRaw, ok := client.responseCache.Get(cacheKey); ok && existRaw != nil {
|
||||||
return exist.(bool), nil
|
exist, err := strconv.ParseBool(existRaw.(string))
|
||||||
|
return exist, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, resp, err := client.sdkClient.GetUserInfo(owner)
|
_, resp, err := client.sdkClient.GetUserInfo(owner)
|
||||||
if resp.StatusCode == http.StatusOK && err == nil {
|
if resp.StatusCode == http.StatusOK && err == nil {
|
||||||
if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil {
|
if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -293,25 +345,27 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
|
||||||
|
|
||||||
_, resp, err = client.sdkClient.GetOrg(owner)
|
_, resp, err = client.sdkClient.GetOrg(owner)
|
||||||
if resp.StatusCode == http.StatusOK && err == nil {
|
if resp.StatusCode == http.StatusOK && err == nil {
|
||||||
if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil {
|
if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if resp.StatusCode != http.StatusNotFound {
|
} else if resp.StatusCode != http.StatusNotFound {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if err := client.responseCache.Set(cacheKey, false, ownerExistenceCacheTimeout); err != nil {
|
if err := client.responseCache.Set(cacheKey, []byte("false"), ownerExistenceCacheTimeout); err != nil {
|
||||||
log.Error().Err(err).Msg("[cache] error on cache write")
|
log.Error().Err(err).Msg("[cache] error on cache write")
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) extToMime(ext string) string {
|
func (client *Client) extToMime(ext string) string {
|
||||||
mimeType := mime.TypeByExtension(ext)
|
mimeType := mime.TypeByExtension(path.Ext(ext))
|
||||||
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)
|
||||||
|
|
||||||
return mimeType
|
return mimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,22 +384,3 @@ func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType
|
||||||
log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType)
|
log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType)
|
||||||
return mimeType, rawType
|
return mimeType, rawType
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldRespBeSavedToCache(resp *http.Response) bool {
|
|
||||||
if resp == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
contentLengthRaw := resp.Header.Get(ContentLengthHeader)
|
|
||||||
if contentLengthRaw == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
contentLength, err := strconv.ParseInt(contentLengthRaw, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("could not parse content length")
|
|
||||||
}
|
|
||||||
|
|
||||||
// if content to big or could not be determined we not cache it
|
|
||||||
return contentLength > 0 && contentLength < fileCacheSizeLimit
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) {
|
||||||
log.Debug().Msg("\n----------------------------------------------------------")
|
|
||||||
log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger()
|
|
||||||
ctx := context.New(w, req)
|
ctx := context.New(w, req)
|
||||||
|
log := log.With().Str("ReqId", ctx.ReqId).Strs("Handler", []string{req.Host, req.RequestURI}).Logger()
|
||||||
|
log.Debug().Msg("\n----------------------------------------------------------")
|
||||||
|
|
||||||
ctx.RespWriter.Header().Set("Server", "pages-server")
|
ctx.RespWriter.Header().Set("Server", "pages-server")
|
||||||
|
|
||||||
|
|
|
@ -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(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache)
|
canonicalDomain, valid := targetOpt.CheckCanonicalDomain(ctx, 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().Msg("tryBranch, now trying upstream 7")
|
log.Debug().Str("url", trimmedHost).Msg("tryBranch, now trying upstream")
|
||||||
tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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),
|
||||||
|
|
|
@ -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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, 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(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(ctx *context.Context, giteaClient *gitea.Client,
|
func tryUpstream(log zerolog.Logger, 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(ctx *context.Context, giteaClient *gitea.Client,
|
||||||
) {
|
) {
|
||||||
// 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(giteaClient, "", mainDomainSuffix, canonicalDomainCache)
|
canonicalDomain, _ := options.CheckCanonicalDomain(ctx, 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,7 +32,12 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +46,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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().Logger().Level(zerolog.TraceLevel)
|
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Caller().Logger().Level(zerolog.TraceLevel)
|
||||||
|
|
||||||
cfg, err := config.ReadConfig(ctx)
|
cfg, err := config.ReadConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -41,7 +41,8 @@ func Serve(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel)
|
fmt.Printf("Setting log level to: %s\n", 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)
|
||||||
|
@ -85,7 +86,7 @@ func Serve(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := certificates.SetupMainDomainCertificates(cfg.Server.MainDomain, acmeClient, certDB); err != nil {
|
if err := certificates.SetupMainDomainCertificates(log.Logger, cfg.Server.MainDomain, acmeClient, certDB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +115,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(certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB)
|
go certificates.MaintainCertDB(log.Logger, 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
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +18,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(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) {
|
func (o *Options) CheckCanonicalDomain(ctx *context.Context, 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)
|
||||||
|
@ -30,7 +31,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain,
|
||||||
return domains[0], valid
|
return domains[0], valid
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig)
|
body, err := giteaClient.GiteaRawContent(ctx, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect {
|
func (o *Options) getRedirects(ctx *context.Context, 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(giteaClient *gitea.Client, redirectsCache cache.I
|
||||||
redirects = cachedValue.([]Redirect)
|
redirects = cachedValue.([]Redirect)
|
||||||
} else {
|
} else {
|
||||||
// Get _redirects file and parse
|
// Get _redirects file and parse
|
||||||
body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig)
|
body, err := giteaClient.GiteaRawContent(ctx, 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,8 +92,9 @@ 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 {
|
||||||
// do rewrite if status code is 200
|
if o.TargetPath == dstURL { // recursion base case, rewrite directly when paths are the same
|
||||||
if redirect.StatusCode == 200 {
|
return true
|
||||||
|
} 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 {
|
||||||
|
|
|
@ -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().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger()
|
log := log.With().Str("ReqId", ctx.ReqId).Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger()
|
||||||
|
|
||||||
log.Debug().Msg("Start")
|
log.Debug().Msg("Start")
|
||||||
|
|
||||||
|
@ -182,10 +182,13 @@ 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(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true)
|
reader, header, statusCode, err = giteaClient.ServeRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true)
|
||||||
if statusCode == 404 {
|
if statusCode == http.StatusNotFound {
|
||||||
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)
|
||||||
|
@ -203,7 +206,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(giteaClient, redirectsCache)
|
redirects := o.getRedirects(ctx, 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
|
||||||
|
@ -231,7 +234,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().Msg("not found")
|
log.Debug().Msg("not found")
|
||||||
|
|
||||||
ctx.StatusCode = http.StatusNotFound
|
ctx.StatusCode = http.StatusNotFound
|
||||||
if o.TryIndexPages {
|
if o.TryIndexPages {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue