mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-08-28 20:06:30 +00:00
Add a configuration file.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DataDir string `toml:"data-dir"`
|
||||
Listen struct {
|
||||
Protocol string `toml:"protocol"`
|
||||
Address string `toml:"address"`
|
||||
} `toml:"listen"`
|
||||
}
|
||||
|
||||
func readConfig(path string, config *Config) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
decoder := toml.NewDecoder(file)
|
||||
decoder.DisallowUnknownFields()
|
||||
return decoder.Decode(config)
|
||||
}
|
||||
+6
-9
@@ -37,7 +37,6 @@ func splitHash(hash plumbing.Hash) string {
|
||||
}
|
||||
|
||||
func fetch(
|
||||
dataDir string,
|
||||
webRoot string,
|
||||
repoURL string,
|
||||
branch string,
|
||||
@@ -61,10 +60,10 @@ func fetch(
|
||||
}
|
||||
head := ref.Hash()
|
||||
|
||||
destDir := filepath.Join(dataDir, "tree", splitHash(head))
|
||||
destDir := filepath.Join(config.DataDir, "tree", splitHash(head))
|
||||
if _, err := os.Stat(destDir); errors.Is(err, os.ErrNotExist) {
|
||||
// check out to a temporary directory to avoid TOCTTOU race on destDir
|
||||
tempDir, err := os.MkdirTemp(dataDir, ".tree")
|
||||
tempDir, err := os.MkdirTemp(config.DataDir, ".tree")
|
||||
if err != nil {
|
||||
return FetchResult{err: fmt.Errorf("mkdir temp: %s", err)}
|
||||
}
|
||||
@@ -96,10 +95,10 @@ func fetch(
|
||||
}
|
||||
}
|
||||
|
||||
webLink := filepath.Join(dataDir, "www", webRoot)
|
||||
webLink := filepath.Join(config.DataDir, "www", webRoot)
|
||||
destDirRel, _ := filepath.Rel(filepath.Dir(webLink), destDir)
|
||||
|
||||
tempLink := filepath.Join(dataDir,
|
||||
tempLink := filepath.Join(config.DataDir,
|
||||
fmt.Sprintf(".link.%s.%s", strings.ReplaceAll(webRoot, "/", ".."), head.String()))
|
||||
if err := os.Symlink(destDirRel, tempLink); err != nil {
|
||||
return FetchResult{err: fmt.Errorf("symlink temp: %s", err)}
|
||||
@@ -131,13 +130,12 @@ func fetch(
|
||||
}
|
||||
|
||||
func Fetch(
|
||||
dataDir string,
|
||||
webRoot string,
|
||||
repoURL string,
|
||||
branch string,
|
||||
) FetchResult {
|
||||
log.Println("fetch:", webRoot, repoURL, branch)
|
||||
result := fetch(dataDir, webRoot, repoURL, branch)
|
||||
result := fetch(webRoot, repoURL, branch)
|
||||
if result.err == nil {
|
||||
status := ""
|
||||
switch result.outcome {
|
||||
@@ -156,7 +154,6 @@ func Fetch(
|
||||
}
|
||||
|
||||
func FetchWithTimeout(
|
||||
dataDir string,
|
||||
webRoot string,
|
||||
repoURL string,
|
||||
branch string,
|
||||
@@ -165,7 +162,7 @@ func FetchWithTimeout(
|
||||
// fetch the updated content with a timeout
|
||||
c := make(chan FetchResult, 1)
|
||||
go func() {
|
||||
result := Fetch(dataDir, webRoot, repoURL, branch)
|
||||
result := Fetch(webRoot, repoURL, branch)
|
||||
c <- result
|
||||
}()
|
||||
select {
|
||||
|
||||
+17
-6
@@ -1,18 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dataDir := os.Args[1]
|
||||
listenAddr := os.Args[2]
|
||||
var config Config
|
||||
|
||||
http.HandleFunc("/", Serve(dataDir))
|
||||
err := http.ListenAndServe(listenAddr, nil)
|
||||
func main() {
|
||||
configPath := flag.String("config", "config.toml", "path to configuration file")
|
||||
flag.Parse()
|
||||
|
||||
if err := readConfig(*configPath, &config); err != nil {
|
||||
log.Fatalln("failed to read configuration:", err)
|
||||
}
|
||||
|
||||
listener, err := net.Listen(config.Listen.Protocol, config.Listen.Address)
|
||||
if err != nil {
|
||||
log.Fatalln("failed to listen:", err)
|
||||
}
|
||||
|
||||
http.HandleFunc("/", Serve)
|
||||
if err := http.Serve(listener, nil); err != nil {
|
||||
log.Fatalln("failed to serve:", err)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-27
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
const fetchTimeout = 30 * time.Second
|
||||
|
||||
func getPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
func getPage(w http.ResponseWriter, r *http.Request) error {
|
||||
host := getHost(r)
|
||||
|
||||
// if the first directory of the path exists under `www/$host`, use it as the root,
|
||||
@@ -29,26 +29,26 @@ func getPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
requestPath := path
|
||||
if projectName, projectPath, found := strings.Cut(path, "/"); found {
|
||||
projectRoot := filepath.Join("www", host, projectName)
|
||||
if file, _ := securejoin.OpenInRoot(dataDir, projectRoot); file != nil {
|
||||
if file, _ := securejoin.OpenInRoot(config.DataDir, projectRoot); file != nil {
|
||||
file.Close()
|
||||
wwwRoot, requestPath = projectRoot, projectPath
|
||||
}
|
||||
}
|
||||
|
||||
// try to serve `$root/$path` first
|
||||
file, err := securejoin.OpenInRoot(dataDir, filepath.Join(wwwRoot, requestPath))
|
||||
file, err := securejoin.OpenInRoot(config.DataDir, filepath.Join(wwwRoot, requestPath))
|
||||
if err == nil {
|
||||
// if it's a directory, serve `$root/$path/index.html`
|
||||
stat, statErr := file.Stat()
|
||||
if statErr == nil && stat.IsDir() {
|
||||
defer file.Close()
|
||||
file, err = securejoin.OpenInRoot(dataDir,
|
||||
file, err = securejoin.OpenInRoot(config.DataDir,
|
||||
filepath.Join(wwwRoot, requestPath, "index.html"))
|
||||
}
|
||||
}
|
||||
// if whatever we were serving doesn't exist, try to serve `$root/404.html`
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
file, _ = securejoin.OpenInRoot(dataDir, filepath.Join(wwwRoot, "404.html"))
|
||||
file, _ = securejoin.OpenInRoot(config.DataDir, filepath.Join(wwwRoot, "404.html"))
|
||||
}
|
||||
|
||||
// acquire read capability to the file being served (if possible)
|
||||
@@ -103,7 +103,7 @@ func getProjectName(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func putPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
func putPage(w http.ResponseWriter, r *http.Request) error {
|
||||
host := getHost(r)
|
||||
|
||||
err := authorize(w, r)
|
||||
@@ -130,7 +130,7 @@ func putPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
branch = "pages"
|
||||
}
|
||||
|
||||
result := FetchWithTimeout(dataDir, webRoot, repoURL, branch, fetchTimeout)
|
||||
result := FetchWithTimeout(webRoot, repoURL, branch, fetchTimeout)
|
||||
if result.err == nil {
|
||||
w.Header().Add("Content-Location", r.URL.String())
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func putPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
return result.err
|
||||
}
|
||||
|
||||
func postPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
func postPage(w http.ResponseWriter, r *http.Request) error {
|
||||
host := getHost(r)
|
||||
|
||||
err := authorize(w, r)
|
||||
@@ -199,7 +199,7 @@ func postPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
webRoot := fmt.Sprintf("%s/%s", host, projectName)
|
||||
repoURL := event["repository"].(map[string]any)["clone_url"].(string)
|
||||
|
||||
result := FetchWithTimeout(dataDir, webRoot, repoURL, "pages", fetchTimeout)
|
||||
result := FetchWithTimeout(webRoot, repoURL, "pages", fetchTimeout)
|
||||
switch result.outcome {
|
||||
case FetchError:
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
@@ -214,23 +214,21 @@ func postPage(dataDir string, w http.ResponseWriter, r *http.Request) error {
|
||||
return result.err
|
||||
}
|
||||
|
||||
func Serve(dataDir string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("serve:", r.Method, r.Host, r.URL)
|
||||
err := error(nil)
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
err = getPage(dataDir, w, r)
|
||||
case http.MethodPut:
|
||||
err = putPage(dataDir, w, r)
|
||||
case http.MethodPost:
|
||||
err = postPage(dataDir, w, r)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
err = fmt.Errorf("method %s not allowed", r.Method)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("serve err:", err)
|
||||
}
|
||||
func Serve(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("serve:", r.Method, r.Host, r.URL)
|
||||
err := error(nil)
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
err = getPage(w, r)
|
||||
case http.MethodPut:
|
||||
err = putPage(w, r)
|
||||
case http.MethodPost:
|
||||
err = postPage(w, r)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
err = fmt.Errorf("method %s not allowed", r.Method)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("serve err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user