2021-03-16 22:22:06 +03:00
// Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories.
//
// Mapping custom domains is not static anymore, but can be done with DNS:
//
2021-03-19 23:33:57 +03:00
// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. The
// first line will be the canonical domain/URL; all other occurrences will be redirected to it.
2021-03-16 22:22:06 +03:00
//
// 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to
// "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else):
// www.example.org. IN CNAME main.pages.example.codeberg.page.
2021-03-19 23:33:57 +03:00
//
2021-03-16 22:22:06 +03:00
// 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record
// for "example.org" (if your provider allows ALIAS or similar records):
// example.org IN ALIAS codeberg.page.
//
// Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge.
package main
import (
"bytes"
"crypto/tls"
"fmt"
2021-07-13 16:45:28 +03:00
"log"
2021-03-16 22:22:06 +03:00
"net"
"os"
"time"
_ "embed"
"github.com/valyala/fasthttp"
)
// MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static
// pages, or used for comparison in CNAME lookups. Static pages can be accessed through
// https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages".
2021-07-09 00:08:50 +03:00
var MainDomainSuffix = [ ] byte ( "." + envOr ( "PAGES_DOMAIN" , "codeberg.page" ) )
2021-03-16 22:22:06 +03:00
// GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash.
2021-07-09 00:08:50 +03:00
var GiteaRoot = [ ] byte ( envOr ( "GITEA_ROOT" , "https://codeberg.org" ) )
2021-03-16 22:22:06 +03:00
//go:embed 404.html
var NotFoundPage [ ] byte
// BrokenDNSPage will be shown (with a redirect) when trying to access a domain for which no DNS CNAME record exists.
2021-07-13 16:45:28 +03:00
var BrokenDNSPage = envOr ( "REDIRECT_BROKEN_DNS" , "https://docs.codeberg.org/pages/custom-domains/" )
2021-03-16 22:22:06 +03:00
// RawDomain specifies the domain from which raw repository content shall be served in the following format:
// https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...}
// (set to []byte(nil) to disable raw content hosting)
2021-07-09 00:08:50 +03:00
var RawDomain = [ ] byte ( envOr ( "RAW_DOMAIN" , "raw.codeberg.org" ) )
2021-03-16 22:22:06 +03:00
// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path).
2021-07-13 16:45:28 +03:00
var RawInfoPage = envOr ( "REDIRECT_RAW_INFO" , "https://docs.codeberg.org/pages/raw-content/" )
2021-03-16 22:22:06 +03:00
// AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed.
var AllowedCorsDomains = [ ] [ ] byte {
RawDomain ,
[ ] byte ( "fonts.codeberg.org" ) ,
[ ] byte ( "design.codeberg.org" ) ,
}
// BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages.
var BlacklistedPaths = [ ] [ ] byte {
[ ] byte ( "/.well-known/acme-challenge/" ) ,
}
// IndexPages lists pages that may be considered as index pages for directories.
var IndexPages = [ ] string {
"index.html" ,
}
// main sets up and starts the web server.
func main ( ) {
// Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash
if ! bytes . HasPrefix ( MainDomainSuffix , [ ] byte { '.' } ) {
MainDomainSuffix = append ( [ ] byte { '.' } , MainDomainSuffix ... )
}
GiteaRoot = bytes . TrimSuffix ( GiteaRoot , [ ] byte { '/' } )
// Use HOST and PORT environment variables to determine listening address
2021-07-13 11:28:36 +03:00
address := fmt . Sprintf ( "%s:%s" , envOr ( "HOST" , "[::]" ) , envOr ( "PORT" , "443" ) )
2021-07-13 16:45:28 +03:00
log . Printf ( "Listening on https://%s" , address )
2021-03-16 22:22:06 +03:00
// Enable compression by wrapping the handler() method with the compression function provided by FastHTTP
compressedHandler := fasthttp . CompressHandlerBrotliLevel ( handler , fasthttp . CompressBrotliBestSpeed , fasthttp . CompressBestSpeed )
2021-07-13 16:45:28 +03:00
server := & fasthttp . Server {
2021-03-17 02:24:34 +03:00
Handler : compressedHandler ,
2021-03-16 22:22:06 +03:00
DisablePreParseMultipartForm : false ,
2021-03-17 02:24:34 +03:00
MaxRequestBodySize : 0 ,
NoDefaultServerHeader : true ,
NoDefaultDate : true ,
ReadTimeout : 10 * time . Second ,
2021-03-19 22:58:53 +03:00
Concurrency : 1024 * 32 , // TODO: adjust bottlenecks for best performance with Gitea!
MaxConnsPerIP : 100 ,
2021-07-13 16:45:28 +03:00
}
// Setup listener and TLS
listener , err := net . Listen ( "tcp" , address )
if err != nil {
log . Fatalf ( "Couldn't create listener: %s" , err )
}
listener = tls . NewListener ( listener , tlsConfig )
2021-11-20 17:54:52 +03:00
setupCertificates ( )
2021-07-13 16:45:28 +03:00
// Start the web server
err = server . Serve ( listener )
2021-03-16 22:22:06 +03:00
if err != nil {
2021-07-13 16:45:28 +03:00
log . Fatalf ( "Couldn't start server: %s" , err )
2021-03-16 22:22:06 +03:00
}
}
// envOr reads an environment variable and returns a default value if it's empty.
func envOr ( env string , or string ) string {
if v := os . Getenv ( env ) ; v != "" {
return v
}
return or
}