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 | gitbay itself. |
115 | gitbay itself. |
| 116 | - =privacy_notice= — operator text shown on =/privacy= under the fixed |
116 | - =privacy_notice= — operator text shown on =/privacy= under the fixed |
| 117 | statement. |
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 | - =apple_app_ids= (empty) — iOS app IDs (=TEAMID.bundle.id=) allowed to |
121 | - =apple_app_ids= (empty) — iOS app IDs (=TEAMID.bundle.id=) allowed to |
| 119 | open this instance's links, served at |
122 | open this instance's links, served at |
| 120 | =/.well-known/apple-app-site-association=. Empty leaves the path a |
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 | // PrivacyNotice is operator-provided text shown on /privacy under the |
130 | // PrivacyNotice is operator-provided text shown on /privacy under the |
| 131 | // fixed project-level statement. Plain text; blank paragraphs split. |
131 | // fixed project-level statement. Plain text; blank paragraphs split. |
| 132 | PrivacyNotice string `toml:"privacy_notice"` |
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 | // AppleAppIDs are the iOS apps (TEAMID.bundle.id) that may open this |
137 | // AppleAppIDs are the iOS apps (TEAMID.bundle.id) that may open this |
| 134 | // instance's links, served in /.well-known/apple-app-site-association. |
138 | // instance's links, served in /.well-known/apple-app-site-association. |
| 135 | // Empty leaves the route unregistered. |
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 | EmailLogin bool |
90 | EmailLogin bool |
| 91 | Sent bool |
91 | Sent bool |
| 92 | Next string |
92 | Next string |
| 93 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, |
93 | }{s.anonBase(), |
| 94 | s.cfg.Registration.Mode, errMsg, s.emailLoginEnabled(), sent, next}) |
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 | Mode string // open | invite |
399 | Mode string // open | invite |
| 400 | Error string |
400 | Error string |
| 401 | Username string |
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 | func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { |
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 | Username string |
421 | Username string |
| 422 | Message string |
422 | Message string |
| 423 | Host string |
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 | // issueNewPage is what the new-issue form renders with, whether that is a |
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 | // Theme is stamped on <html> as data-theme: light or dark when the |
26 | // Theme is stamped on <html> as data-theme: light or dark when the |
| 27 | // viewer chose one, empty when the browser's own scheme decides. |
27 | // viewer chose one, empty when the browser's own scheme decides. |
| 28 | Theme string |
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 | // base builds the layout-wide data for a request that has not already |
40 | // base builds the layout-wide data for a request that has not already |
| 32 | // resolved a viewer. |
41 | // resolved a viewer. |
| 33 | func (s *Server) base(r *http.Request) basePage { |
42 | func (s *Server) base(r *http.Request) basePage { |
| 34 | if s.cfg.Web.Mode != "accounts" { |
43 | if s.cfg.Web.Mode != "accounts" { |
| 35 | return basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
44 | return s.anonBase() |
| 36 | } |
45 | } |
| 37 | return s.baseFor(s.viewer(r)) |
46 | return s.baseFor(s.viewer(r)) |
| 38 | } |
47 | } |
| @@ -40,7 +49,7 @@ func (s *Server) base(r *http.Request) basePage { |
| 40 | // baseFor is base for a handler that already holds the viewer, so the |
49 | // baseFor is base for a handler that already holds the viewer, so the |
| 41 | // session lookup is not repeated. |
50 | // session lookup is not repeated. |
| 42 | func (s *Server) baseFor(viewer store.User) basePage { |
51 | func (s *Server) baseFor(viewer store.User) basePage { |
| 43 | b := basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
52 | b := s.anonBase() |
| 44 | if viewer.ID == 0 { |
53 | if viewer.ID == 0 { |
| 45 | return b |
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 | Accounts bool |
198 | Accounts bool |
| 199 | Signup bool |
199 | Signup bool |
| 200 | EmailLogin bool |
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 | s.cfg.Web.Mode == "accounts" && s.cfg.Registration.Mode != "closed", |
202 | s.cfg.Web.Mode == "accounts" && s.cfg.Registration.Mode != "closed", |
| 203 | s.emailLoginEnabled()}) |
203 | s.emailLoginEnabled()}) |
| 204 | } |
204 | } |
internal/web/templates/layout.html
+1 −1
| @@ -88,7 +88,7 @@ |
| 88 | </main> |
88 | </main> |
| 89 | |
89 | |
| 90 | <footer> |
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 | </footer> |
92 | </footer> |
| 93 | </div> |
93 | </div> |
| 94 | </div> |
94 | </div> |