internal/httpd/aasa.go
69 lines · 2686 bytes
1package httpd
2
3import (
4 "encoding/json"
5 "net/http"
6)
7
8// aasaComponent is one applinks path pattern. iOS takes the first
9// component that matches; * matches any run of characters, slashes
10// included, and ? matches one.
11type aasaComponent struct {
12 Path string `json:"/"`
13 Exclude bool `json:"exclude,omitempty"`
14}
15
16// aasaComponents lists the pages the iOS app opens. Web-only pages come
17// first as exclusions, then the repository sections the app has screens
18// for; anything else of three or more segments stays in the browser,
19// and what is left is an owner or a repository.
20var aasaComponents = func() []aasaComponent {
21 var c []aasaComponent
22 for _, p := range []string{
23 "/.well-known/*", "/static/*", "/api/*", "/admin", "/admin/*",
24 "/settings", "/settings/*", "/login", "/logout", "/register",
25 "/new", "/explore", "/search", "/healthz", "/privacy", "/favicon.svg",
26 "/*/activity.atom", "/*/*/releases.atom", "/*/*/log.atom", "/*/*/log.atom/*",
27 "/*/-/queries", "/*/-/queries/*", "/*/-/snippets/new", "/*/-/snippets/*/raw/*",
28 "/*/*/settings", "/*/*/fork", "/*/*/search", "/*/*/symbols",
29 "/*/*/issues/new", "/*/*/mrs/new", "/*/*/mrs/*/range-diff",
30 "/*/*/edit/*", "/*/*/raw/*", "/*/*/archive/*", "/*/*/releases/download/*",
31 "/*/*/badge/*", "/*/*/wiki/_raw/*", "/*/*/compare/*", "/*/*/info/*",
32 } {
33 c = append(c, aasaComponent{Path: p, Exclude: true})
34 }
35 for _, p := range []string{
36 "/bookmarks", "/notifications",
37 "/*/-/repositories", "/*/-/bookmarks", "/*/-/people", "/*/-/labels",
38 "/*/-/milestones", "/*/-/snippets", "/*/-/snippets/*",
39 "/*/*/issues", "/*/*/issues/*", "/*/*/mrs", "/*/*/mrs/*",
40 "/*/*/builds", "/*/*/builds/*", "/*/*/tree/*", "/*/*/blob/*",
41 "/*/*/blame/*", "/*/*/commit/*", "/*/*/log", "/*/*/log/*",
42 "/*/*/refs", "/*/*/compare", "/*/*/releases", "/*/*/milestones",
43 "/*/*/labels", "/*/*/wiki", "/*/*/wiki/*",
44 } {
45 c = append(c, aasaComponent{Path: p})
46 }
47 return append(c,
48 aasaComponent{Path: "/*/*/?*", Exclude: true},
49 aasaComponent{Path: "/?*"},
50 )
51}()
52
53// appleAppSiteAssociation serves the file iOS fetches to decide which
54// links open the app ([web] apple_app_ids). Apple requires JSON at this
55// exact path with no redirect.
56func (s *Server) appleAppSiteAssociation(w http.ResponseWriter, r *http.Request) {
57 type detail struct {
58 AppIDs []string `json:"appIDs"`
59 Components []aasaComponent `json:"components"`
60 }
61 var doc struct {
62 Applinks struct {
63 Details []detail `json:"details"`
64 } `json:"applinks"`
65 }
66 doc.Applinks.Details = []detail{{AppIDs: s.cfg.Web.AppleAppIDs, Components: aasaComponents}}
67 w.Header().Set("Content-Type", "application/json")
68 json.NewEncoder(w).Encode(doc)
69}