coverage~bigbes/sr-ht-compare9720ccc2contrib/dev-stub/main.go

Coverage
0.0% 0/25 statements
Δ
Blob
59cbb18
1 // Command dev-stub is a throwaway fake git.sr.ht GraphQL API for local
2 // development of diff.sr.ht. It answers the two queries diff.sr.ht's
3 // authorizer issues (a single `user{repository}` lookup and the `me`
4 // repository list) with fixed PUBLIC data, so you can drive the UI without a
5 // real SourceHut instance or its internal-auth machinery.
6 //
7 // It ignores authentication entirely: every request — anonymous or "logged in"
8 // — sees the same public repository, which is exactly what you want when
9 // exercising the compare/commit pages against a directory of local bare repos.
10 //
11 // Usage:
12 //
13 // go run ./contrib/dev-stub -addr 127.0.0.1:5101
14 //
15 // Then point diff.sr.ht's config at it and start the daemon:
16 //
17 // [git.sr.ht]
18 // api-origin=http://127.0.0.1:5101 # dev-stub serves POST /query here
19 // repos=/path/to/local/bare/repos # {repos}/~{owner}/{name}
20 //
21 // make run-dev
22 //
23 // The repository NAME echoed back is taken from the query variables, so any
24 // /~owner/<name> you visit resolves; put a matching bare repo at
25 // {repos}/~owner/<name> for gitx to read. See README.md ("Development") for the
26 // full recipe including forging a dev login cookie.
27 //
28 // stdlib only, no build-time dependencies. Not meant for production.
29 package main
30
31 import (
32 "encoding/json"
33 "flag"
34 "log"
35 "net/http"
36 "strings"
37 )
38
39 // repoNode is the repository shape diff.sr.ht's authorizer decodes for both
40 // the single-repo lookup and the me.repositories list.
41 type repoNode struct {
42 ID int `json:"id"`
43 Name string `json:"name"`
44 Description string `json:"description"`
45 Visibility string `json:"visibility"`
46 }
47
48 type gqlRequest struct {
49 Query string `json:"query"`
50 Variables map[string]any `json:"variables"`
51 }
52
53 0 func main() {
54 0 addr := flag.String("addr", "127.0.0.1:5101", "address to listen on")
55 0 flag.Parse()
56 0
57 0 http.HandleFunc("/query", handleQuery)
58 0 http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
59 0 _, _ = w.Write([]byte("ok\n"))
60 0 })
61
62 0 log.Printf("dev-stub git.sr.ht GraphQL API listening on http://%s/query", *addr)
63 0 log.Printf("every query resolves to a fixed PUBLIC repository (auth ignored)")
64 0 if err := http.ListenAndServe(*addr, nil); err != nil {
65 0 log.Fatal(err)
66 0 }
67 }
68
69 0 func handleQuery(w http.ResponseWriter, r *http.Request) {
70 0 if r.Method != http.MethodPost {
71 0 http.Error(w, "POST only", http.StatusMethodNotAllowed)
72 0 return
73 0 }
74 0 var req gqlRequest
75 0 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
76 0 writeData(w, map[string]any{}) // let the client surface a decode-shaped miss
77 0 return
78 0 }
79
80 0 switch {
81 0 case strings.Contains(req.Query, "repositories("):
82 0 // me { repositories { results { ... } cursor } }
83 0 writeData(w, map[string]any{
84 0 "me": map[string]any{
85 0 "repositories": map[string]any{
86 0 "results": []repoNode{
87 0 {ID: 1, Name: "demo", Description: "a public demo repo", Visibility: "PUBLIC"},
88 0 {ID: 2, Name: "playground", Description: "scratch space", Visibility: "UNLISTED"},
89 0 },
90 0 "cursor": nil,
91 0 },
92 0 },
93 0 })
94 0 case strings.Contains(req.Query, "repository("):
95 0 // user(username:$u) { repository(name:$r) { ... } }
96 0 name, _ := req.Variables["r"].(string)
97 0 if name == "" {
98 0 name = "demo"
99 0 }
100 0 writeData(w, map[string]any{
101 0 "user": map[string]any{
102 0 "repository": repoNode{
103 0 ID: 1,
104 0 Name: name,
105 0 Description: "a public demo repo (dev-stub)",
106 0 Visibility: "PUBLIC",
107 0 },
108 0 },
109 0 })
110 0 default:
111 0 writeData(w, map[string]any{})
112 }
113 }
114
115 0 func writeData(w http.ResponseWriter, data map[string]any) {
116 0 w.Header().Set("Content-Type", "application/json")
117 0 _ = json.NewEncoder(w).Encode(map[string]any{"data": data})
118 0 }