Merge branch dev into master

Reviewed-on: https://codeberg.org/gothub/gothub/pulls/69
This commit is contained in:
Arya K
2023-03-07 12:43:15 +00:00
24 changed files with 955 additions and 555 deletions
+8
View File
@@ -0,0 +1,8 @@
root = true
[*.yml]
end_of_line = lf
insert_final_newline = false
indent_style = tab
indent_size = 2
charset = utf-8
+51
View File
@@ -0,0 +1,51 @@
name: "Instance Request"
about: "Have an instance you want to add to the list? Use this template."
title: "[New Instance] "
labels:
- instance
body:
- type: input
attributes:
label: Server Host
description: Where is your server hosted? (Input your ISP's name if you are hosting at home)
placeholder: OVHcloud, Hetzner, Telenor, etc.
validations:
required: true
- type: input
attributes:
label: Location
description: Which country is your server hosted?
placeholder: Germany, Norway, France etc.
validations:
required: true
- type: checkboxes
attributes:
label: Cloudflare
description: Is your server using Cloudflare's proxy/DDoS protection?
options:
- label: My server uses Cloudflare's proxy/DDoS protection
- type: input
attributes:
label: Instance URL
description: What is the URL of your instance?
placeholder: https://gothub.example.org
validations:
required: true
- type: dropdown
attributes:
label: Which branch of GotHub are you using?
options:
- master
- dev
- '"latest" docker image'
- type: textarea
attributes:
label: Other details
description: Anything else we should know?
placeholder: We use a custom fork over at https://git.example.org/foo/gothub
- type: input
attributes:
label: Contact details
description: Input your contact information should we need to contact you privately. We recommend Matrix. If you don't have a contact method, or don't want your contact method public, tell us.
placeholder: "@foo:example.org"
+5 -2
View File
@@ -18,10 +18,13 @@
# Dependency directories (remove the comment below to include it)
# vendor/
# Gothub binary (on linux and mac)
/gothub
# Go workspace file
go.work
tmp/
# Gothub binary.
gothub
# Fiber compressed files
*.fiber.gz
-72
View File
@@ -1,72 +0,0 @@
pipeline:
dockerize:
when:
branch: [main, master]
event: [push]
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: Dockerfile
registry: https://codeberg.org/v2
repo: codeberg.org/gothub/gothub
platforms: linux/amd64,linux/arm64
tag: latest
username:
from_secret: user
password:
from_secret: passwd
secrets: [user, passwd]
#dockerize-arm64:
# environment:
# - GOOS=linux
# - GOARCH=arm64
# when:
# branch: [main, master]
# event: [push]
# image: plugins/docker
# settings:
# registry: codeberg.org
# repo: codeberg.org/gothub/gothub
# username:
# from_secret: user
# password:
# from_secret: passwd
# # password = key or actual password
# tags: arm64
dockerize-dev:
when:
event: [push]
branch: [dev]
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: Dockerfile
registry: https://codeberg.org/v2
repo: codeberg.org/gothub/gothub
platforms: linux/amd64,linux/arm64
tag: dev
username:
from_secret: user
password:
from_secret: passwd
secrets: [user, passwd]
#dockerize-dev-arm64:
# environment:
# - ARCH=
# - GOOS=linux
# - GOARCH=arm64
# when:
# event: [push]
# branch: [dev]
# image: plugins/docker
# settings:
# registry: codeberg.org
# repo: codeberg.org/gothub/gothub
# username:
# from_secret: user
# password:
# from_secret: passwd
# # password = key or actual password
# tags: ${CI_COMMIT_SHA:0:8}-arm64
# dockerfile: Dockerfile-arm64
# # You can thank the drone docker maintainer for not providing a buildx option.
+17
View File
@@ -0,0 +1,17 @@
pipeline:
dockerize-dev:
when:
event: push
branch: dev
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: Dockerfile
registry: https://codeberg.org/v2
repo: codeberg.org/gothub/gothub
platforms: linux/amd64,linux/arm64
tag: dev
username:
from_secret: user
password:
from_secret: passwd
secrets: [user, passwd]
+17
View File
@@ -0,0 +1,17 @@
pipeline:
dockerize:
when:
branch: main, master
event: push
image: woodpeckerci/plugin-docker-buildx
settings:
dockerfile: Dockerfile
registry: https://codeberg.org/v2
repo: codeberg.org/gothub/gothub
platforms: linux/amd64,linux/arm64
tag: latest
username:
from_secret: user
password:
from_secret: passwd
secrets: [user, passwd]
+28
View File
@@ -0,0 +1,28 @@
# Credit to Hygna (https://codeberg.org/Hygna)
pipeline:
instancejson:
image: python:3-bullseye
secrets: [ssh_key, mail]
when:
- event: cron
cron: instancejson
- path:
include: ["README.md", ".woodpecker/instancejson.yml"]
branch: dev
commands:
# SSH configuration
- mkdir ~/.ssh
- ssh-keyscan -t ed25519 codeberg.org >> ~/.ssh/known_hosts
- eval `ssh-agent`
- echo "$SSH_KEY" | tr -d '\r' | ssh-add -
# Running script
- git clone git@codeberg.org:gothub/gothub-instances.git instances
- cd instances
- pip install -r requirements.txt
- python3 main.py
# Git configuration
- git config --global user.email $MAIL
- git config --global user.name "Woodpecker CI"
- git add instances.json
- git commit -m "Updated instances"
- git push -u origin master
+41 -2
View File
@@ -1,6 +1,7 @@
# GotHub
Alternative front-end for GitHub written with Go. (WIP)
[Matrix](https://matrix.to/#/%23gothub%3Atrygve.me?via=projectsegfau.lt&via=vern.cc&via=matrix.org&via=trygve.me)
## Features
@@ -11,7 +12,27 @@ Alternative front-end for GitHub written with Go. (WIP)
- Save bandwidth - no JavaScript/tracking bloat loaded!
## Instances
The instances list is in the [wiki](https://codeberg.org/gothub/gothub/wiki/Instances).
### Difference between branches
The ``master`` branch runs on the master branch of GotHub. This means that the instance is running a stable, tested version of GotHub that doesn't get updated as much as ``dev``. Doesn't mean they're not bad though!
``dev`` instances run on the dev branch of GotHub. This might mean they are more up-to-date than other instances, but at the same time, they might not be (instance admin might not always update their instance, no auto-updates etc.). You can get the latest and greatest straight out of Git, but you could meet on bugs and other nasty things.
| Link | Cloudflare | Country | ISP | Branch |
| -------- | ---------- | ----------- | ----- | ----- |
| [gh.odyssey346.dev](https://gh.odyssey346.dev) | No | 🇫🇷 | OVHcloud (Trolling Solutions) | master |
| [gh.riverside.rocks](https://gh.riverside.rocks) | No | 🇺🇸 | Comcast | master |
| [gh.vern.cc](https://gh.vern.cc)[^1] | No | 🇺🇸 | Hetzner | master |
| [gh.akisblack.dev](https://gh.akisblack.dev) | No | 🇩🇪 | OVHcloud | master |
| [gothub.lunar.icu](https://gothub.lunar.icu) | Yes | 🇩🇪 | Unesty | master |
| [gh.phreedom.club](https://gh.phreedom.club) | No | 🇮🇸 | Flokinet | master |
| [gh.dev.odyssey346.dev](https://gh.dev.odyssey346.dev) | No | 🇫🇷 | OVHcloud (Trolling Solutions) | dev |
| [gothub.esmailelbob.xyz](https://gothub.esmailelbob.xyz) | No | 🇨🇦 | OVHcloud | dev |
| [dev.gh.akisblack.dev](https://dev.gh.akisblack.dev) | No | 🇩🇪 | OVHcloud | dev |
Have an instance you want on the list? Make an issue or PR!
You can find a json-list of the same in [gothub/gothub-instances](https://codeberg.org/gothub/gothub-instances).
[^1]: Has some modifications: https://git.vern.cc/vern/modifications/src/branch/master/gothub
## Setup
### Docker
@@ -25,6 +46,9 @@ If you are using Portainer or Podman, you know how to create the GotHub containe
### Manual
You will need an installation of Go 1.19+ installed on your system.
We recommend setting the `GO_TELEMETRY` environment variable to `off`. In the future, the Go CLI will send telemetry to Google, which you might not want.
```bash
git clone https://codeberg.org/gothub/gothub.git
cd gothub
@@ -34,9 +58,24 @@ go build -o gothub
If you don't want to use port 3000 for GotHub, set the GOTHUB_PORT environment variable to the port you want to use.
### Web server configuration (Caddy)
We recommend Caddy for the web server, as it provides automatic HTTPS, HTTP/3 and is easy to setup.
```caddyfile
gothub.example.com {
reverse_proxy localhost:(the port for GotHub)
}
```
## Scraping
Other than for the explore page, gothub uses scraping in order to retrieve the information.
This makes it hard for gothub to get ratelimited.
You can also set the GOTHUB_USER_AGENT envvar in order to set the user agent with which GotHub makes requests to Github.
By default, we use the user agent of Chrome 110 on Windows 10.
## DMCA/Legal notice
All content shown on a GotHub instance is from GitHub. Any issues with content shown on a GotHub instance needs to be reported to GitHub, not the instance host's internet or domain provider.
GitHub is a registered trademark of GitHub, Inc. GotHub is not affiliated with GitHub, Inc.
GitHub is a registered trademark of GitHub, Inc. GotHub is not affiliated with GitHub, Inc.
+62
View File
@@ -0,0 +1,62 @@
package gothub
import (
"bytes"
"github.com/spf13/cobra"
"os"
"os/exec"
"runtime"
)
var updateCmd = &cobra.Command{
Use: "update",
Short: "Update GotHub.",
Long: `This command checks for updates and re-builds GotHub. Does not work on Docker`,
Run: func(cmd *cobra.Command, args []string) {
update()
},
}
func init() {
rootCmd.AddCommand(updateCmd)
}
func update() {
if os.Getenv("DOCKER") == "true" {
println("The GotHub update command does not work on Docker. Please re-create the container with a new image instead.")
os.Exit(1)
}
gitPull := exec.Command("git", "pull")
var out bytes.Buffer
gitPull.Stdout = &out
err := gitPull.Run()
if out.String() == "Already up to date.\r" {
println("GotHub is already up to date.")
os.Exit(0)
}
if err != nil {
println("Couldn't run Git pull. Are you using Git?", err)
os.Exit(1)
}
if runtime.GOOS == "windows" {
err := exec.Command("go", "build", "-o", "gothub.exe").Run()
if err != nil {
println("Couldn't build GotHub.", err)
os.Exit(1)
} else {
println("GotHub updated successfully.")
os.Exit(0)
}
} else {
err := exec.Command("go", "build").Run()
if err != nil {
println("Couldn't build GotHub.", err)
os.Exit(1)
} else {
println("GotHub updated successfully.")
os.Exit(0)
}
}
}
+18
View File
@@ -14,17 +14,32 @@ require (
)
require (
github.com/PuerkitoBio/goquery v1.8.0 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/antchfx/htmlquery v1.3.0 // indirect
github.com/antchfx/xmlquery v1.3.15 // indirect
github.com/antchfx/xpath v1.2.3 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gocolly/colly v1.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jawher/mow.cli v1.2.0 // indirect
github.com/kennygrant/sanitize v1.2.4 // indirect
github.com/klauspost/compress v1.15.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/temoto/robotstxt v1.1.2 // 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
@@ -32,4 +47,7 @@ require (
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
+45
View File
@@ -54,7 +54,11 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
@@ -64,6 +68,14 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/antchfx/htmlquery v1.3.0 h1:5I5yNFOVI+egyia5F2s/5Do2nFWxJz41Tr3DyfKD25E=
github.com/antchfx/htmlquery v1.3.0/go.mod h1:zKPDVTMhfOmcwxheXUsx4rKJy8KEY/PU6eXr/2SebQ8=
github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw=
github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA=
github.com/antchfx/xpath v1.2.3 h1:CCZWOzv5bAqjVv0offZ2LVgVYFbeldKQVuLNbViZdes=
github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -108,9 +120,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
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=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -136,6 +151,10 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI=
github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk=
github.com/gofiber/fiber/v2 v2.41.0 h1:YhNoUS/OTjEz+/WLYuQ01xI7RXgKEFnGBKMagAu5f0M=
@@ -148,6 +167,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -174,6 +194,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK0Ja9a3OUa2Fo+EaN0cbLu0eKpBwPFzc8=
@@ -258,6 +279,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
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/jawher/mow.cli v1.2.0 h1:e6ViPPy+82A/NFF/cfbq3Lr6q4JHKT9tyHwTCcUQgQw=
github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko=
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=
@@ -265,6 +288,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
@@ -347,6 +372,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -363,6 +390,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -370,6 +398,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg=
github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -392,6 +422,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
@@ -416,6 +447,7 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -454,6 +486,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -496,8 +529,10 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
@@ -529,6 +564,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -595,12 +631,15 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -610,6 +649,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -668,6 +709,7 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -710,6 +752,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -815,6 +858,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+67
View File
@@ -0,0 +1,67 @@
package pages
import (
"bufio"
"bytes"
"context"
"html/template"
htmlfmt "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/carlmjohnson/requests"
"github.com/gofiber/fiber/v2"
)
// FileView is the file view page
func FileView(c *fiber.Ctx) error {
var file string
url := "https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("+")
err := requests.
URL(url).
ToString(&file).
Fetch(context.Background())
if err != nil {
return c.Status(404).Render("error", fiber.Map{
"error": err,
})
}
lexer := lexers.Match(c.Params("file"))
if lexer == nil {
lexer = lexers.Fallback
}
formatter := htmlfmt.New(
htmlfmt.WithClasses(true),
htmlfmt.WithLineNumbers(true),
htmlfmt.PreventSurroundingPre(false),
)
buf := bytes.Buffer{}
writer := bufio.NewWriter(&buf)
style := styles.Get("native")
tokens, err := lexer.Tokenise(nil, file)
if err != nil {
return c.Status(500).Render("error", fiber.Map{
"error": err,
})
}
formatter.Format(writer, style, tokens)
cssbuf := bytes.Buffer{}
cssw := bufio.NewWriter(&cssbuf)
formatter.WriteCSS(cssw, style)
writer.Flush()
cssw.Flush()
return c.Render("file", fiber.Map{
"file": template.HTML(buf.String()),
"css": template.CSS(cssbuf.String()),
"fullname": c.Params("user") + "/" + c.Params("repo"),
"name": c.Params("file"),
"branch": c.Params("branch"),
})
}
+72 -41
View File
@@ -1,27 +1,28 @@
package pages
import (
"context"
"log"
"codeberg.org/gothub/gothub/utils"
"context"
"github.com/carlmjohnson/requests"
"github.com/gocolly/colly"
"github.com/gofiber/fiber/v2"
"github.com/gomarkdown/markdown"
"log"
"net/http"
"os"
)
type Repo struct {
Fullname string
Description string
HtmlUrl string
Fork bool
Parent string
Stars int64
Forks int64
Watchers int64
Language string
Stars string
Forks string
Watchers string
Language []string
License string
DefaultBranch string
Readme string
}
type RepoFiles struct {
@@ -35,55 +36,85 @@ type RepoFiles struct {
func HandleRepo(c *fiber.Ctx) error {
var repoArray []Repo
var repoFilesArray []RepoFiles
// get repo
repo := utils.GetRequest("https://api.github.com/repos/" + c.Params("user") + "/" + c.Params("repo"))
if repo.Get("message").String() == "Not Found" {
resp, statusErr := http.Get("https://github.com/" + c.Params("user") + "/" + c.Params("repo"))
if statusErr != nil {
log.Println(statusErr)
}
if resp.StatusCode == 404 {
// I need a better way to do this
return c.Status(404).Render("error", fiber.Map{
"title": "Error",
"error": "Repository " + c.Params("user") + "/" + c.Params("repo") + " not found",
})
}
repoFiles := utils.GetRequest("https://api.github.com/repos/" + c.Params("user") + "/" + c.Params("repo") + "/contents")
bruh := repoFiles.Get("#.@pretty").Array()
for _, item := range bruh {
repoFilesArray = append(repoFilesArray, RepoFiles{
Name: item.Get("path").String(),
Path: item.Get("path").String(),
Type: item.Get("type").String(),
Fullname: repo.Get("full_name").String(),
DefaultBranch: repo.Get("default_branch").String(),
})
// Scraping
Scrape := Repo{}
UserAgent, ok := os.LookupEnv("GOTHUB_USER_AGENT")
if !ok {
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
}
var readmee string
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
sc.OnHTML("div.Layout-sidebar", func(e *colly.HTMLElement) {
Scrape.Fullname = c.Params("user") + "/" + c.Params("repo")
Scrape.Description = e.ChildText("p.f4")
Scrape.Stars = e.ChildText("a[href*='/" + c.Params("user") + "/" + c.Params("repo") + "/stargazers' i] strong")
Scrape.Watchers = e.ChildText("a[href*='/" + c.Params("user") + "/" + c.Params("repo") + "/watchers' i] strong")
Scrape.Forks = e.ChildText("a[href*='/" + c.Params("user") + "/" + c.Params("repo") + "/network/members' i] strong")
Scrape.License = e.ChildText("a[data-analytics-event*='{\"category\":\"Repository Overview\",\"action\":\"click\",\"label\":\"location:sidebar;file:license\"}']")
})
sc.OnHTML("div#readme", func(e *colly.HTMLElement) {
Scrape.Readme = e.ChildText("a[href*='#readme']")
})
sc.OnHTML("div.BorderGrid-cell ul.list-style-none", func(e *colly.HTMLElement) {
e.ForEach("li.d-inline .d-inline-flex", func(i int, el *colly.HTMLElement) {
Scrape.Language = append(Scrape.Language, el.ChildText("span.text-bold")+" "+el.ChildText("span:contains('%')"))
})
})
sc.OnHTML("div#repository-container-header", func(e *colly.HTMLElement) {
Scrape.Parent = e.ChildText("span.text-small a")
})
sc.OnHTML("summary[title*='Switch branches or tags']", func(e *colly.HTMLElement) {
Scrape.DefaultBranch = e.ChildText("span.css-truncate-target")
})
sc.OnHTML("div.js-details-container div.Details-content--hidden-not-important", func(e *colly.HTMLElement) {
e.ForEach("div.js-navigation-item", func(i int, el *colly.HTMLElement) {
var FileType string
if el.ChildAttr("div.flex-shrink-0 svg", "aria-label") == "Directory" {
FileType = "dir"
} else {
FileType = "file"
}
repoFilesArray = append(repoFilesArray, RepoFiles{
Name: el.ChildText("div.flex-auto span.d-block a.js-navigation-open"),
Path: el.ChildText("div.flex-auto span.d-block a.js-navigation-open"),
Type: FileType,
Fullname: Scrape.Fullname,
DefaultBranch: Scrape.DefaultBranch,
})
})
})
sc.Visit("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/")
// Add scrape-based info to repoArray
repoArray = append(repoArray, Scrape)
// README
var readmee string
err := requests.
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + repo.Get("default_branch").String() + "/README.md").
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + Scrape.DefaultBranch + "/" + Scrape.Readme).
ToString(&readmee).
Fetch(context.Background())
if err != nil {
readmee = ""
log.Println(err)
}
mightBeUnsafe := markdown.ToHTML([]byte(readmee), nil, nil)
// Trust Nobody
readmeOutput := UGCPolicy().SanitizeBytes(mightBeUnsafe)
repoArray = append(repoArray, Repo{
Fullname: repo.Get("full_name").String(),
Description: repo.Get("description").String(),
HtmlUrl: repo.Get("html_url").String(),
Fork: repo.Get("fork").Bool(),
Stars: repo.Get("stargazers_count").Int(),
Forks: repo.Get("forks_count").Int(),
Watchers: repo.Get("watchers_count").Int(),
Language: repo.Get("language").String(),
License: repo.Get("license").Get("name").String(),
Parent: repo.Get("parent").Get("full_name").String(),
DefaultBranch: repo.Get("default_branch").String(),
})
readmeOutput := utils.UGCPolicy().SanitizeBytes(mightBeUnsafe)
return c.Render("repo", fiber.Map{
"title": "Repository " + c.Params("user") + "/" + c.Params("repo"),
+168 -290
View File
@@ -1,311 +1,189 @@
package pages
import (
"fmt"
"context"
"log"
"github.com/carlmjohnson/requests"
"github.com/gofiber/fiber/v2"
"github.com/gomarkdown/markdown"
"github.com/microcosm-cc/bluemonday"
"os"
"strings"
"codeberg.org/gothub/gothub/utils"
"context"
"regexp"
"strings"
"github.com/carlmjohnson/requests"
"github.com/enescakir/emoji"
"github.com/gocolly/colly"
"github.com/gofiber/fiber/v2"
"github.com/gomarkdown/markdown"
)
type User struct {
Login string
Name string
Bio string
AvatarUrl string
Location string
Following int64
Followers int64
Link string
Company string
Type string
EwTwitter string
Readme string
Login string
Name string
Bio string
Status string
StatusEmoji string
AvatarUrl string
Location string
Email string
Timezone string
Following string
Followers string
Link string
Social []string
Organizations []string
OrgMembers []string
Company string
Type string
Contributions string
Readme string
MainRepos []RepoList
PinOrPopular string
}
type Ratelimit struct {
Remaining int64
Limit int64
type RepoList struct {
Name string
Type string
Desc string
Lang string
Stars string
Forks string
ForkOf string
}
// HandleUser handles the user page.
func HandleUser(c *fiber.Ctx) error {
user := utils.GetRequest("https://api.github.com/users/" + c.Params("user"))
if user.Get("message").String() == "Not Found" {
// Declare Array used for displaying data
var userArray []User
// User README
var readmee string
err0 := requests.
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("user") + "/master/README.md").
ToString(&readmee).
Fetch(context.Background())
if err0 != nil {
err2 := requests.
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/.github/master/profile/README.md").
ToString(&readmee).
Fetch(context.Background())
if err2 != nil {
readmee = ""
log.Println(err0)
}
}
mightBeUnsafe := markdown.ToHTML([]byte(readmee), nil, nil)
// Sanitize the user README incase there is any weird shit in it
readmeOutput := utils.UGCPolicy().SanitizeBytes(mightBeUnsafe)
// Scraping
Scrape := User{
Readme: string(readmeOutput),
}
UserAgent, ok := os.LookupEnv("GOTHUB_USER_AGENT")
if !ok {
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
}
sc1 := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
sc1.OnHTML("div[itemscope]", func(e *colly.HTMLElement) {
Scrape.Type = e.Attr("itemtype")
})
sc1.Visit("https://github.com/" + c.Params("user") + "/")
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
if Scrape.Type == "http://schema.org/Person" {
sc.OnHTML("div.js-profile-editable-replace", func(e *colly.HTMLElement) {
// Main info
Scrape.Login = e.ChildText("span[itemprop*='additionalName']")
Scrape.Name = e.ChildText("span[itemprop*='name']")
Scrape.Bio = e.ChildText("div[data-bio-text] div")
Scrape.AvatarUrl = e.ChildAttr("img[alt*='Avatar']", "src")
// Metadata
Scrape.Location = e.ChildText("li[itemprop*='homeLocation'] span")
Scrape.Timezone = e.ChildText("li[itemprop*='localTime'] span")
Scrape.Company = e.ChildText("li[itemprop*='worksFor'] span")
Scrape.Link = e.ChildText("li[itemprop*='url'] a")
e.ForEach("li[itemprop*='social']", func(i int, el *colly.HTMLElement) {
Scrape.Social = append(Scrape.Social, el.ChildText("a.Link--primary"))
})
// Followers/Following
Scrape.Followers = e.ChildText("a[href*='https://github.com/" + c.Params("user") + "?tab=followers' i] span")
Scrape.Following = e.ChildText("a[href*='https://github.com/" + c.Params("user") + "?tab=following' i] span")
// Organizations
e.ForEach("a[data-hovercard-type*='organization']", func(i int, el *colly.HTMLElement) {
Scrape.Organizations = append(Scrape.Organizations, el.Attr("aria-label"))
})
// User Status
Scrape.Status = e.ChildText("div.user-status-circle-badge div.user-status-message-wrapper div")
Scrape.StatusEmoji = e.ChildAttr("div.user-status-circle-badge div.user-status-emoji-container g-emoji", "alias")
})
// Contributions
sc.OnHTML("div.js-yearly-contributions", func(e *colly.HTMLElement) {
Scrape.Contributions = e.ChildText("h2")
})
} else if Scrape.Type == "http://schema.org/Code" {
sc.OnHTML("div.container-xl div.flex-md-items-center div.flex-1", func(e *colly.HTMLElement) {
// Main info
Scrape.Name = e.ChildText("h1.h2")
Scrape.Bio = e.ChildText("div.color-fg-muted div")
// Metadata
Scrape.Location = e.ChildText("span[itemprop*='location']")
Scrape.Email = e.ChildText("a[itemprop*='email']")
Scrape.Link = e.ChildText("a[itemprop*='url']")
e.ForEach("a.Link--primary", func(i int, el *colly.HTMLElement) {
Scrape.Social = append(Scrape.Social, el.Attr("href"))
})
// Followers
Scrape.Followers = e.ChildText("a[href*='/orgs/" + c.Params("user") + "/followers' i] span")
})
sc.OnHTML("img[alt*='@"+c.Params("user")+"' i]", func(e *colly.HTMLElement) {
Scrape.AvatarUrl = e.Attr("src")
Scrape.Login = e.Attr("alt")
})
// Org Members
sc.OnHTML("div.clearfix", func(e *colly.HTMLElement) {
e.ForEach("a[data-hovercard-type*='user'] img", func(i int, el *colly.HTMLElement) {
Scrape.OrgMembers = append(Scrape.OrgMembers, strings.TrimPrefix(el.Attr("alt"), "@"))
})
})
} else {
return c.Status(404).Render("error", fiber.Map{
"error": "User " + c.Params("user") + " not found",
})
}
if strings.Contains(user.Get("message").String(), "rate limit") { // dont wanna get the status code so i'll just do this instead 👍
ratelimitJSON := utils.GetRequest("https://api.github.com/rate_limit")
fmt.Println(ratelimitJSON)
var ratelimitArray []Ratelimit
ratelimitArray = append(ratelimitArray, Ratelimit{
Remaining: ratelimitJSON.Get("resources.core.remaining").Int(),
Limit: ratelimitJSON.Get("resources.core.limit").Int(),
})
fmt.Println(ratelimitArray)
return c.Render("ratelimit", fiber.Map{
"Title": "GitHub API /users endpoint rate limit exceeded",
"ratelimit": ratelimitArray,
})
} else {
var userArray []User
var readmee string
err := requests.
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("user") + "/master/README.md").
ToString(&readmee).
Fetch(context.Background())
if err != nil {
readmee = ""
log.Println(err)
}
mightBeUnsafe := markdown.ToHTML([]byte(readmee), nil, nil)
// Trust Nobody
readmeOutput := UGCPolicy().SanitizeBytes(mightBeUnsafe)
var link string
if user.Get("blog").String() == "" {
link = ""
} else {
link = user.Get("blog").String()
if strings.HasPrefix(link, "https://") {
link = strings.TrimPrefix(link, "https://")
} else if strings.HasPrefix(link, "http://") {
link = strings.TrimPrefix(link, "http://")
} else {
log.Println("Has no prefix")
}
}
userArray = append(userArray, User{
Login: user.Get("login").String(),
Name: user.Get("name").String(),
Bio: user.Get("bio").String(),
AvatarUrl: "/avatar/" + user.Get("id").String(),
Location: user.Get("location").String(),
Following: user.Get("following").Int(),
Followers: user.Get("followers").Int(),
Link: link,
Company: user.Get("company").String(),
Type: user.Get("type").String(),
EwTwitter: user.Get("twitter_username").String(),
Readme: string(readmeOutput),
})
fmt.Println(userArray)
return c.Render("user", fiber.Map{
"Title": "User " + c.Params("user"),
"user": userArray,
sc.OnHTML("div.js-pinned-items-reorder-container", func(e *colly.HTMLElement) {
e.ForEach("div.pinned-item-list-item-content", func(i int, el *colly.HTMLElement) {
var MainRepo RepoList
MainRepo = RepoList{} // Clear data if old data is present
MainRepo.Name = el.ChildAttr("div.width-full a", "href")
MainRepo.Type = el.ChildText("div.width-full span.Label")
MainRepo.Desc = el.ChildText("p.pinned-item-desc")
MainRepo.Lang = el.ChildText("p.color-fg-muted span[itemprop*='programmingLanguage']")
MainRepo.Stars = el.ChildText("p.color-fg-muted a[href*='/stargazers' i]")
MainRepo.Forks = el.ChildText("p.color-fg-muted a[href*='/forks' i]")
MainRepo.ForkOf = el.ChildText("p.text-small a.Link--muted")
Scrape.MainRepos = append(Scrape.MainRepos, MainRepo)
})
Scrape.PinOrPopular = e.ChildText("h2.text-normal")
})
sc.Visit("https://github.com/" + c.Params("user") + "/")
// Fixing the output a bit
Scrape.AvatarUrl = strings.TrimPrefix(Scrape.AvatarUrl, "https://avatars.githubusercontent.com/u/")
Scrape.AvatarUrl = "/avatar/" + Scrape.AvatarUrl // Avatar needs to be in /avatar so its proxied
if Scrape.StatusEmoji != "" {
Scrape.StatusEmoji = emoji.Parse(":" + Scrape.StatusEmoji + ":") // Convert the emoji code to an actual emoji
}
}
// copied from bluemonday's GitHub repostiory, with some adaptations
func UGCPolicy() *bluemonday.Policy {
p := bluemonday.NewPolicy()
///////////////////////
// Global attributes //
///////////////////////
// "class" is not permitted as we are not allowing users to style their own
// content
p.AllowStandardAttributes()
//////////////////////////////
// Global URL format policy //
//////////////////////////////
p.AllowStandardURLs()
////////////////////////////////
// Declarations and structure //
////////////////////////////////
// "xml" "xslt" "DOCTYPE" "html" "head" are not permitted as we are
// expecting user generated content to be a fragment of HTML and not a full
// document.
//////////////////////////
// Sectioning root tags //
//////////////////////////
// "article" and "aside" are permitted and takes no attributes
p.AllowElements("article", "aside")
// "body" is not permitted as we are expecting user generated content to be a fragment
// of HTML and not a full document.
// "details" is permitted, including the "open" attribute which can either
// be blank or the value "open".
p.AllowAttrs(
"open",
).Matching(regexp.MustCompile(`(?i)^(|open)$`)).OnElements("details")
// "fieldset" is not permitted as we are not allowing forms to be created.
// "figure" is permitted and takes no attributes
p.AllowElements("figure")
// "nav" is not permitted as it is assumed that the site (and not the user)
// has defined navigation elements
// "section" is permitted and takes no attributes
p.AllowElements("section")
// "summary" is permitted and takes no attributes
p.AllowElements("summary")
//////////////////////////
// Headings and footers //
//////////////////////////
// "footer" is not permitted as we expect user content to be a fragment and
// not structural to this extent
// "h1" through "h6" are permitted and take no attributes
p.AllowElements("h1", "h2", "h3", "h4", "h5", "h6")
// "header" is not permitted as we expect user content to be a fragment and
// not structural to this extent
// "hgroup" is permitted and takes no attributes
p.AllowElements("hgroup")
/////////////////////////////////////
// Content grouping and separating //
/////////////////////////////////////
// "blockquote" is permitted, including the "cite" attribute which must be
// a standard URL.
p.AllowAttrs("cite").OnElements("blockquote")
// "br" "div" "hr" "p" "span" "wbr" are permitted and take no attributes
p.AllowElements("br", "div", "hr", "p", "span", "wbr")
///////////
// Links //
///////////
// "a" is permitted
p.AllowAttrs("href").OnElements("a")
// "area" is permitted along with the attributes that map image maps work
p.AllowAttrs("name").Matching(
regexp.MustCompile(`^([\p{L}\p{N}_-]+)$`),
).OnElements("map")
p.AllowAttrs("alt").Matching(bluemonday.Paragraph).OnElements("area")
p.AllowAttrs("coords").Matching(
regexp.MustCompile(`^([0-9]+,)+[0-9]+$`),
).OnElements("area")
p.AllowAttrs("href").OnElements("area")
p.AllowAttrs("rel").Matching(bluemonday.SpaceSeparatedTokens).OnElements("area")
p.AllowAttrs("shape").Matching(
regexp.MustCompile(`(?i)^(default|circle|rect|poly)$`),
).OnElements("area")
p.AllowAttrs("usemap").Matching(
regexp.MustCompile(`(?i)^#[\p{L}\p{N}_-]+$`),
).OnElements("img")
// "link" is not permitted
/////////////////////
// Phrase elements //
/////////////////////
// The following are all inline phrasing elements
p.AllowElements("abbr", "acronym", "cite", "code", "dfn", "em",
"figcaption", "mark", "s", "samp", "strong", "sub", "sup", "var")
// "q" is permitted and "cite" is a URL and handled by URL policies
p.AllowAttrs("cite").OnElements("q")
// "time" is permitted
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("time")
////////////////////
// Style elements //
////////////////////
// block and inline elements that impart no semantic meaning but style the
// document
p.AllowElements("b", "i", "pre", "small", "strike", "tt", "u")
// "style" is not permitted as we are not yet sanitising CSS and it is an
// XSS attack vector
//////////////////////
// HTML5 Formatting //
//////////////////////
// "bdi" "bdo" are permitted
p.AllowAttrs("dir").Matching(bluemonday.Direction).OnElements("bdi", "bdo")
// "rp" "rt" "ruby" are permitted
p.AllowElements("rp", "rt", "ruby")
///////////////////////////
// HTML5 Change tracking //
///////////////////////////
// "del" "ins" are permitted
p.AllowAttrs("cite").Matching(bluemonday.Paragraph).OnElements("del", "ins")
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("del", "ins")
///////////
// Lists //
///////////
p.AllowLists()
////////////
// Tables //
////////////
p.AllowTables()
///////////
// Forms //
///////////
// By and large, forms are not permitted. However there are some form
// elements that can be used to present data, and we do permit those
//
// "button" "fieldset" "input" "keygen" "label" "output" "select" "datalist"
// "textarea" "optgroup" "option" are all not permitted
// "meter" is permitted
p.AllowAttrs(
"value",
"min",
"max",
"low",
"high",
"optimum",
).Matching(bluemonday.Number).OnElements("meter")
// "progress" is permitted
p.AllowAttrs("value", "max").Matching(bluemonday.Number).OnElements("progress")
return p
Scrape.Login = strings.TrimPrefix(Scrape.Login, "@") // Only for orgs
// Remove HTTP(s) from user website url if it exists
if strings.HasPrefix(Scrape.Link, "https://") {
Scrape.Link = strings.TrimPrefix(Scrape.Link, "https://")
} else if strings.HasPrefix(Scrape.Link, "http://") {
Scrape.Link = strings.TrimPrefix(Scrape.Link, "http://")
}
// Add scrape-based info to userArray
userArray = append(userArray, Scrape)
return c.Render("user", fiber.Map{
"title": c.Params("user"),
"user": userArray,
})
}
+43 -34
View File
@@ -12,12 +12,15 @@
--secondary-background: #353535;
--background-darker: #151515;
--accent: #00b7c3;
color-scheme: dark;
}
body {
font-family: 'Lato', sans-serif;
color: #FFF;
background-color: #151515;
transition: ease-in-out .25s;
margin: 0;
}
::-webkit-scrollbar {
@@ -40,10 +43,10 @@ main {
justify-content: space-between;
flex-direction: row;
padding: 8px;
}
.navbarImg {
height: 30px;
margin-bottom: 8px;
background-color: var(--background);
}
.indexGitHub {
@@ -57,22 +60,24 @@ main {
display: flex;
align-items: center;
gap: 8px;
transition: opactiy .25s;
transition: ease-in-out .25s;
color: var(--text);
}
.navbarSlogan:hover {
color: var(--accent);
text-decoration: underline;
text-underline-offset: 5px;
}
.brand:hover {
opacity: .95;
color: var(--text);
text-decoration: underline;
}
.error {
background-color: #252525;
padding: 8px;
border-radius: 8px;
border-radius: 4px;
font-family: 'Lato', sans-serif;
color: #fff;
}
@@ -96,11 +101,20 @@ a:hover {
.exploreIDontKnowWhatToNameThisDiv {
background-color: #252525;
padding: 8px;
border-radius: 8px;
border-radius: 4px;
margin: 8px 0 8px 0;
text-overflow: ellipsis;
white-space: wrap;
text-decoration: none;
display: flex;
transition: ease-in-out .25s;
}
.exploreIDontKnowWhatToNameThisDiv:hover {
background-color: var(--secondary-background);
color: var(--text);
}
/* URI: /:user */
@@ -108,7 +122,7 @@ a:hover {
.userProfile {
background-color: var(--background);
padding: 8px;
border-radius: 8px;
border-radius: 4px;
margin: 8px;
display: flex;
flex-direction: column;
@@ -138,14 +152,14 @@ a:hover {
.userBio, .userReadme {
background-color: var(--background);
padding: 8px;
border-radius: 8px;
border-radius: 4x;
margin: 8px;
}
.userBioText, .userReadmeText {
background-color: var(--background-darker);
padding: 8px;
border-radius: 8px;
border-radius: 4px;
margin-bottom: 0;
}
@@ -153,11 +167,16 @@ a:hover {
margin-top: 0;
}
.sociallinks {
display: flex;
flex-direction: row;
}
/* URI: /:user/:repo */
.button {
background-color: var(--background);
padding: 8px;
border-radius: 8px;
border-radius: 4px;
margin: 8px;
color: var(--text);
text-decoration: none;
@@ -166,7 +185,7 @@ a:hover {
}
.button:hover {
background-color: var(--accent);
background-color: var(--secondary-background);
color: var(--text);
}
@@ -192,7 +211,7 @@ a:hover {
/* URI: /file/:user/:repo/:file */
.userReadme pre {
padding: 8px;
border-radius: 8px;
border-radius: 4px;
/*white-space: pre-wrap;*/
overflow-x: auto;
}
@@ -212,20 +231,15 @@ a:hover {
@media screen and (prefers-color-scheme: light) {
:root {
--text: #000;
--background: #f1f1f1;
--secondary-background: #e1e1e1;
--background-darker: #fff;
--background: #ececec;
--secondary-background: #f5f5f5;
--background-darker: #dddddd;
color-scheme: light;
}
body {
background-color: #fff;
background-color: var(--background-darker);
color: #000;
}
.navbarSlogan {
color: #00b7c3;
}
.brand:hover {
opacity: .75;
}
a {
color: black;
}
@@ -233,18 +247,9 @@ a:hover {
color: #00b7c3;
}
.exploreIDontKnowWhatToNameThisDiv {
background-color: #f1f1f1;
background-color: var(--background);
color: #000;
}
::-webkit-scrollbar {
width: 8px;
height: 10px;
}
::-webkit-scrollbar-thumb {
border-radius: 8px;
background: #f1f1f1;
}
}
@media screen and (max-width: 900px) {
@@ -265,4 +270,8 @@ a:hover {
margin-bottom: 8px;
margin-right: 0;
}
.sociallinks {
flex-direction: column;
}
}
+30 -89
View File
@@ -1,9 +1,6 @@
package serve
import (
"bufio"
"bytes"
"context"
"html/template"
"log"
"os"
@@ -12,17 +9,11 @@ import (
"codeberg.org/gothub/gothub/pages"
"codeberg.org/gothub/gothub/utils"
"github.com/carlmjohnson/requests"
htmlfmt "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"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"
)
@@ -31,7 +22,7 @@ func Serve() {
engine := html.New("./views", ".html")
engine.AddFunc(
// add unescape function
// Add unescape function. This is needed to render HTML from Markdown.
"unescape", func(s string) template.HTML {
return template.HTML(s)
},
@@ -41,7 +32,7 @@ func Serve() {
Views: engine,
Prefork: false,
AppName: "GotHub",
// fucked up way to fix rate limits
// kind of screwed up way to fix rate limits
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0/0"},
ProxyHeader: fiber.HeaderXForwardedFor,
@@ -67,8 +58,6 @@ func Serve() {
Expiration: 5 * time.Minute,
}))
app.Get("/", pages.HandleIndex)
app.Use(compress.New(compress.Config{
Level: compress.LevelBestSpeed, // 1
}))
@@ -85,90 +74,44 @@ func Serve() {
},
})
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")
staticConfig := fiber.Static{
Compress: true,
// Cache-Control: max-age=31536000
MaxAge: 31536000,
}
// add global headers
app.Use(func(c *fiber.Ctx) error {
c.Set("X-Frame-Options", "SAMEORIGIN")
c.Set("X-XSS-Protection", "1; mode=block")
c.Set("X-Content-Type-Options", "nosniff")
c.Set("Referrer-Policy", "no-referrer")
c.Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data; font-src 'self'; script-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self'; connect-src 'self';")
c.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
return c.Next()
})
app.Get("/", pages.HandleIndex)
app.Static("/css", "./public/css", staticConfig)
app.Static("/fonts", "./public/fonts", staticConfig)
app.Static("/robots.txt", "./public/robots.txt", staticConfig)
app.Static("/favicon.ico", "./public/assets/favicon.ico", staticConfig)
app.Static("/logo.svg", "./public/assets/logo.svg", staticConfig)
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)
utils.ProxyRequest(c, "https://avatars.githubusercontent.com/u/"+c.Params("id")+"?v=4")
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,
})
}
lexer := lexers.Match(c.Params("file"))
if lexer == nil {
lexer = lexers.Fallback
}
formatter := htmlfmt.New(
htmlfmt.WithClasses(true),
htmlfmt.WithLineNumbers(true),
htmlfmt.PreventSurroundingPre(false),
)
buf := bytes.Buffer{}
writer := bufio.NewWriter(&buf)
style := styles.Get("native")
tokens, err := lexer.Tokenise(nil, file)
if err != nil {
return c.Status(500).Render("error", fiber.Map{
"error": err,
})
}
formatter.Format(writer, style, tokens)
cssbuf := bytes.Buffer{}
cssw := bufio.NewWriter(&cssbuf)
formatter.WriteCSS(cssw, style)
writer.Flush()
cssw.Flush()
return c.Render("file", fiber.Map{
"file": template.HTML(buf.String()),
"css": template.CSS(cssbuf.String()),
"fullname": c.Params("user") + "/" + c.Params("repo"),
"name": c.Params("file"),
"branch": c.Params("branch"),
})
})
app.Get("/:user/:repo/tree/:branch/+", pages.FileView)
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)
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+"/archive/"+c.Params("branch")+".zip")
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)
utils.ProxyRequest(c, "https://raw.githubusercontent.com/"+c.Params("user")+"/"+c.Params("repo")+"/"+c.Params("branch")+"/"+c.Params("file"))
return nil
})
@@ -185,8 +128,6 @@ func Serve() {
val, ok := os.LookupEnv("GOTHUB_PORT")
if !ok {
val = "3000"
} else {
log.Println("Using port from env variable")
}
log.Fatal(app.Listen(":" + val))
}
+7 -3
View File
@@ -1,10 +1,10 @@
package utils
import (
"github.com/tidwall/gjson"
"io"
"net/http"
"github.com/tidwall/gjson"
"os"
)
func GetRequest(url string) gjson.Result {
@@ -13,8 +13,12 @@ func GetRequest(url string) gjson.Result {
panic(err)
}
UserAgent, ok := os.LookupEnv("GOTHUB_USER_AGENT")
if !ok {
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
}
r.Header.Set("Accept", "application/vnd.github.v3+json")
r.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
r.Header.Set("User-Agent", UserAgent)
client := &http.Client{}
res, err := client.Do(r)
+16
View File
@@ -0,0 +1,16 @@
package utils
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/proxy"
)
// ProxyRequest proxies requests to GitHub media
func ProxyRequest(c *fiber.Ctx, url string) error {
if err := proxy.Do(c, url); err != nil {
return err
}
// Remove Server header from response
c.Response().Header.Del(fiber.HeaderServer)
return nil
}
+198
View File
@@ -0,0 +1,198 @@
package utils
import (
"github.com/microcosm-cc/bluemonday"
"regexp"
)
// copied from bluemonday's GitHub repostiory, with some adaptations
func UGCPolicy() *bluemonday.Policy {
p := bluemonday.NewPolicy()
///////////////////////
// Global attributes //
///////////////////////
// "class" is not permitted as we are not allowing users to style their own
// content
p.AllowStandardAttributes()
//////////////////////////////
// Global URL format policy //
//////////////////////////////
p.AllowStandardURLs()
////////////////////////////////
// Declarations and structure //
////////////////////////////////
// "xml" "xslt" "DOCTYPE" "html" "head" are not permitted as we are
// expecting user generated content to be a fragment of HTML and not a full
// document.
//////////////////////////
// Sectioning root tags //
//////////////////////////
// "article" and "aside" are permitted and takes no attributes
p.AllowElements("article", "aside")
// "body" is not permitted as we are expecting user generated content to be a fragment
// of HTML and not a full document.
// "details" is permitted, including the "open" attribute which can either
// be blank or the value "open".
p.AllowAttrs(
"open",
).Matching(regexp.MustCompile(`(?i)^(|open)$`)).OnElements("details")
// "fieldset" is not permitted as we are not allowing forms to be created.
// "figure" is permitted and takes no attributes
p.AllowElements("figure")
// "nav" is not permitted as it is assumed that the site (and not the user)
// has defined navigation elements
// "section" is permitted and takes no attributes
p.AllowElements("section")
// "summary" is permitted and takes no attributes
p.AllowElements("summary")
//////////////////////////
// Headings and footers //
//////////////////////////
// "footer" is not permitted as we expect user content to be a fragment and
// not structural to this extent
// "h1" through "h6" are permitted and take no attributes
p.AllowElements("h1", "h2", "h3", "h4", "h5", "h6")
// "header" is not permitted as we expect user content to be a fragment and
// not structural to this extent
// "hgroup" is permitted and takes no attributes
p.AllowElements("hgroup")
/////////////////////////////////////
// Content grouping and separating //
/////////////////////////////////////
// "blockquote" is permitted, including the "cite" attribute which must be
// a standard URL.
p.AllowAttrs("cite").OnElements("blockquote")
// "br" "div" "hr" "p" "span" "wbr" are permitted and take no attributes
p.AllowElements("br", "div", "hr", "p", "span", "wbr")
///////////
// Links //
///////////
// "a" is permitted
p.AllowAttrs("href").OnElements("a")
// "area" is permitted along with the attributes that map image maps work
p.AllowAttrs("name").Matching(
regexp.MustCompile(`^([\p{L}\p{N}_-]+)$`),
).OnElements("map")
p.AllowAttrs("alt").Matching(bluemonday.Paragraph).OnElements("area")
p.AllowAttrs("coords").Matching(
regexp.MustCompile(`^([0-9]+,)+[0-9]+$`),
).OnElements("area")
p.AllowAttrs("href").OnElements("area")
p.AllowAttrs("rel").Matching(bluemonday.SpaceSeparatedTokens).OnElements("area")
p.AllowAttrs("shape").Matching(
regexp.MustCompile(`(?i)^(default|circle|rect|poly)$`),
).OnElements("area")
p.AllowAttrs("usemap").Matching(
regexp.MustCompile(`(?i)^#[\p{L}\p{N}_-]+$`),
).OnElements("img")
// "link" is not permitted
/////////////////////
// Phrase elements //
/////////////////////
// The following are all inline phrasing elements
p.AllowElements("abbr", "acronym", "cite", "code", "dfn", "em",
"figcaption", "mark", "s", "samp", "strong", "sub", "sup", "var")
// "q" is permitted and "cite" is a URL and handled by URL policies
p.AllowAttrs("cite").OnElements("q")
// "time" is permitted
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("time")
////////////////////
// Style elements //
////////////////////
// block and inline elements that impart no semantic meaning but style the
// document
p.AllowElements("b", "i", "pre", "small", "strike", "tt", "u")
// "style" is not permitted as we are not yet sanitising CSS and it is an
// XSS attack vector
//////////////////////
// HTML5 Formatting //
//////////////////////
// "bdi" "bdo" are permitted
p.AllowAttrs("dir").Matching(bluemonday.Direction).OnElements("bdi", "bdo")
// "rp" "rt" "ruby" are permitted
p.AllowElements("rp", "rt", "ruby")
///////////////////////////
// HTML5 Change tracking //
///////////////////////////
// "del" "ins" are permitted
p.AllowAttrs("cite").Matching(bluemonday.Paragraph).OnElements("del", "ins")
p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("del", "ins")
///////////
// Lists //
///////////
p.AllowLists()
////////////
// Tables //
////////////
p.AllowTables()
///////////
// Forms //
///////////
// By and large, forms are not permitted. However there are some form
// elements that can be used to present data, and we do permit those
//
// "button" "fieldset" "input" "keygen" "label" "output" "select" "datalist"
// "textarea" "optgroup" "option" are all not permitted
// "meter" is permitted
p.AllowAttrs(
"value",
"min",
"max",
"low",
"high",
"optimum",
).Matching(bluemonday.Number).OnElements("meter")
// "progress" is permitted
p.AllowAttrs("value", "max").Matching(bluemonday.Number).OnElements("progress")
return p
}
+5 -3
View File
@@ -5,8 +5,9 @@
<p><b>25</b> repositories shown, sorted by most stars.</p>
{{ if .repos}}
{{ range $key, $value := .repos}}
<div class="exploreIDontKnowWhatToNameThisDiv">
<a href="/{{.Fullname}}">{{ .Fullname }}</a>
<a href="{{.Fullname}}" class="exploreIDontKnowWhatToNameThisDiv">
<div>
<p>{{.Fullname}}</p>
<p>{{.Description}}</p>
{{ if .Language }}
{{ if .License }}
@@ -21,7 +22,8 @@
<p>⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ No license</p>
{{ end }}
{{ end }}
</div>
</div>
</a>
{{ end }}
{{ else }}
<p>There are no trending repositories at the moment.</p>
+1 -1
View File
@@ -13,7 +13,7 @@
<body>
<nav>
<div class="navbar">
<a href="/" style="text-decoration: none;" class="brand"><img src="/logo.svg" class="navbarImg" alt="GotHub" /><b class="navbarSlogan">GotHub (alpha)</b></a>
<a href="/" style="text-decoration: none;" class="brand"><img src="/logo.svg" class="navbarImg" height="30" width="30" alt=""><b class="navbarSlogan">GotHub (alpha)</b></a>
<div class="navbarLinks">
<a href="/explore">Explore</a>
<a href="https://codeberg.org/gothub/gothub">Source code</a>
+3 -3
View File
@@ -11,7 +11,7 @@
<li>Save bandwidth - no JavaScript/tracking bloat loaded!</li>
</ul>
<h3>Privacy</h3>
<p>GitHub is owned by Microsoft, a big tech company known to track it's users. <a href="https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement" target="_blank">If you care, you can read GitHub's privacy policy here</a> or <a href="https://tosdr.org/en/service/297" target="_blank">if you don't care enough to read all that and want it condensed</a>. GotHub solves this by proxying all GitHub requests for you!</a></p>
<p>GitHub is owned by Microsoft, a big tech company known to track it's users. <a href="https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement" target="_blank">If you care, you can read GitHub's privacy policy here</a> or <a href="https://tosdr.org/en/service/297" target="_blank">if you don't care enough to read all that and want it condensed</a>. GotHub solves this by proxying all GitHub requests for you!</p>
<h3>Usage</h3>
<p>Just replace <code class="indexGitHub">github.com</code> with <code class="indexGitHub">{{.host}}</code>!</p>
<h3>DMCA/Copyright Notice</h3>
@@ -21,11 +21,11 @@
<h3>Credits</h3>
<a href="https://midou36o.github.io">Midou36O</a> - desigining the GotHub logo
<h3>Info</h3>
{{ if eq .version "unknown"}}
{{ if eq .version "unknown, please build with Go 1.13+ or use Git"}}
GotHub version: <code>unknown</code>
{{ else }}
GotHub version: <a href="https://codeberg.org/gothub/gothub/commit/{{ .version}}"><code>{{ .version}}</code></a>
{{ end }}
<br>
Fiber version: {{ .fiberversion}}, running on {{ .goversion}}
</main>
</main>
+17 -9
View File
@@ -10,20 +10,24 @@
<div class="userProfile">
<h1>{{.Fullname}}</h1>
{{ if .Fork }}
{{ if .Parent }}
<p>This repository is a fork of <a href="/{{.Parent}}">{{.Parent}}</a>.</p>
{{ end }}
{{ if .Description }}
<p>{{.Description}}</p>
{{ end }}
{{ if .Language}}
{{ if .License }}
<p>⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ {{.License}} 🗒️ {{.Language}}</p>
<p>⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ {{.License}} 🌿 {{.DefaultBranch}}</p>
{{ else }}
<p>⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ No license 🗒️ {{.Language}}</p>
<p>⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ No license 🌿 {{.DefaultBranch}}</p>
{{ end }}
{{ else }}
{{ end }}
{{ if .Language }}
<p>🗒️
{{range .Language}}
{{.}}
{{end}}
</p>
{{end}}
</div>
{{end}}
{{ if .files}}
@@ -35,7 +39,7 @@
{{ if eq .Type "dir" }}
<li class="filesList"><a href="#" class="filesA">{{.Path}} (directory)</a></li>
{{ else }}
<li class="filesList"><a href="/file/{{.Fullname}}/{{.DefaultBranch}}/{{.Path}}">{{.Path}}</a></li>
<li class="filesList"><a href="/{{.Fullname}}/tree/{{.DefaultBranch}}/{{.Path}}">{{.Path}}</a></li>
{{ end }}
{{ end }}
</ul>
@@ -44,7 +48,11 @@
{{ end }}
{{ if .readme}}
<div class="userReadme">
<h3>README.md</h3>
{{ if .repo }}
{{ range $key, $value := .repo}}
<h3>{{.Readme}}</h3>
{{end}}
{{end}}
<div class="userReadmeText">
{{ unescape .readme}}
</div>
@@ -54,4 +62,4 @@
<h2>Repository not found</h2>
<p>That repository doesn't exist.</p>
{{ end }}
</main>
</main>
+36 -6
View File
@@ -12,23 +12,53 @@
<h1 class="user-profile-h1">{{.Name}}</h1>
{{ end }}
<h2>{{.Login}}</h2>
{{ if eq .Type "User" }}
{{ if eq .Type "http://schema.org/Person" }}
<p><b>{{.Followers}}</b> followers - <b>{{.Following}}</b> following</p>
{{ else }}
<p><b>{{.Followers}}</b> followers</p>
{{ end }}
{{ if .StatusEmoji}}
<p>{{.StatusEmoji}} {{.Status}}</p>
{{ end }}
{{ if .Location }}
<p>🌍 {{.Location}}</p>
{{ end }}
{{ if .Timezone }}
<p>🕑️ {{.Timezone}}</p>
{{ end }}
{{ if .Link }}
<p>🔗 <a href="https://{{.Link}}" target="_blank">{{.Link}}</a></p>
<p>🌐 <a href="https://{{.Link}}" target="_blank">{{.Link}}</a></p>
{{ end }}
{{ if .Email }}
<p>✉️ {{.Email}}</p>
{{ end }}
{{ if .Social }}
<div class="sociallinks">
{{range .Social}}
<p>🔗 <a href="{{.}}" target="_blank">{{.}}</a></p>
{{ end }}
</div>
{{ end }}
{{ if .Organizations }}
<p>Organizations:
{{range .Organizations}}
<a href="/{{.}}" target="_blank">{{.}}</a>
{{ end }}
</p>
{{ end }}
{{ if .OrgMembers }}
<p>Members:
{{range .OrgMembers}}
<a href="/{{.}}" target="_blank">@{{.}}</a>
{{ end }}
</p>
{{ end }}
{{ if .Company }}
<p>🏢 {{.Company}}</p>
{{ end }}
{{ if .EwTwitter }}
<p>🐦 <a href="https://twitter.com/{{.EwTwitter}}" target="_blank">{{.EwTwitter}}</a></p>
{{ end }}
{{ if eq .Type "http://schema.org/Person" }}
<p>✏️ {{ if .Name }} {{.Name}} {{ else }} {{.Login}} {{ end }} has made {{.Contributions}}</p>
{{ end }}
</div>
{{ if .Bio }}
@@ -48,4 +78,4 @@
<h2>User not found</h2>
<p>That user doesn't exist.</p>
{{ end }}
</main>
</main>