Initial cmd.
Signed-off-by: Odyssey <odyssey346@disroot.org>
このコミットが含まれているのは:
コミット
41a7fa5689
24
cmd/gothub/root.go
ノーマルファイル
24
cmd/gothub/root.go
ノーマルファイル
@ -0,0 +1,24 @@
|
||||
package gothub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "gothub",
|
||||
Short: "An alternative front-end for GitHub.",
|
||||
Long: `An alternative front-end for GitHub, written by Odyssey346 and Midou360.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running GotHub's CLI: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
20
cmd/gothub/serve.go
ノーマルファイル
20
cmd/gothub/serve.go
ノーマルファイル
@ -0,0 +1,20 @@
|
||||
package gothub
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"codeberg.org/gothub/gothub/serve"
|
||||
)
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start the web server.",
|
||||
Long: `Start the web server.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
serve.Serve()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
3
go.mod
3
go.mod
@ -15,11 +15,14 @@ require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/spf13/cobra v1.6.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
|
8
go.sum
8
go.sum
@ -100,6 +100,7 @@ github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWH
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -246,6 +247,9 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -342,7 +346,10 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -815,6 +822,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
153
main.go
153
main.go
@ -1,155 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/gofiber/template/html"
|
||||
|
||||
"codeberg.org/gothub/gothub/pages"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/proxy"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
|
||||
"html/template"
|
||||
|
||||
"github.com/carlmjohnson/requests"
|
||||
)
|
||||
import "codeberg.org/gothub/gothub/cmd/gothub"
|
||||
|
||||
func main() {
|
||||
engine := html.New("./views", ".html")
|
||||
|
||||
engine.AddFunc(
|
||||
// add unescape function
|
||||
"unescape", func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
)
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
Views: engine,
|
||||
Prefork: false,
|
||||
AppName: "GotHub",
|
||||
// fucked up way to fix rate limits
|
||||
EnableTrustedProxyCheck: true,
|
||||
TrustedProxies: []string{"0.0.0.0/0"},
|
||||
ProxyHeader: fiber.HeaderXForwardedFor,
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
code := fiber.StatusInternalServerError
|
||||
|
||||
if e, ok := err.(*fiber.Error); ok {
|
||||
code = e.Code
|
||||
}
|
||||
|
||||
err = ctx.Status(code).Render("error", fiber.Map{
|
||||
"error": err,
|
||||
})
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
app.Use(cache.New(cache.Config{
|
||||
Expiration: 5 * time.Minute,
|
||||
}))
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.Render("index", fiber.Map{
|
||||
"host": c.Hostname(),
|
||||
})
|
||||
})
|
||||
|
||||
app.Use(compress.New(compress.Config{
|
||||
Level: compress.LevelBestSpeed, // 1
|
||||
}))
|
||||
|
||||
app.Use(recover.New())
|
||||
|
||||
ratelimiter := limiter.New(limiter.Config{
|
||||
Max: 5,
|
||||
Expiration: 5 * time.Minute,
|
||||
LimitReached: func(c *fiber.Ctx) error {
|
||||
return c.Status(429).Render("ratelimit_gt", fiber.Map{
|
||||
"Title": "Rate limit exceeded",
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
app.Static("/css", "./public/css")
|
||||
app.Static("/fonts", "./public/fonts")
|
||||
app.Static("/robots.txt", "./public/robots.txt")
|
||||
app.Static("/favicon.ico", "./public/assets/favicon.ico")
|
||||
app.Static("/logo.svg", "./public/assets/logo.svg")
|
||||
app.Get("/explore", ratelimiter, pages.HandleExplore)
|
||||
app.Get("/:user", ratelimiter, pages.HandleUser)
|
||||
app.Get("/:user/:repo", ratelimiter, pages.HandleRepo)
|
||||
app.Get("/file/:user/:repo/:branch/:file", func(c *fiber.Ctx) error {
|
||||
var file string
|
||||
url := "https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("file")
|
||||
err := requests.
|
||||
URL(url).
|
||||
ToString(&file).
|
||||
Fetch(context.Background())
|
||||
if err != nil {
|
||||
return c.Status(404).Render("error", fiber.Map{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
return c.Render("file", fiber.Map{
|
||||
"file": file,
|
||||
"fullname": c.Params("user") + "/" + c.Params("repo"),
|
||||
"name": c.Params("file"),
|
||||
"branch": c.Params("branch"),
|
||||
})
|
||||
})
|
||||
app.Get("/avatar/:id", func(c *fiber.Ctx) error {
|
||||
url := "https://avatars.githubusercontent.com/u/" + c.Params("id") + "?v=4"
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
app.Get("/download/:user/:repo/:branch", ratelimiter, func(c *fiber.Ctx) error {
|
||||
url := "https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/archive/" + c.Params("branch") + ".zip"
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
app.Get("/raw/:user/:repo/:branch/:file", ratelimiter, func(c *fiber.Ctx) error {
|
||||
url := "https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("file")
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
val, ok := os.LookupEnv("GOTHUB_PORT")
|
||||
if !ok {
|
||||
val = "3000"
|
||||
} else {
|
||||
log.Println("Using port from env variable")
|
||||
}
|
||||
log.Fatal(app.Listen(":" + val))
|
||||
gothub.Execute()
|
||||
// Looking for the Fiber server? It's in serve/serve.go.
|
||||
}
|
||||
|
144
serve/serve.go
ノーマルファイル
144
serve/serve.go
ノーマルファイル
@ -0,0 +1,144 @@
|
||||
package serve
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gothub/gothub/pages"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
"github.com/gofiber/fiber/v2/middleware/proxy"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/gofiber/template/html"
|
||||
)
|
||||
|
||||
func Serve() {
|
||||
engine := html.New("./views", ".html")
|
||||
|
||||
engine.AddFunc(
|
||||
// add unescape function
|
||||
"unescape", func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
)
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
Views: engine,
|
||||
Prefork: false,
|
||||
AppName: "GotHub",
|
||||
// fucked up way to fix rate limits
|
||||
EnableTrustedProxyCheck: true,
|
||||
TrustedProxies: []string{"0.0.0.0/0"},
|
||||
ProxyHeader: fiber.HeaderXForwardedFor,
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
code := fiber.StatusInternalServerError
|
||||
|
||||
if e, ok := err.(*fiber.Error); ok {
|
||||
code = e.Code
|
||||
}
|
||||
|
||||
err = ctx.Status(code).Render("error", fiber.Map{
|
||||
"error": err,
|
||||
})
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
app.Use(cache.New(cache.Config{
|
||||
Expiration: 5 * time.Minute,
|
||||
}))
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.Render("index", fiber.Map{
|
||||
"host": c.Hostname(),
|
||||
})
|
||||
})
|
||||
|
||||
app.Use(compress.New(compress.Config{
|
||||
Level: compress.LevelBestSpeed, // 1
|
||||
}))
|
||||
|
||||
app.Use(recover.New())
|
||||
|
||||
ratelimiter := limiter.New(limiter.Config{
|
||||
Max: 5,
|
||||
Expiration: 5 * time.Minute,
|
||||
LimitReached: func(c *fiber.Ctx) error {
|
||||
return c.Status(429).Render("ratelimit_gt", fiber.Map{
|
||||
"Title": "Rate limit exceeded",
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
app.Static("/css", "./public/css")
|
||||
app.Static("/fonts", "./public/fonts")
|
||||
app.Static("/robots.txt", "./public/robots.txt")
|
||||
app.Static("/favicon.ico", "./public/assets/favicon.ico")
|
||||
app.Static("/logo.svg", "./public/assets/logo.svg")
|
||||
app.Get("/explore", ratelimiter, pages.HandleExplore)
|
||||
app.Get("/:user", ratelimiter, pages.HandleUser)
|
||||
app.Get("/avatar/:id", func(c *fiber.Ctx) error {
|
||||
url := "https://avatars.githubusercontent.com/u/" + c.Params("id") + "?v=4"
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
app.Get("/:user/:repo", ratelimiter, pages.HandleRepo)
|
||||
app.Get("/file/:user/:repo/:branch/:file", func(c *fiber.Ctx) error {
|
||||
var file string
|
||||
url := "https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("file")
|
||||
err := requests.
|
||||
URL(url).
|
||||
ToString(&file).
|
||||
Fetch(context.Background())
|
||||
if err != nil {
|
||||
return c.Status(404).Render("error", fiber.Map{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
return c.Render("file", fiber.Map{
|
||||
"file": file,
|
||||
"fullname": c.Params("user") + "/" + c.Params("repo"),
|
||||
"name": c.Params("file"),
|
||||
"branch": c.Params("branch"),
|
||||
})
|
||||
})
|
||||
app.Get("/download/:user/:repo/:branch", ratelimiter, func(c *fiber.Ctx) error {
|
||||
url := "https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/archive/" + c.Params("branch") + ".zip"
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
app.Get("/raw/:user/:repo/:branch/:file", ratelimiter, func(c *fiber.Ctx) error {
|
||||
url := "https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("file")
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
val, ok := os.LookupEnv("GOTHUB_PORT")
|
||||
if !ok {
|
||||
val = "3000"
|
||||
} else {
|
||||
log.Println("Using port from env variable")
|
||||
}
|
||||
log.Fatal(app.Listen(":" + val))
|
||||
}
|
読み込み中…
新しいイシューから参照
ユーザーをブロックする