Commit ec2ae93416
ec2ae93416df10c3876972f8b9d494ba932dac80
parent: aaf2234b85
Verified · cmc ci/build: success ci/test: success
cmc <hello@cleberg.net> · 2026-10-02 15:15 UTC
web: footer links to the operator's abuse and terms pages
[web] abuse_url and terms_url, empty by default, add "Report abuse"
and "Terms" to every page's footer. Wiki pages Abuse and Terms are
gitbay.org's.
Closes #323
Closes #324
Layout: unified · split
.gitbay/wiki/Abuse.org
added
+21
| @@ -0,0 +1,21 @@ |
| 1 | #+title: Reporting abuse |
| 2 | |
| 3 | This page is for gitbay.org. Another instance links its own page from |
| 4 | its footer, or none. |
| 5 | |
| 6 | Mail =abuse@gitbay.org= with: |
| 7 | |
| 8 | - the URL of the repository, issue, merge request, comment or snippet; |
| 9 | - what is wrong with it: malware, spam, phishing, a copyright or |
| 10 | licence claim, illegal content, harassment, or anything else; |
| 11 | - if you are the rights holder or the person affected, say so. |
| 12 | |
| 13 | You do not need an account. One person runs the instance and reads the |
| 14 | mail. Depending on the report, the content is removed, the repository |
| 15 | is archived or made private, or the account is disabled |
| 16 | (=admin user disable=, which refuses SSH, web and API access without |
| 17 | deleting anything). The account holder is told what was done and why, |
| 18 | unless that would help them continue. |
| 19 | |
| 20 | A security problem in gitbay itself can go to the same address; do not |
| 21 | file it as a public issue. |
.gitbay/wiki/Admin.org
+3
| @@ -115,6 +115,9 @@ build page's live log arrives only when the build ends; |
| 115 | 115 | gitbay itself. |
| 116 | 116 | - =privacy_notice= — operator text shown on =/privacy= under the fixed |
| 117 | 117 | statement. |
| 118 | - =abuse_url=, =terms_url= (empty) — linked from every page's footer as |
| 119 | "Report abuse" and "Terms"; no link when empty. gitbay.org points them |
| 120 | at the [[Abuse][Abuse]] and [[Terms][Terms]] wiki pages. |
| 118 | 121 | - =apple_app_ids= (empty) — iOS app IDs (=TEAMID.bundle.id=) allowed to |
| 119 | 122 | open this instance's links, served at |
| 120 | 123 | =/.well-known/apple-app-site-association=. Empty leaves the path a |
.gitbay/wiki/Terms.org
added
+51
| @@ -0,0 +1,51 @@ |
| 1 | #+title: Terms |
| 2 | |
| 3 | These are the terms for gitbay.org, kept informal on purpose. Another |
| 4 | instance sets its own. |
| 5 | |
| 6 | * What this is |
| 7 | |
| 8 | One person runs gitbay.org on one server in Nuremberg, Germany. It is |
| 9 | free, comes with no warranty, and makes no uptime promise. Repositories |
| 10 | are backed up nightly off the host, but keep your own clone of anything |
| 11 | that matters. |
| 12 | |
| 13 | * What is not allowed |
| 14 | |
| 15 | - malware, phishing kits, or anything built to attack other systems |
| 16 | (security research and proof-of-concept code with a clear purpose are |
| 17 | fine); |
| 18 | - spam, including accounts or repositories that exist to carry links; |
| 19 | - content that is illegal in Germany, or that infringes someone's |
| 20 | copyright; |
| 21 | - harassment of other users; |
| 22 | - using CI, storage or bandwidth as general-purpose hosting: crypto |
| 23 | mining, file dumps, or mirrors of large binaries unrelated to a |
| 24 | project. |
| 25 | |
| 26 | * Limits |
| 27 | |
| 28 | Each account may own 250 repositories and 5 GB. An organization has |
| 29 | the same caps, and an account may create 5 organizations. Ask if you |
| 30 | need more. |
| 31 | |
| 32 | * What the operator may do |
| 33 | |
| 34 | Remove content, archive a repository or make it private, or disable an |
| 35 | account, when it breaks the rules above or puts the instance at risk. |
| 36 | Reports go to the address on the [[Abuse][abuse page]]. |
| 37 | |
| 38 | * Leaving |
| 39 | |
| 40 | =account export= writes your profile, repositories, issues and merge |
| 41 | requests as a bundle another gitbay instance can replay |
| 42 | (=gitbay migrate=), and every repository can be cloned. To have |
| 43 | the account deleted, mail the abuse address from the account's primary |
| 44 | email. |
| 45 | |
| 46 | If gitbay.org ever shuts down, every account with a verified address |
| 47 | gets notice and time to export first. |
| 48 | |
| 49 | * Privacy |
| 50 | |
| 51 | See https://gitbay.org/privacy. |
internal/config/config.go
+4
| @@ -130,6 +130,10 @@ type Web struct { |
| 130 | 130 | // PrivacyNotice is operator-provided text shown on /privacy under the |
| 131 | 131 | // fixed project-level statement. Plain text; blank paragraphs split. |
| 132 | 132 | PrivacyNotice string `toml:"privacy_notice"` |
| 133 | // AbuseURL and TermsURL, when set, are linked from every page's |
| 134 | // footer: where to report abuse, and the instance's terms of use. |
| 135 | AbuseURL string `toml:"abuse_url"` |
| 136 | TermsURL string `toml:"terms_url"` |
| 133 | 137 | // AppleAppIDs are the iOS apps (TEAMID.bundle.id) that may open this |
| 134 | 138 | // instance's links, served in /.well-known/apple-app-site-association. |
| 135 | 139 | // Empty leaves the route unregistered. |
internal/httpd/accounts.go
+3 −3
| @@ -90,7 +90,7 @@ func (s *Server) renderLogin(w http.ResponseWriter, errMsg string, sent bool, ne |
| 90 | 90 | EmailLogin bool |
| 91 | 91 | Sent bool |
| 92 | 92 | Next string |
| 93 | | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, |
| 93 | }{s.anonBase(), |
| 94 | 94 | s.cfg.Registration.Mode, errMsg, s.emailLoginEnabled(), sent, next}) |
| 95 | 95 | } |
| 96 | 96 | |
| @@ -399,7 +399,7 @@ func (s *Server) renderSignup(w http.ResponseWriter, errMsg, username string) { |
| 399 | 399 | Mode string // open | invite |
| 400 | 400 | Error string |
| 401 | 401 | Username string |
| 402 | | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, s.cfg.SiteHost(), s.cfg.Registration.Mode, errMsg, username}) |
| 402 | }{s.anonBase(), s.cfg.SiteHost(), s.cfg.Registration.Mode, errMsg, username}) |
| 403 | 403 | } |
| 404 | 404 | |
| 405 | 405 | func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { |
| @@ -421,7 +421,7 @@ func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { |
| 421 | 421 | Username string |
| 422 | 422 | Message string |
| 423 | 423 | Host string |
| 424 | | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, username, msg, s.cfg.SiteHost()}) |
| 424 | }{s.anonBase(), username, msg, s.cfg.SiteHost()}) |
| 425 | 425 | } |
| 426 | 426 | |
| 427 | 427 | // issueNewPage is what the new-issue form renders with, whether that is a |
internal/httpd/footer_test.go
added
+26
| @@ -0,0 +1,26 @@ |
| 1 | package httpd |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // The footer links the operator's terms and abuse pages only when |
| 9 | // configured, on a page with no viewer and on the 404 page. |
| 10 | func TestFooterOperatorLinks(t *testing.T) { |
| 11 | s, _, _ := newTokenTestServer(t) |
| 12 | for _, path := range []string{"/privacy", "/no-such-owner"} { |
| 13 | if body := get(t, s.Handler(), path, nil).Body.String(); strings.Contains(body, "Report abuse") || strings.Contains(body, ">Terms<") { |
| 14 | t.Errorf("%s: footer links with nothing configured", path) |
| 15 | } |
| 16 | } |
| 17 | s.cfg.Web.AbuseURL = "https://example.test/wiki/Abuse" |
| 18 | s.cfg.Web.TermsURL = "https://example.test/wiki/Terms" |
| 19 | for _, path := range []string{"/privacy", "/no-such-owner"} { |
| 20 | body := get(t, s.Handler(), path, nil).Body.String() |
| 21 | if !strings.Contains(body, `<a href="https://example.test/wiki/Abuse">Report abuse</a>`) || |
| 22 | !strings.Contains(body, `<a href="https://example.test/wiki/Terms">Terms</a>`) { |
| 23 | t.Errorf("%s: footer lacks the configured links", path) |
| 24 | } |
| 25 | } |
| 26 | } |
internal/httpd/page.go
+11 −2
| @@ -26,13 +26,22 @@ type basePage struct { |
| 26 | 26 | // Theme is stamped on <html> as data-theme: light or dark when the |
| 27 | 27 | // viewer chose one, empty when the browser's own scheme decides. |
| 28 | 28 | Theme string |
| 29 | // AbuseURL and TermsURL are the operator's footer links, empty when |
| 30 | // not configured. |
| 31 | AbuseURL string |
| 32 | TermsURL string |
| 33 | } |
| 34 | |
| 35 | // anonBase is the layout-wide data with no viewer. |
| 36 | func (s *Server) anonBase() basePage { |
| 37 | return basePage{Site: s.siteName(), Host: s.cfg.SiteHost(), AbuseURL: s.cfg.Web.AbuseURL, TermsURL: s.cfg.Web.TermsURL} |
| 29 | 38 | } |
| 30 | 39 | |
| 31 | 40 | // base builds the layout-wide data for a request that has not already |
| 32 | 41 | // resolved a viewer. |
| 33 | 42 | func (s *Server) base(r *http.Request) basePage { |
| 34 | 43 | if s.cfg.Web.Mode != "accounts" { |
| 35 | | return basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
| 44 | return s.anonBase() |
| 36 | 45 | } |
| 37 | 46 | return s.baseFor(s.viewer(r)) |
| 38 | 47 | } |
| @@ -40,7 +49,7 @@ func (s *Server) base(r *http.Request) basePage { |
| 40 | 49 | // baseFor is base for a handler that already holds the viewer, so the |
| 41 | 50 | // session lookup is not repeated. |
| 42 | 51 | func (s *Server) baseFor(viewer store.User) basePage { |
| 43 | | b := basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
| 52 | b := s.anonBase() |
| 44 | 53 | if viewer.ID == 0 { |
| 45 | 54 | return b |
| 46 | 55 | } |
internal/httpd/web.go
+1 −1
| @@ -198,7 +198,7 @@ func (s *Server) index(w http.ResponseWriter, r *http.Request) { |
| 198 | 198 | Accounts bool |
| 199 | 199 | Signup bool |
| 200 | 200 | EmailLogin bool |
| 201 | | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, host, s.cfg.Web.Mode == "accounts", |
| 201 | }{s.anonBase(), host, s.cfg.Web.Mode == "accounts", |
| 202 | 202 | s.cfg.Web.Mode == "accounts" && s.cfg.Registration.Mode != "closed", |
| 203 | 203 | s.emailLoginEnabled()}) |
| 204 | 204 | } |
internal/web/templates/layout.html
+1 −1
| @@ -88,7 +88,7 @@ |
| 88 | 88 | </main> |
| 89 | 89 | |
| 90 | 90 | <footer> |
| 91 | | <p>Powered by <a href="https://gitbay.org/krz/gitbay">gitbay</a>{{with gitbayVersion}} · <code><a href="https://gitbay.org/krz/gitbay/commit/{{gitbayCommit}}">{{.}}</a></code>{{end}} · <a href="https://apps.apple.com/us/app/gitbay/id6806399095">iOS app</a> · <a href="/privacy">Privacy</a></p> |
| 91 | <p>Powered by <a href="https://gitbay.org/krz/gitbay">gitbay</a>{{with gitbayVersion}} · <code><a href="https://gitbay.org/krz/gitbay/commit/{{gitbayCommit}}">{{.}}</a></code>{{end}} · <a href="https://apps.apple.com/us/app/gitbay/id6806399095">iOS app</a> · <a href="/privacy">Privacy</a>{{with field . "TermsURL"}} · <a href="{{.}}">Terms</a>{{end}}{{with field . "AbuseURL"}} · <a href="{{.}}">Report abuse</a>{{end}}</p> |
| 92 | 92 | </footer> |
| 93 | 93 | </div> |
| 94 | 94 | </div> |