mirror of
https://codeberg.org/gothub/gothub
synced 2024-12-06 19:16:24 +01:00
merge dev into master
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve Invidious
|
||||
title: "[Bug] "
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!--
|
||||
BEFORE TRYING TO REPORT A BUG:
|
||||
|
||||
* Use the search function to check if there is already an issue open for your problem!
|
||||
* Read the docs (docs.gothub.app)
|
||||
|
||||
If you want to suggest a new feature please use "Feature request" instead
|
||||
If you want to suggest an enhancement to an existing feature please use "Enhancement" instead
|
||||
-->
|
||||
|
||||
### Describe the bug
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!-- Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
### Logs
|
||||
|
||||
<!-- If applicable, copy the log that appear in the browser page where the error is reported. -->
|
||||
|
||||
### Screenshots
|
||||
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context about the problem here.
|
||||
- Browser (if applicable):
|
||||
- OS (if applicable):
|
||||
- Instance: https://gothub.example.com
|
||||
-->
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Enhancement
|
||||
about: Suggest an enhancement for an existing feature
|
||||
title: "[Enhancement] "
|
||||
labels: enhancement
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
|
||||
<!-- If you want to suggest a new feature please use "Feature request" instead -->
|
||||
<!-- If you want to report a bug, please use "Bug report" instead -->
|
||||
|
||||
### Is your enhancement request related to a problem? Please describe.
|
||||
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the enhancement here. -->
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature request] "
|
||||
labels: feature
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
|
||||
<!-- If you want to suggest an enhancement to an existing feature please use "Enhancement" instead -->
|
||||
<!-- If you want to report a bug, please use "Bug report" instead -->
|
||||
|
||||
### Is your feature request related to a problem? Please describe.
|
||||
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
@@ -48,4 +48,9 @@ body:
|
||||
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"
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Instance Rules
|
||||
description: I have read and accepted the [instance rules](https://codeberg.org/gothub/gothub#instance-rules)
|
||||
options:
|
||||
- label: I have read and accepted the instance rules
|
||||
|
||||
@@ -5,6 +5,7 @@ ARG TARGETARCH
|
||||
WORKDIR /src
|
||||
RUN apk --no-cache add git
|
||||
RUN git clone https://codeberg.org/gothub/gothub .
|
||||
RUN git checkout master
|
||||
|
||||
RUN go mod download
|
||||
RUN GOOS=linux GOARCH=$TARGETARCH go build -o /src/gothub -ldflags="-X codeberg.org/gothub/gothub/utils.Branch=master"
|
||||
|
||||
@@ -5,6 +5,7 @@ ARG TARGETARCH
|
||||
WORKDIR /src
|
||||
RUN apk --no-cache add git
|
||||
RUN git clone https://codeberg.org/gothub/gothub .
|
||||
RUN git checkout dev
|
||||
|
||||
RUN go mod download
|
||||
RUN GOOS=linux GOARCH=$TARGETARCH go build -o /src/gothub -ldflags="-X codeberg.org/gothub/gothub/utils.Branch=dev"
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# GotHub
|
||||
|
||||
**GotHub is looking for new maintainers. If you are interested, please checkout [#131](https://codeberg.org/gothub/gothub/issues/131).**
|
||||
|
||||
Alternative front-end for GitHub written with Go. (WIP)
|
||||
|
||||
[Matrix](https://matrix.to/#/#gothub:matrix.org)
|
||||
[](https://matrix.to/#/#gothub:matrix.org)
|
||||
[](https://goreportcard.com/report/codeberg.org/gothub/gothub)
|
||||
[](https://gothub.app)
|
||||
|
||||
## Features
|
||||
|
||||
- Lightweight - for both you and the instance host
|
||||
- No JavaScript - pure HTML and CSS goodness
|
||||
- No requests made to GitHub from the client side - they won't even know you made a request!
|
||||
@@ -17,45 +20,62 @@ Alternative front-end for GitHub written with Go. (WIP)
|
||||
|
||||
Tested with PageSpeed Insights. Using ansible/ansible as a test repo.
|
||||
|
||||
| | [GotHub](https://pagespeed.web.dev/analysis/https-gh-dev-odyssey346-dev-ansible-ansible/2430mn1ece?form_factor=mobile) | [GitHub](https://pagespeed.web.dev/analysis/https-github-com-ansible-ansible/maetxu2zat?form_factor=mobile) |
|
||||
| -------- | ---------- | ----------- |
|
||||
| Performance | 100 | 81 |
|
||||
| Requests | 3 | 88 |
|
||||
| Resource Size | 8kb | 629kb |
|
||||
| Time to Interactive | 0.9s | 2.7s |
|
||||
| | [GotHub](https://pagespeed.web.dev/analysis/https-gh-psf-lt-ansible-ansible/6ssup87ipo?form_factor=mobile) | [GitHub](https://pagespeed.web.dev/analysis/https-github-com-ansible-ansible/maetxu2zat?form_factor=mobile) |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| Performance | 100 | 81 |
|
||||
| Requests | 3 | 88 |
|
||||
| Resource Size | 8kb | 629kb |
|
||||
| Time to Interactive | 0.9s | 2.7s |
|
||||
|
||||
Quite the difference, isn't it?
|
||||
|
||||
## 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 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.akisblack.dev](https://gh.akisblack.dev) | No | Germany | OVHcloud | master |
|
||||
| [gh.odyssey346.dev](https://gh.odyssey346.dev) | No | France | OVHcloud (Trolling Solutions) | master |
|
||||
| [gh.phreedom.club](https://gh.phreedom.club) | No | Iceland | Flokinet | master |
|
||||
| [gothub.projectsegfau.lt](https://gothub.projectsegfau.lt) | No | GeoDNS (Luxembourg/USA/India) | BuyVM/Digital Ocean/Airtel | master |
|
||||
| [gh.riverside.rocks](https://gh.riverside.rocks) | No | USA | Comcast | master |
|
||||
| [gh.fascinated.cc](https://gh.fascinated.cc) | Yes | Germany | Contabo | master |
|
||||
| [gothub.lunar.icu](https://gothub.lunar.icu) | Yes | Germany | Unesty | master |
|
||||
| [gothub.xbdm.fun](https://gothub.xbdm.fun) | Yes | Germany | Hetzner | master |
|
||||
| [dev.gh.akisblack.dev](https://dev.gh.akisblack.dev) | No | Germany | OVHcloud | dev |
|
||||
| [gh.creller.net](https://gh.creller.net) | No | Scotland | British Telecommunications PLC | dev |
|
||||
| [gothub.esmailelbob.xyz](https://gothub.esmailelbob.xyz) | No | Canada | OVHcloud | dev |
|
||||
| [gothub.no-logs.com](https://gothub.no-logs.com/) | No | Sweden | Vultr | dev |
|
||||
| [gh.dev.odyssey346.dev](https://gh.dev.odyssey346.dev) | No | France | OVHcloud (Trolling Solutions) | dev |
|
||||
| [gothub.dev.projectsegfau.lt](https://gothub.dev.projectsegfau.lt) | No | Luxembourg | BuyVM | dev |
|
||||
### 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 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 | Tor | Cloudflare | Country | ISP | Branch |
|
||||
| -------- | -------- | ---------- | ----------- | ----- | ----- |
|
||||
| [gh.bloatcat.tk](https://gh.bloatcat.tk) | No | No | Iceland | 1984 | master |
|
||||
| [gothub.frontendfriendly.xyz](https://gothub.frontendfriendly.xyz) | No | No | USA | Hetzner | master |
|
||||
| [gothub.hyperreal.coffee](https://gh.hyperreal.coffee) | No | No | USA | Linode | master |
|
||||
| [gothub.lunar.icu](https://gothub.lunar.icu) | No | No | Germany | Avoro | master |
|
||||
| [gothub.no-logs.com](https://gothub.no-logs.com/) | No | No | Sweden | Vultr | master |
|
||||
| [g.opnxng.com](https://g.opnxng.com/) | No | No | Singapore | Vultr | master |
|
||||
| [gh.owo.si](https://gh.owo.si/) | [Yes](http://gh.pk47sgwhncn5cgidm7bofngmh7lc7ukjdpk5bjwfemmyp27ovl25ikyd.onion/) | No | Germany | Contabo | master |
|
||||
| [gothub.projectsegfau.lt](https://gothub.projectsegfau.lt) | [Yes](http://gothub.pjsfkvpxlinjamtawaksbnnaqs2fc2mtvmozrzckxh7f3kis6yea25ad.onion/) | No | GeoDNS (Germany/USA/India) | Avoro/Racknerd/Airtel | master |
|
||||
| [gh.whateveritworks.org](https://gh.whateveritworks.org) | No | No | Germany | Hetzner | master |
|
||||
| [gothub.r4fo.com](https://gothub.r4fo.com) | [Yes](http://gothub.r4focoma7gu2zdwwcjjad47ysxt634lg73sxmdbkdozanwqslho5ohyd.onion/) | No | Netherlands | Oracle | master |
|
||||
| [gothub.dev.projectsegfau.lt](https://gothub.dev.projectsegfau.lt) | [Yes](http://gothub.dev.pjsfkvpxlinjamtawaksbnnaqs2fc2mtvmozrzckxh7f3kis6yea25ad.onion/) | No | India | Airtel | dev |
|
||||
| [gh.phreedom.club](https://gh.phreedom.club/) | No | No | Iceland | Flokinet | dev |
|
||||
| [gothub.ducks.party](https://gothub.ducks.party) | No | No | Netherlands | Timeweb | 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).
|
||||
|
||||
### Instance rules
|
||||
|
||||
These are the rules an instance must follow to be in our official instance list.
|
||||
|
||||
Most of these rules have been copied from the [invidious project](https://docs.invidious.io/instances).
|
||||
|
||||
- Instance MUST have been updated in the last month.
|
||||
- Instances using any EXTERNAL DDoS Protection / MITM / Anti-Bot protection MUST be marked as such.
|
||||
- Instances MUST NOT use any sort of web analytics
|
||||
- Instances MUST NOT serve ads
|
||||
- Instances MUST NOT restrict or disallow the access / usage to any natural person (eg. a country's IP range MUST NOT be blocked, access by a natural person MUST NOT be disallowed for arbirary reason) - this rule doesn't apply to juridical persons.
|
||||
|
||||
These rules may change at any time, we will inform the current hosters in the list, and give them 2 weeks so they can correct it
|
||||
|
||||
## Setup
|
||||
|
||||
See [gothub.app/docs](https://gothub.app/docs) for information about setting up GotHub.
|
||||
|
||||
## Scraping
|
||||
|
||||
GotHub utilizes scraping powered by [Colly](http://go-colly.org/) to retrieve information from GitHub for everything except the explore page. By default, GotHub uses Chrome on Windows 10 as the user agent, but you can change it by setting the `GOTHUB_USER_AGENT` environment variable.
|
||||
|
||||
## Funding
|
||||
@@ -67,8 +87,3 @@ Our domain [gothub.app](https://gothub.app) is funded by the [Project Segfault](
|
||||
All content shown on a GotHub instance is from GitHub. Any issues with content shown on a GotHub instance need 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.
|
||||
|
||||
## Credits
|
||||
- [Arya](https://aryak.me) - Main maintainer
|
||||
- [Midou](https://midou.dev) - GotHub logo and many parts of the frontend
|
||||
- [Odyssey346](https://odyssey346.dev) - Creator and ex-maintainer
|
||||
|
||||
@@ -29,6 +29,8 @@ func env() {
|
||||
} else {
|
||||
fmt.Println("Docker: false")
|
||||
}
|
||||
// Proxying
|
||||
fmt.Println("Proxying: " + os.Getenv("GOTHUB_PROXYING_ENABLED"))
|
||||
// IP logging
|
||||
fmt.Println("IP logging: " + os.Getenv("GOTHUB_IP_LOGGED"))
|
||||
// URL request logging
|
||||
|
||||
+13
-1
@@ -29,6 +29,7 @@ type Vars struct {
|
||||
InstanceCountry string
|
||||
InstanceProvider string
|
||||
CloudflareStatus string
|
||||
Proxying string
|
||||
}
|
||||
|
||||
func setup() {
|
||||
@@ -130,6 +131,16 @@ func setup() {
|
||||
vars.CloudflareStatus = "false"
|
||||
}
|
||||
|
||||
// ask user whether they want proxying enabled
|
||||
fmt.Print("Do you want proxying enabled? (y/n) ")
|
||||
var proxying string
|
||||
fmt.Scanln(&proxying)
|
||||
if proxying == "y" {
|
||||
vars.Proxying = "true"
|
||||
} else {
|
||||
vars.Proxying = "false"
|
||||
}
|
||||
|
||||
// save to .env file
|
||||
fmt.Println("Saving environment variables to .env file...")
|
||||
f, err := os.Create(".env")
|
||||
@@ -150,7 +161,8 @@ func setup() {
|
||||
f.WriteString("GOTHUB_INSTANCE_PRIVACY_POLICY=" + vars.PrivacyPolicy + "\n")
|
||||
f.WriteString("GOTHUB_INSTANCE_COUNTRY=" + vars.InstanceCountry + "\n")
|
||||
f.WriteString("GOTHUB_INSTANCE_PROVIDER=" + vars.InstanceProvider + "\n")
|
||||
f.WriteString("GOTHUB_INSTANCE_CLOUDFLARE=" + vars.CloudflareStatus)
|
||||
f.WriteString("GOTHUB_INSTANCE_CLOUDFLARE=" + vars.CloudflareStatus + "\n")
|
||||
f.WriteString("GOTHUB_PROXYING_ENABLED=" + vars.Proxying)
|
||||
|
||||
println("All done! You can now start your GotHub instance with 'gothub serve'.")
|
||||
println("You can review the environment variables with 'gothub env'.")
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
version: "3"
|
||||
services:
|
||||
gothub:
|
||||
image: codeberg.org/gothub/gothub:latest
|
||||
image: codeberg.org/gothub/gothub:latest # Master branch
|
||||
# image: codeberg.org/gothub/gothub:dev # Dev branch
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
@@ -9,6 +9,7 @@ services:
|
||||
environment:
|
||||
- DOCKER=true
|
||||
- GOTHUB_SETUP_COMPLETE=false
|
||||
- GOTHUB_PROXYING_ENABLED=true/false
|
||||
- GOTHUB_IP_LOGGED=true/false
|
||||
- GOTHUB_REQUEST_URL_LOGGED=true/false
|
||||
- GOTHUB_USER_AGENT_LOGGED=true/false
|
||||
@@ -21,4 +22,4 @@ services:
|
||||
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/version || exit 1
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
retries: 2
|
||||
@@ -1,6 +1,6 @@
|
||||
module codeberg.org/gothub/gothub
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.7.0
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type PrivacyInfo struct {
|
||||
ProxyEnabled string
|
||||
IPAddr string
|
||||
ReqURL string
|
||||
UserAgent string
|
||||
@@ -24,6 +25,7 @@ type PrivacyInfo struct {
|
||||
func HandleAbout(c *fiber.Ctx) error {
|
||||
var privacyInfoArray []PrivacyInfo
|
||||
privacyInfoArray = append(privacyInfoArray, PrivacyInfo{
|
||||
ProxyEnabled: os.Getenv("GOTHUB_PROXYING_ENABLED"),
|
||||
IPAddr: os.Getenv("GOTHUB_IP_LOGGED"),
|
||||
ReqURL: os.Getenv("GOTHUB_REQUEST_URL_LOGGED"),
|
||||
UserAgent: os.Getenv("GOTHUB_USER_AGENT_LOGGED"),
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
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/gocolly/colly"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Commit struct {
|
||||
CommitName string
|
||||
CommitDesc string
|
||||
CommitHashShortened string
|
||||
CommitDate string
|
||||
Commiter []string
|
||||
CommitToBranch string
|
||||
PreviousCommit string
|
||||
Additions int64
|
||||
Deletions int64
|
||||
ChangedFiles int64
|
||||
FullName string
|
||||
}
|
||||
|
||||
func HandleCommit(c *fiber.Ctx) error {
|
||||
var commitArray []Commit
|
||||
|
||||
resp, statusErr := http.Get("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/commit/" + c.Params("commit") + "/")
|
||||
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": "Commit" + c.Params("commit") + "not found",
|
||||
})
|
||||
}
|
||||
var file string
|
||||
url := "https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/commit/" + c.Params("commit") + ".patch"
|
||||
err := requests.
|
||||
URL(url).
|
||||
ToString(&file).
|
||||
Fetch(context.Background())
|
||||
if err != nil {
|
||||
return c.Status(404).Render("error", fiber.Map{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
re := regexp.MustCompile("\\A(?s).*?---(?-s).*\\n")
|
||||
file = re.ReplaceAllString(file, "---\n")
|
||||
lexer := lexers.Match("something.diff")
|
||||
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()
|
||||
|
||||
// Scraping
|
||||
Scrape := Commit{}
|
||||
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"
|
||||
}
|
||||
|
||||
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
|
||||
sc.OnHTML("div.commit", func(e *colly.HTMLElement) {
|
||||
Scrape.CommitName = e.ChildText("div.commit-title")
|
||||
Scrape.CommitDesc = e.ChildText("div.commit-desc pre")
|
||||
Scrape.CommitHashShortened = e.ChildText("div.text-lg-right span.m-0 span.sha")
|
||||
Scrape.PreviousCommit = e.ChildText("div.text-lg-right span.ml-0 a.sha")
|
||||
Scrape.CommitDate = e.ChildText("div.flex-self-start relative-time")
|
||||
Scrape.CommitToBranch = e.ChildText("ul.branches-list li.branch a")
|
||||
e.ForEach("div.flex-self-start .commit-author", func(i int, el *colly.HTMLElement) {
|
||||
Scrape.Commiter = append(Scrape.Commiter, strings.TrimPrefix(el.Attr("title"), "View all commits by "))
|
||||
})
|
||||
})
|
||||
Scrape.FullName = c.Params("user") + "/" + c.Params("repo")
|
||||
sc.Visit("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/commit/" + c.Params("commit") + "/")
|
||||
if len(Scrape.PreviousCommit) > 7 {
|
||||
Minus := len(Scrape.PreviousCommit) - 7
|
||||
Scrape.PreviousCommit = Scrape.PreviousCommit[:len(Scrape.PreviousCommit)-Minus]
|
||||
}
|
||||
// Add scrape-based info to commitArray
|
||||
commitArray = append(commitArray, Scrape)
|
||||
|
||||
return c.Render("commit", fiber.Map{
|
||||
"title": "Commit " + Scrape.CommitName + " | " + c.Params("user") + "/" + c.Params("repo"),
|
||||
"commit": commitArray,
|
||||
"file": template.HTML(buf.String()),
|
||||
"css": template.CSS(cssbuf.String()),
|
||||
})
|
||||
}
|
||||
+52
-32
@@ -4,7 +4,8 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"codeberg.org/gothub/gothub/utils"
|
||||
"github.com/gocolly/colly"
|
||||
@@ -12,21 +13,52 @@ import (
|
||||
)
|
||||
|
||||
type Dir struct {
|
||||
Readme string
|
||||
Fullname string
|
||||
DirName string
|
||||
Branch string
|
||||
Readme string
|
||||
Fullname string
|
||||
DirName string
|
||||
Branch string
|
||||
// TruncationWarning string
|
||||
}
|
||||
|
||||
// TODO: Handle repository truncation warning.
|
||||
// Example text: "Sorry, we had to truncate this directory to 1,000 files.
|
||||
// 718 entries were omitted from the list. Latest commit info may be omitted."
|
||||
|
||||
type DirFiles struct {
|
||||
Name string
|
||||
Path string
|
||||
Type string
|
||||
Branch string
|
||||
Fullname string
|
||||
DirName string
|
||||
}
|
||||
|
||||
// Given a gjson object, as well as branch name and fullname ('user/repository' format),
|
||||
// return an array of directory files
|
||||
func return_dir_files_array(dirList gjson.Result, branch string, fullname string) []DirFiles {
|
||||
var dirFilesArray []DirFiles
|
||||
|
||||
dirList.ForEach(func(i, e gjson.Result) bool {
|
||||
var FileType string
|
||||
if e.Get("contentType").Str == "directory" {
|
||||
FileType = "dir"
|
||||
} else {
|
||||
FileType = "file"
|
||||
}
|
||||
|
||||
dirFilesArray = append(dirFilesArray, DirFiles{
|
||||
Name: e.Get("name").Str,
|
||||
// Path is the path to the file/folder from the repository root
|
||||
Path: e.Get("path").Str,
|
||||
Type: FileType,
|
||||
Branch: branch,
|
||||
Fullname: fullname,
|
||||
})
|
||||
|
||||
return true //keep iterating
|
||||
})
|
||||
return dirFilesArray
|
||||
|
||||
}
|
||||
func DirView(c *fiber.Ctx) error {
|
||||
var dirArray []Dir
|
||||
var dirFilesArray []DirFiles
|
||||
@@ -40,7 +72,7 @@ func DirView(c *fiber.Ctx) error {
|
||||
// I need a better way to do this
|
||||
return c.Status(404).Render("error", fiber.Map{
|
||||
"title": "Error",
|
||||
"error": "Directory" + c.Params("+") + "not found",
|
||||
"error": "Directory " + c.Params("+") + " not found",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -56,33 +88,21 @@ func DirView(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
|
||||
sc.OnHTML("div#readme", func(e *colly.HTMLElement) {
|
||||
Scrape.Readme = e.ChildText("a[href='#readme']")
|
||||
|
||||
sc.OnHTML("react-app script[data-target]", func(e *colly.HTMLElement) {
|
||||
jsonMess := e.Text
|
||||
dirList := gjson.Get(jsonMess, "payload.tree.items")
|
||||
dirFilesArray = return_dir_files_array(dirList, Scrape.Branch, Scrape.Fullname)
|
||||
})
|
||||
sc.OnHTML("div#readme div.Box-body", func(e *colly.HTMLElement) {
|
||||
Content, _ := e.DOM.Html()
|
||||
readmeOutput = strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(utils.UGCPolicy().SanitizeBytes([]byte(Content))), "https://github.com", "", -1), "user-content-", "", -1), "https://camo.githubusercontent.com", "/camo", -1), "https://raw.githubusercontent.com", "/raw", -1)
|
||||
})
|
||||
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"
|
||||
}
|
||||
if el.ChildText("div.flex-auto span.d-block a.js-navigation-open") != "" {
|
||||
dirFilesArray = append(dirFilesArray, DirFiles{
|
||||
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,
|
||||
DirName: Scrape.DirName,
|
||||
Branch: Scrape.Branch,
|
||||
})
|
||||
}
|
||||
|
||||
// Get repository truncation warning. This only works with js enabled, so it does not work with colly
|
||||
/*
|
||||
sc.OnHTML("[data-testid='repo-truncation-warning']", func(e *colly.HTMLElement) {
|
||||
fmt.Printf("Truncation warning: %s", e.Text)
|
||||
Scrape.TruncationWarning = e.Text
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
sc.Visit("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/tree/" + c.Params("branch") + "/" + c.Params("+"))
|
||||
// Add scrape-based info to dirArray
|
||||
dirArray = append(dirArray, Scrape)
|
||||
|
||||
@@ -5,12 +5,15 @@ import (
|
||||
"bytes"
|
||||
"codeberg.org/gothub/gothub/utils"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
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"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileView is the file view page
|
||||
@@ -27,6 +30,35 @@ func FileView(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
ext := filepath.Ext(c.Params("+"))
|
||||
// Make an issue for other formats thanks
|
||||
if ext == ".jpeg" || ext == ".jpg" || ext == ".png" || ext == ".svg" || ext == ".gif" || ext == ".webp" {
|
||||
data := []byte(file)
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
||||
base64.StdEncoding.Encode(dst, data)
|
||||
dstStr := string(dst)
|
||||
// Detect content-type since base64 encoding needs content-type
|
||||
mimeType := http.DetectContentType(data)
|
||||
if ext == ".svg" {
|
||||
mimeType = "image/svg+xml"
|
||||
}
|
||||
css := `img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
}`
|
||||
html := "<img src='data:" + mimeType + ";base64," + dstStr + "' alt=" + c.Params("+") + ">"
|
||||
return c.Render("file", fiber.Map{
|
||||
"title": c.Params("+") + " | " + c.Params("user") + "/" + c.Params("repo"),
|
||||
"branch": utils.Branch,
|
||||
"file": html,
|
||||
"css": css,
|
||||
"fullname": c.Params("user") + "/" + c.Params("repo"),
|
||||
"name": c.Params("+"),
|
||||
"fileBranch": c.Params("branch"),
|
||||
"type": "img",
|
||||
})
|
||||
}
|
||||
lexer := lexers.Match(c.Params("+"))
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
@@ -64,5 +96,6 @@ func FileView(c *fiber.Ctx) error {
|
||||
"fullname": c.Params("user") + "/" + c.Params("repo"),
|
||||
"name": c.Params("+"),
|
||||
"fileBranch": c.Params("branch"),
|
||||
"type": "code",
|
||||
})
|
||||
}
|
||||
|
||||
+53
-24
@@ -6,11 +6,19 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"codeberg.org/gothub/gothub/utils"
|
||||
"github.com/gocolly/colly"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type Languages struct {
|
||||
Name string
|
||||
Percent string
|
||||
Color string
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
Fullname string
|
||||
Description string
|
||||
@@ -18,8 +26,9 @@ type Repo struct {
|
||||
Stars string
|
||||
Forks string
|
||||
CommitsBehind string
|
||||
CommitsAhead string
|
||||
Watchers string
|
||||
Language []string
|
||||
Language []Languages
|
||||
License string
|
||||
DefaultBranch string
|
||||
Readme string
|
||||
@@ -56,7 +65,6 @@ func HandleRepo(c *fiber.Ctx) error {
|
||||
"error": "Repository " + c.Params("user") + "/" + repoUrl + branchExists + " not found",
|
||||
})
|
||||
}
|
||||
|
||||
// Scraping
|
||||
Scrape := Repo{}
|
||||
|
||||
@@ -79,18 +87,55 @@ func HandleRepo(c *fiber.Ctx) error {
|
||||
})
|
||||
})
|
||||
sc.OnHTML("div.Box-body div.d-flex div span", func(e *colly.HTMLElement) {
|
||||
Scrape.CommitsBehind = strings.TrimSuffix(e.ChildText("a"), " commits behind")
|
||||
Scrape.CommitsBehind = strings.TrimSuffix(e.ChildText("a[data-analytics-event*='Behind Compare']"), " commits behind")
|
||||
Scrape.CommitsAhead = strings.TrimSuffix(e.ChildText("a[data-analytics-event*='Ahead Compare']"), " commits ahead")
|
||||
})
|
||||
sc.OnHTML("div#readme", func(e *colly.HTMLElement) {
|
||||
Scrape.Readme = e.ChildText("a[href='#readme']")
|
||||
})
|
||||
sc.OnHTML("div#readme div.Box-body", func(e *colly.HTMLElement) {
|
||||
Content, _ := e.DOM.Html()
|
||||
readmeOutput = strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(utils.UGCPolicy().SanitizeBytes([]byte(Content))), "https://github.com", "", -1), "user-content-", "", -1), "https://camo.githubusercontent.com", "/camo", -1), "https://raw.githubusercontent.com", "/raw", -1)
|
||||
|
||||
// jq '.props.initialPayload.overview.overviewFiles[]|select(.tabName == "README").richText'
|
||||
sc.OnHTML("div.Layout-main react-partial script", func(e *colly.HTMLElement) {
|
||||
jsonMess := e.Text
|
||||
|
||||
result := gjson.Get(jsonMess, "props.initialPayload.overview.overviewFiles")
|
||||
result.ForEach(func(i, e gjson.Result) bool {
|
||||
if e.Get("tabName").Str == "README" {
|
||||
unsanitizedOutput := e.Get("richText").Str
|
||||
readmeOutput = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(utils.UGCPolicy().SanitizeBytes([]byte(unsanitizedOutput))), "https://github.com", "", -1), "user-content-", "", -1), "https://camo.githubusercontent.com", "/camo", -1), "https://raw.githubusercontent.com", "/raw", -1), "https://user-images.githubusercontent.com", "/user-images", -1)
|
||||
return false
|
||||
}
|
||||
return true // keep iterating
|
||||
})
|
||||
result = gjson.Get(jsonMess, "props.initialPayload.tree.items")
|
||||
//jq '.props.initialPayload.tree.items'
|
||||
result.ForEach(func(i, e gjson.Result) bool {
|
||||
var FileType string
|
||||
if e.Get("contentType").Str == "directory" {
|
||||
FileType = "dir"
|
||||
} else {
|
||||
FileType = "file"
|
||||
}
|
||||
|
||||
repoFilesArray = append(repoFilesArray, RepoFiles{
|
||||
Name: e.Get("name").Str,
|
||||
Path: e.Get("path").Str,
|
||||
Type: FileType,
|
||||
DefaultBranch: gjson.Get(jsonMess, "props.initialPayload.repo.defaultBranch").Str,
|
||||
Fullname: Scrape.Fullname,
|
||||
})
|
||||
|
||||
return true //keep iterating
|
||||
})
|
||||
})
|
||||
|
||||
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('%')"))
|
||||
var lang Languages
|
||||
lang.Name = el.ChildText("span.text-bold")
|
||||
lang.Percent = el.ChildText("span:contains('%')")
|
||||
lang.Color = strings.ReplaceAll(strings.ReplaceAll(el.ChildAttr("svg", "style"), "color:", ""), ";", "")
|
||||
Scrape.Language = append(Scrape.Language, lang)
|
||||
})
|
||||
})
|
||||
sc.OnHTML("div#repository-container-header", func(e *colly.HTMLElement) {
|
||||
@@ -99,24 +144,8 @@ func HandleRepo(c *fiber.Ctx) error {
|
||||
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") + "/" + repoUrl + branchExists)
|
||||
|
||||
// Add scrape-based info to repoArray
|
||||
repoArray = append(repoArray, Scrape)
|
||||
return c.Render("repo", fiber.Map{
|
||||
|
||||
@@ -158,6 +158,7 @@ func HandleUser(c *fiber.Ctx) error {
|
||||
sc.OnHTML("article.markdown-body", func(e *colly.HTMLElement) {
|
||||
Content, _ := e.DOM.Html()
|
||||
Scrape.Readme = strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(utils.UGCPolicy().SanitizeBytes([]byte(Content))), "https://github.com", "", -1), "user-content-", "", -1), "https://camo.githubusercontent.com", "/camo", -1), "https://raw.githubusercontent.com", "/raw", -1)
|
||||
|
||||
})
|
||||
sc.OnHTML("div.text-mono", func(e *colly.HTMLElement) {
|
||||
Scrape.ReadmeUrl = strings.Replace(e.ChildAttr("a", "href"), "tree", "blob", -1)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"codeberg.org/gothub/gothub/utils"
|
||||
"github.com/gocolly/colly"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Wiki struct {
|
||||
RepoUser string
|
||||
RepoName string
|
||||
Title string
|
||||
LastEdit string
|
||||
LastEditor string
|
||||
RevisionNum string
|
||||
PageNum string
|
||||
PageContent string
|
||||
Sidebar []Sidebar
|
||||
}
|
||||
type Sidebar struct {
|
||||
Name string
|
||||
Link string
|
||||
}
|
||||
|
||||
func WikiView(c *fiber.Ctx) error {
|
||||
var wikiArray []Wiki
|
||||
|
||||
resp, statusErr := http.Get("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/wiki/" + c.Params("page"))
|
||||
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": "Wiki" + c.Params("page") + "not found",
|
||||
})
|
||||
}
|
||||
|
||||
// Scraping
|
||||
Scrape := Wiki{}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
|
||||
sc.OnHTML("div#repository-container-header", func(e *colly.HTMLElement) {
|
||||
Scrape.RepoUser = e.ChildText("span.author a")
|
||||
Scrape.RepoName = e.ChildText("strong a")
|
||||
})
|
||||
sc.OnHTML("div#wiki-wrapper", func(e *colly.HTMLElement) {
|
||||
Scrape.Title = e.ChildText("div.d-flex h1")
|
||||
Scrape.LastEdit = e.ChildText("div.gh-header-meta relative-time")
|
||||
re := regexp.MustCompile(`\ edited.*`)
|
||||
Scrape.LastEditor = re.ReplaceAllString(strings.Replace(e.ChildText("div.gh-header-meta"), "\n", "", -1), "${1}")
|
||||
Scrape.RevisionNum = strings.TrimSuffix(e.ChildText("a.Link--muted"), " revisions")
|
||||
})
|
||||
sc.OnHTML("div.wiki-pages-box div.Box--condensed", func(e *colly.HTMLElement) {
|
||||
Scrape.PageNum = e.ChildText("span.Counter--primary")
|
||||
e.ForEach("ul.list-style-none li.Box-row", func(i int, el *colly.HTMLElement) {
|
||||
Scrape.Sidebar = append(Scrape.Sidebar, Sidebar{
|
||||
Name: el.ChildText("summary span a.Truncate-text"),
|
||||
Link: el.ChildAttr("summary span a.Truncate-text", "href"),
|
||||
})
|
||||
})
|
||||
})
|
||||
sc.OnHTML("div#wiki-body > div.markdown-body", func(e *colly.HTMLElement) {
|
||||
Content, _ := e.DOM.Html()
|
||||
Scrape.PageContent = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(utils.UGCPolicy().SanitizeBytes([]byte(Content))), "https://github.com", "", -1), "user-content-", "", -1), "https://camo.githubusercontent.com", "/camo", -1), "https://raw.githubusercontent.com", "/raw", -1), "https://user-images.githubusercontent.com", "/user-images", -1)
|
||||
|
||||
})
|
||||
sc.Visit("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/wiki/" + c.Params("page"))
|
||||
// Add scrape-based info to wikiArray
|
||||
wikiArray = append(wikiArray, Scrape)
|
||||
|
||||
return c.Render("wiki", fiber.Map{
|
||||
"title": "Wiki page " + c.Params("page") + " | " + c.Params("user") + "/" + c.Params("repo"),
|
||||
"wiki": wikiArray,
|
||||
})
|
||||
}
|
||||
+71
-7
@@ -6,12 +6,14 @@
|
||||
--secondary-background: #353535;
|
||||
--background-darker: #151515;
|
||||
--accent: #00b7c3;
|
||||
--yellow: #8B8000;
|
||||
--yellow: #8b8000;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui,
|
||||
helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial,
|
||||
sans-serif;
|
||||
color: var(--text);
|
||||
background-color: var(--background-darker);
|
||||
transition: ease-in-out 0.25s;
|
||||
@@ -32,6 +34,20 @@ main {
|
||||
margin: 0 24vw;
|
||||
}
|
||||
|
||||
.margin-ow {
|
||||
margin: 0 12vw;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow: scroll;
|
||||
background: var(--secondary-background);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
}
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -73,7 +89,9 @@ main {
|
||||
background-color: var(--background);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui,
|
||||
helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial,
|
||||
sans-serif;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@@ -95,7 +113,9 @@ a:hover {
|
||||
background-color: var(--yellow);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui,
|
||||
helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial,
|
||||
sans-serif;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@@ -111,7 +131,8 @@ a:hover {
|
||||
|
||||
/* URI: /explore */
|
||||
|
||||
.explore-card, .user-repo-card {
|
||||
.explore-card,
|
||||
.user-repo-card {
|
||||
background-color: var(--background);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
@@ -125,7 +146,8 @@ a:hover {
|
||||
transition: ease-in-out 0.25s;
|
||||
}
|
||||
|
||||
.explore-card:hover, .user-repo-card:hover {
|
||||
.explore-card:hover,
|
||||
.user-repo-card:hover {
|
||||
background-color: var(--secondary-background);
|
||||
color: var(--text);
|
||||
}
|
||||
@@ -147,6 +169,7 @@ a:hover {
|
||||
margin: 4px;
|
||||
word-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.user-profile h2 {
|
||||
@@ -211,6 +234,20 @@ a:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.lang-bar:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.lang-bar {
|
||||
width: 100%;
|
||||
display: inherit;
|
||||
height: 10px;
|
||||
border-radius:4px;
|
||||
overflow: hidden; /* To force the corners to be round. */
|
||||
user-select: none; /* Prevent the selection */
|
||||
-webkit-user-select: none; /* Safari specific fix. */
|
||||
}
|
||||
|
||||
/* URI: /:user/:repo */
|
||||
.button {
|
||||
background-color: var(--background);
|
||||
@@ -266,6 +303,23 @@ a:hover {
|
||||
background-color: var(--secondary-background);
|
||||
}
|
||||
|
||||
/* Overwrite */
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
display: grid;
|
||||
flex: none;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--text: #000;
|
||||
@@ -293,6 +347,16 @@ a:hover {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: none;
|
||||
display: inline-grid;
|
||||
}
|
||||
|
||||
.navbar-slogan {
|
||||
display: none;
|
||||
}
|
||||
@@ -312,5 +376,5 @@ a:hover {
|
||||
}
|
||||
}
|
||||
.cl {
|
||||
padding-left: 8px
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
+97
-15
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
// For debugging purposes
|
||||
// "github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/gofiber/template/html"
|
||||
|
||||
@@ -62,6 +64,10 @@ func Serve(port string) {
|
||||
app.Use(cache.New(cache.Config{
|
||||
Expiration: 5 * time.Minute,
|
||||
}))
|
||||
// For debugging purposes
|
||||
// app.Use(logger.New(logger.Config{
|
||||
// Format: "[${ip}]:${port} ${status} - ${method} ${path} ${queryParams}\n",
|
||||
// }))
|
||||
|
||||
app.Use(compress.New(compress.Config{
|
||||
Level: compress.LevelBestSpeed, // 1
|
||||
@@ -86,13 +92,21 @@ func Serve(port string) {
|
||||
MaxAge: 31536000,
|
||||
}
|
||||
|
||||
proxying, ok := os.LookupEnv("GOTHUB_PROXYING_ENABLED")
|
||||
if !ok {
|
||||
proxying = "true"
|
||||
}
|
||||
// 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';")
|
||||
if proxying == "true" {
|
||||
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';")
|
||||
} else {
|
||||
c.Set("Content-Security-Policy", "default-src 'self' https://camo.githubusercontent.com https://avatars.githubusercontent.com https://user-images.githubusercontent.com https://github.com https://raw.githubusercontent.com https://gist.github.com; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://user-images.githubusercontent.com https://camo.githubusercontent.com https://avatars.githubusercontent.com https://gist.github.com; font-src 'self'; connect-src 'self' https://user-images.githubusercontent.com https://camo.githubusercontent.com https://avatars.githubusercontent.com https://github.com https://raw.githubusercontent.com https://gist.github.com; frame-ancestors 'self'; form-action 'self'; base-uri 'self'")
|
||||
}
|
||||
c.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
|
||||
|
||||
return c.Next()
|
||||
@@ -107,30 +121,98 @@ func Serve(port string) {
|
||||
app.Get("/explore", ratelimiter, pages.HandleExplore)
|
||||
app.Get("/:user", pages.HandleUser)
|
||||
app.Get("/avatar/:id", func(c *fiber.Ctx) error {
|
||||
utils.ProxyRequest(c, "https://avatars.githubusercontent.com/u/"+c.Params("id")+"?v=4")
|
||||
return nil
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://avatars.githubusercontent.com/u/"+c.Params("id")+"?v=4")
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://avatars.githubusercontent.com/u/" + c.Params("id") + "?v=4")
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/user-images/:id/:img", func(c *fiber.Ctx) error {
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://user-images.githubusercontent.com/"+c.Params("id")+"/"+c.Params("img")+"?v=4")
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://user-images.githubusercontent.com/" + c.Params("id") + "/" + c.Params("img") + "?v=4")
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/:user/:repo", pages.HandleRepo)
|
||||
app.Get("/:user/:repo/commit/:commit", pages.HandleCommit)
|
||||
app.Get("/diff/:user/:repo/commit/:commit", func(c *fiber.Ctx) error {
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+"/commit/"+c.Params("commit")+".diff")
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/commit/" + c.Params("commit") + ".diff")
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/:user/:repo/info/+", func(c *fiber.Ctx) error {
|
||||
// This is needed but it looks like git doesn't send it. Or maybe i just shouldn't program at midnight
|
||||
param := "?service=git-upload-pack"
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+".git/info/"+c.Params("+")+param)
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + ".git/info/" + c.Params("+") + param)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Post("/:user/:repo/git-upload-pack", func(c *fiber.Ctx) error {
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+".git/git-upload-pack")
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + ".git/git-upload-pack")
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/:user/:repo/blob/:branch/+", pages.FileView)
|
||||
app.Get("/:user/:repo/tree/:branch/+", pages.DirView)
|
||||
app.Get("/:user/:repo/tree/:branch", pages.HandleRepo)
|
||||
app.Get("/download/:user/:repo/:branch", func(c *fiber.Ctx) error {
|
||||
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+"/archive/"+c.Params("branch")+".zip")
|
||||
app.Get("/:user/:repo/wiki", func(c *fiber.Ctx) error {
|
||||
c.Redirect("/" + c.Params("user") + "/" + c.Params("repo") + "/wiki/Home")
|
||||
return nil
|
||||
})
|
||||
app.Get("/:user/:repo/wiki/:page", pages.WikiView)
|
||||
app.Get("/:user/:repo/tree/:branch", pages.HandleRepo)
|
||||
app.Get("/:user/:repo/archive/:branch", func(c *fiber.Ctx) error {
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://github.com/"+c.Params("user")+"/"+c.Params("repo")+"/archive/"+c.Params("branch"))
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://github.com/" + c.Params("user") + "/" + c.Params("repo") + "/archive/" + c.Params("branch"))
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/raw/:user/:repo/:branch/+", func(c *fiber.Ctx) error {
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://raw.githubusercontent.com/"+c.Params("user")+"/"+c.Params("repo")+"/"+c.Params("branch")+"/"+c.Params("+"))
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("repo") + "/" + c.Params("branch") + "/" + c.Params("+"))
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/camo/:p1/:p2", func(c *fiber.Ctx) error {
|
||||
utils.ProxyRequest(c, "https://camo.githubusercontent.com/"+c.Params("p1")+"/"+c.Params("p2"))
|
||||
return nil
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://camo.githubusercontent.com/"+c.Params("p1")+"/"+c.Params("p2"))
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://camo.githubusercontent.com/" + c.Params("p1") + "/" + c.Params("p2"))
|
||||
return nil
|
||||
}
|
||||
})
|
||||
app.Get("/gist/:user/:gistID", pages.HandleGist)
|
||||
app.Get("/download/gist/:user/:gistID/:revision", func(c *fiber.Ctx) error {
|
||||
utils.ProxyRequest(c, "https://gist.github.com/"+c.Params("user")+"/"+c.Params("gistID")+"/archive/"+c.Params("revision")+".zip")
|
||||
return nil
|
||||
})
|
||||
|
||||
app.Get("/raw/:user/:repo/:branch/:file", func(c *fiber.Ctx) error {
|
||||
utils.ProxyRequest(c, "https://raw.githubusercontent.com/"+c.Params("user")+"/"+c.Params("repo")+"/"+c.Params("branch")+"/"+c.Params("file"))
|
||||
return nil
|
||||
if proxying == "true" {
|
||||
utils.ProxyRequest(c, "https://gist.github.com/"+c.Params("user")+"/"+c.Params("gistID")+"/archive/"+c.Params("revision")+".zip")
|
||||
return nil
|
||||
} else {
|
||||
c.Redirect("https://gist.github.com/" + c.Params("user") + "/" + c.Params("gistID") + "/archive/" + c.Params("revision") + ".zip")
|
||||
return nil
|
||||
}
|
||||
})
|
||||
|
||||
api := app.Group("/api")
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</p>
|
||||
<p>If this page isn't filled, please contact your instance's maintainer.</p>
|
||||
<ul>
|
||||
<li><b>Proxying enabled:</b> {{.ProxyEnabled}}</li>
|
||||
<li><b>IP Address logged:</b> {{.IPAddr}}</li>
|
||||
<li><b>Request URL logged:</b> {{.ReqURL}}</li>
|
||||
<li><b>User Agent logged:</b> {{.UserAgent}}</li>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{{ template "header" .}}
|
||||
|
||||
<main>
|
||||
{{ if.commit }}
|
||||
<style>
|
||||
// prettier-ignore
|
||||
{{.css}}
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
{{ range $key, $value := .commit}}
|
||||
<div class="download-parent">
|
||||
<a href="/{{.FullName}}" class="button">Back to {{.FullName}}</a>
|
||||
</div>
|
||||
|
||||
<div class="user-readme">
|
||||
<div class="user-container">
|
||||
<h3>{{.CommitHashShortened}} | {{.CommitName}}</h3>
|
||||
<a
|
||||
href="/diff/{{.FullName}}/commit/{{.CommitHashShortened}}"
|
||||
class="button secondary"
|
||||
>Raw</a
|
||||
>
|
||||
</div>
|
||||
{{.CommitName}} {{ if .CommitDesc }}
|
||||
<pre>{{.CommitDesc}}</pre>
|
||||
{{end}}
|
||||
<p>
|
||||
Commited on {{.CommitDate}} by {{range .Commiter}}
|
||||
<a href="/{{.}}" target="_blank">@{{.}}</a>{{ end }}
|
||||
</p>
|
||||
<p>
|
||||
Previous Commit:
|
||||
<a href="/{{.FullName}}/commit/{{.PreviousCommit}}"
|
||||
>{{.PreviousCommit}}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
{{end}} {{ .file}} {{ else }}
|
||||
<h2>Commit not found</h2>
|
||||
<p>That commit doesn't exist.</p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ template "footer" .}}
|
||||
+57
-51
@@ -1,57 +1,63 @@
|
||||
{{ template "header" .}}
|
||||
|
||||
<main>
|
||||
{{ if .dir }} {{ range $key, $value := .dir}}
|
||||
<div class="button-parent">
|
||||
<a
|
||||
rel="noreferrer"
|
||||
href="https://github.com/{{.Fullname}}/tree/{{.Branch}}/{{.DirName}}"
|
||||
class="button"
|
||||
>View on GitHub</a
|
||||
>
|
||||
<a href="/{{.Fullname}}/tree/{{.Branch}}/{{.Branch}}" class="button"
|
||||
>Back to {{.Fullname}}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<h1>{{.Fullname}} | {{.DirName}}</h1>
|
||||
</div>
|
||||
{{end}} {{ if .files}}
|
||||
<div class="user-readme">
|
||||
<h3>Files</h3>
|
||||
<div class="user-readme-text">
|
||||
<ul class="file-u-list">
|
||||
{{ range $key, $value := .files}} {{ if eq .Type "dir" }}
|
||||
<li class="file-list">
|
||||
📁
|
||||
<a
|
||||
href="/{{.Fullname}}/tree/{{.Branch}}/{{.DirName}}/{{.Path}}"
|
||||
class="file-a"
|
||||
>{{.Path}}</a
|
||||
>
|
||||
</li>
|
||||
{{ else }}
|
||||
<li class="file-list">
|
||||
🗒️
|
||||
<a href="/{{.Fullname}}/blob/{{.Branch}}/{{.DirName}}/{{.Path}}"
|
||||
>{{.Path}}</a
|
||||
>
|
||||
</li>
|
||||
{{ end }} {{ end }}
|
||||
</ul>
|
||||
{{ if .dir }} {{ range $key, $value := .dir}}
|
||||
<div class="button-parent">
|
||||
<a
|
||||
rel="noreferrer"
|
||||
href="https://github.com/{{.Fullname}}/tree/{{.Branch}}/{{.DirName}}"
|
||||
class="button"
|
||||
>View on GitHub</a
|
||||
>
|
||||
<a href="/{{.Fullname}}/tree/{{.Branch}}" class="button"
|
||||
>Back to {{.Fullname}}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }} {{ if .readme}}
|
||||
<div class="user-readme">
|
||||
{{ if .dir }} {{ range $key, $value := .dir }}
|
||||
<h3>{{.Readme}}</h3>
|
||||
{{end}} {{end}}
|
||||
<div class="user-readme-text">{{ unescape .readme}}</div>
|
||||
</div>
|
||||
{{ end }} {{ else }}
|
||||
<h2>Directory not found</h2>
|
||||
<p>That directory doesn't exist.</p>
|
||||
{{ end }}
|
||||
|
||||
<div class="user-profile">
|
||||
<h1>{{.Fullname}} | {{.DirName}}</h1>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{ if .TruncationWarning }}
|
||||
<blockquote>{{.TruncationWarning}}</blockquote>
|
||||
{{ end }}
|
||||
|
||||
{{ if .files}}
|
||||
<div class="user-readme">
|
||||
<h3>Files</h3>
|
||||
<div class="user-readme-text">
|
||||
<ul class="file-u-list">
|
||||
{{ range $key, $value := .files}} {{ if eq .Type "dir" }}
|
||||
<li class="file-list">
|
||||
📁
|
||||
<a
|
||||
href="/{{.Fullname}}/tree/{{.Branch}}/{{.Path}}"
|
||||
class="file-a"
|
||||
>{{.Name}}</a
|
||||
>
|
||||
</li>
|
||||
{{ else }}
|
||||
<li class="file-list">
|
||||
🗒️
|
||||
<a href="/{{.Fullname}}/blob/{{.Branch}}/{{.Path}}"
|
||||
>{{.Name}}</a
|
||||
>
|
||||
</li>
|
||||
{{ end }} {{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }} {{ if .readme}}
|
||||
<div class="user-readme">
|
||||
{{ if .dir }} {{ range $key, $value := .dir }}
|
||||
<h3>{{.Readme}}</h3>
|
||||
{{end}} {{end}}
|
||||
<div class="user-readme-text">{{ unescape .readme}}</div>
|
||||
</div>
|
||||
{{ end }} {{ else }}
|
||||
<h2>Directory not found</h2>
|
||||
<p>That directory doesn't exist.</p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ template "footer" .}}
|
||||
|
||||
+6
-1
@@ -9,6 +9,11 @@
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
div.user-readme img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
<div class="download-parent">
|
||||
<a href="/{{.fullname}}/tree/{{.fileBranch}}" class="button"
|
||||
@@ -25,7 +30,7 @@
|
||||
>Raw</a
|
||||
>
|
||||
</div>
|
||||
{{ .file}}
|
||||
{{ if eq .type "img" }} {{ unescape .file }} {{else}} {{ .file }} {{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</main>
|
||||
|
||||
@@ -60,6 +60,14 @@
|
||||
using a VPN if you have to clone a GitHub repository and want to keep
|
||||
yourself private from them instead.</i
|
||||
>
|
||||
<h3>Credits</h3>
|
||||
<p style="margin: 0; padding: 0">
|
||||
<a href="https://midou.dev">Midou36O</a> - designing the GotHub logo
|
||||
</p>
|
||||
<p style="margin: 0; padding: 0">
|
||||
<a href="https://arya.projectsegfau.lt">Arya</a> - code contributions and
|
||||
maintainer
|
||||
</p>
|
||||
<h3>Info</h3>
|
||||
{{ if eq .version "unknown, please build with Go 1.13+ or use Git"}} GotHub
|
||||
version: <code>unknown</code> {{ else }} GotHub version:
|
||||
|
||||
+18
-5
@@ -3,7 +3,7 @@
|
||||
<main>
|
||||
{{ if .repo }} {{ range $key, $value := .repo}}
|
||||
<div class="button-parent">
|
||||
<a href="/download/{{.Fullname}}/{{.DefaultBranch}}" class="button"
|
||||
<a href="/{{.Fullname}}/archive/{{.DefaultBranch}}.zip" class="button"
|
||||
>Download (zip)</a
|
||||
>
|
||||
<a rel="noreferrer" href="https://github.com/{{.Fullname}}" class="button"
|
||||
@@ -30,10 +30,23 @@
|
||||
⭐ {{.Stars}} 🍴 {{.Forks}} 👀 {{.Watchers}} ⚖️ No license 🌿
|
||||
{{.DefaultBranch}}
|
||||
</p>
|
||||
{{ end }} {{ if .Language }}
|
||||
<p>🗒️ {{range .Language}} {{.}} {{end}}</p>
|
||||
{{end}} {{ if .CommitsBehind }} This repository is {{.CommitsBehind}}
|
||||
commits behind its parent. {{end}}
|
||||
<p><a href="/{{.Fullname}}/wiki">Wiki</a></p>
|
||||
{{ end }} {{ if .Language }} {{range $o := .Language}}{{$o.Name}}
|
||||
{{$o.Percent}}, {{end}}
|
||||
<div class="lang-bar">
|
||||
{{range $i, $o := .Language}}
|
||||
<span style="width: {{$o.Percent}}; background-color: {{$o.Color}}">
|
||||
|
||||
</span>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}} {{ if or .CommitsAhead .CommitsBehind }}
|
||||
<p>
|
||||
This repository is {{if .CommitsBehind}} {{.CommitsBehind}} commits behind
|
||||
{{end}} {{if and .CommitsBehind .CommitsAhead}} and {{end}} {{if
|
||||
.CommitsAhead}} {{ .CommitsAhead }} commits ahead of {{end}} its parent.
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}} {{ if .files}}
|
||||
<div class="user-readme">
|
||||
|
||||
+31
-20
@@ -74,27 +74,38 @@
|
||||
{{ end }} {{ if .MainRepos }}
|
||||
<div class="user-readme">
|
||||
<h3>{{.PinOrPopular}} repositories</h3>
|
||||
{{range .MainRepos}}
|
||||
<a class="user-repo-card" href="{{.Link}}">
|
||||
<p class="user-repo-header">{{.Name}} ({{.Type}})</p>
|
||||
{{if .ForkOf}} Forked from: <a href="/{{.ForkOf}}">{{.ForkOf}}</a>
|
||||
{{else}} {{end}} {{if .Desc}} {{if eq .Type "Gist"}}
|
||||
<style>
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
<pre> {{.Desc}} </pre>
|
||||
{{range .MainRepos}} {{ if .ForkOf }}
|
||||
<div class="user-repo-card">
|
||||
{{else}}
|
||||
<p class="">{{.Desc}}</p>
|
||||
{{end}} {{else}} {{end}}
|
||||
<p class="">
|
||||
{{if .Stars}}⭐ {{.Stars}}{{else}}{{end}} {{if .Forks}} 🍴 {{.Forks}}
|
||||
{{else}}{{end}} {{if .Lang}} 🗒️ {{.Lang}} {{else}}{{end}}
|
||||
</p>
|
||||
</a>
|
||||
{{ end }}
|
||||
<a class="user-repo-card" href="{{.Link}}">
|
||||
{{end}} {{if .ForkOf}}
|
||||
<p class="user-repo-header">
|
||||
<a href="{{.Link}}">{{.Name}} ({{.Type}})</a>
|
||||
</p>
|
||||
{{else}}
|
||||
<p class="user-repo-header">{{.Name}} ({{.Type}})</p>
|
||||
{{end}} {{if .ForkOf}}
|
||||
<p class="">Forked from: <a href="/{{.ForkOf}}">{{.ForkOf}}</a></p>
|
||||
{{else}} {{end}} {{if .Desc}} {{if eq .Type "Gist"}}
|
||||
<style>
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
<pre> {{.Desc}} </pre>
|
||||
{{else}}
|
||||
<p class="">{{.Desc}}</p>
|
||||
{{end}} {{else}} {{end}}
|
||||
<p class="">
|
||||
{{if .Stars}}⭐ {{.Stars}}{{else}}{{end}} {{if .Forks}} 🍴 {{.Forks}}
|
||||
{{else}}{{end}} {{if .Lang}} 🗒️ {{.Lang}} {{else}}{{end}}
|
||||
</p>
|
||||
{{ if .ForkOf }} {{else}}
|
||||
</a>
|
||||
{{end}} {{ if .ForkOf }}
|
||||
</div>
|
||||
{{end}} {{ end }}
|
||||
</div>
|
||||
{{ end }} {{ end }} {{ else }}
|
||||
<h2>User not found</h2>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{{ template "header" .}}
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<main class="margin-ow">
|
||||
{{ if .wiki }} {{ range $key, $value := .wiki}}
|
||||
<div id="wrap">
|
||||
<div>
|
||||
<h1>{{.Title}}</h1>
|
||||
<h2>{{.RepoUser}}/{{.RepoName}}</h2>
|
||||
<p>
|
||||
{{.LastEditor}} edited this page on {{.LastEdit}} · {{.RevisionNum}} revisions
|
||||
</p>
|
||||
<hr style="border-color: #252525;">
|
||||
<div style="margin-right: auto; width: 50vw;">
|
||||
{{ unescape .PageContent }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-readme float-right">
|
||||
<h3><b>{{.PageNum}}</b> pages</h3>
|
||||
<div class="no-padding user-readme-text">
|
||||
{{ range $key, $value := .Sidebar }}
|
||||
<hr style="border-color: #252525;">
|
||||
<a href="{{.Link}}"><p style="padding:0px 8px 0px 8px;">{{.Name}}</p></a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<h2>Wiki not found</h2>
|
||||
<p>That wiki doesn't exist.</p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ template "footer" .}}
|
||||
Reference in New Issue
Block a user