package main import ( "embed" "encoding/json" "fmt" "html/template" "io" "mime" "net/http" "os" "path/filepath" "strings" "github.com/gin-gonic/gin" "github.com/joho/godotenv" ) //go:embed pages var embeddedFiles embed.FS type transport struct { apiKey string underlyingTransport http.RoundTripper } func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { if t.apiKey != "" { req.Header.Add("Authorization", "token "+t.apiKey) } return t.underlyingTransport.RoundTrip(req) } func main() { godotenv.Load() // if os.Args[len(os.Args)-1] == "gencrt" { // GenCerts() // os.Exit(0) // } // if _, err := os.Stat("./certs/cert.crt"); err != nil { // fmt.Println("Not found cert file") // GenCerts() // } // if _, err := os.Stat("./certs/priv.key"); err != nil { // fmt.Println("Not fount cert key file") // GenCerts() // } //certFile, _ := os.ReadFile("./certs/cert.crt") //block, _ := pem.Decode(certFile) //if block == nil { // fmt.Println("invalid certificate") // GenCerts() // } // cert, _ := x509.ParseCertificate(block.Bytes) // certExpired := time.Now().After(cert.NotAfter) // if certExpired { // GenCerts() // } rootDomain, exists := os.LookupEnv("ROOT_DOMAIN") if !exists { rootDomain = "localhost" } giteaUrl, exists := os.LookupEnv("GITEA_URL") if !exists { giteaUrl = "https://codeberg.org" } defaultRef, exists := os.LookupEnv("DEFAULT_REF") if !exists { defaultRef = "pages" } indexFile, exists := os.LookupEnv("INDEX_FILE") if !exists { indexFile = "index.html" } directoryIndexStr := os.Getenv("DIRECTORY_INDEX") fmt.Println(rootDomain, giteaUrl) directoryIndex := false if directoryIndexStr == "true" { directoryIndex = true } defaultUser := os.Getenv("DEFAULT_USER") servePort, exists := os.LookupEnv("SERVE_PORT") if !exists { servePort = "8080" } giteaToken := os.Getenv("GITEA_TOKEN") gitea := http.Client{Transport: &transport{underlyingTransport: http.DefaultTransport, apiKey: giteaToken}} r := gin.Default() templ := template.Must(template.New("").ParseFS(embeddedFiles, "pages/*")) r.SetHTMLTemplate(templ) r.GET("/*path", func(ctx *gin.Context) { path := ctx.Params.ByName("path") rawHost := ctx.Request.Host if !strings.HasSuffix(rawHost, rootDomain) { ctx.HTML(404, "error.html", gin.H{ "error_code": "400", "error_message": "invalid root domain", }) return } hostPrefix := strings.TrimSuffix(rawHost, rootDomain) host := strings.Split(hostPrefix, ".") var owner string switch len(host) - 1 { case 0: ctx.HTML(200, "index.html", gin.H{ "root_domain": rootDomain, }) return case 2: owner = host[1] case 1: if defaultUser == "" { goto godefault } owner = defaultUser break godefault: fallthrough default: ctx.HTML(400, "index.html", gin.H{ "root_domain": rootDomain, }) return } repo := host[0] contentPath := giteaUrl + "/api/v1/repos/" + owner + "/" + repo + "/contents/" + path + "?ref=" + defaultRef apiPath := giteaUrl + "/api/v1/repos/" + owner + "/" + repo + "/raw" + path resp, err := gitea.Get(apiPath + "?ref=" + defaultRef) if err != nil { ctx.AbortWithError(500, err) } body, _ := io.ReadAll(resp.Body) var message struct { Message string } if resp.StatusCode == 200 { // if file found ct := mime.TypeByExtension(filepath.Ext(path)) ctx.Data(200, ct, body) } else { // if file not found, and get err message json.Unmarshal(body, &message) if message.Message != "getBlobForEntry" && path != "/" { ctx.HTML(404, "error.html", gin.H{ "error_code": "404", "error_message": "file not found", }) return } // check is index file (from env) exists indexPath := apiPath + "/" + indexFile + "?ref=" + defaultRef resp, _ := gitea.Get(indexPath) if resp.StatusCode == 200 { // index file exist body, _ := io.ReadAll(resp.Body) ctx.Data(200, "text/html", body) return } else { // else this is directory resp, _ := gitea.Get(contentPath) body, _ := io.ReadAll(resp.Body) var dir []struct { Name string Path string } json.Unmarshal(body, &dir) if len(dir) == 0 { // zero files, empty or non-existent repo ctx.HTML(404, "error.html", gin.H{ "error_code": "404", "error_message": "pages for repository not found", }) return } if !directoryIndex { // if directory index disabled ctx.HTML(401, "error.html", gin.H{ "error_code": "401", "error_message": "directory index disabled", }) return } var html string html += "

Index of " + path + "

" html += "Parrent directory
" for _, file := range dir { html += "" + file.Name + "
" } ctx.Data(200, "text/html", []byte(html)) return } } //ctx.JSON(200, gin.H{"host": host, "path": path}) }) r.Run(":" + servePort) }