+1
-7
appview/db/pulls.go
+1
-7
appview/db/pulls.go
···
83
83
return pullAt, err
84
84
}
85
85
86
-
func GetPullId(e Execer, repoAt syntax.ATURI) (int, error) {
86
+
func NextPullId(e Execer, repoAt syntax.ATURI) (int, error) {
87
87
var pullId int
88
88
err := e.QueryRow(`select next_pull_id from repo_pull_seqs where repo_at = ?`, repoAt).Scan(&pullId)
89
89
return pullId - 1, err
90
-
}
91
-
92
-
func GetPullOwnerDid(e Execer, repoAt syntax.ATURI, pullId int) (string, error) {
93
-
var ownerDid string
94
-
err := e.QueryRow(`select owner_did from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&ownerDid)
95
-
return ownerDid, err
96
90
}
97
91
98
92
func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pull, error) {
+1
appview/pages/pages.go
+1
appview/pages/pages.go
+10
-1
appview/pages/templates/repo/pulls/new.html
+10
-1
appview/pages/templates/repo/pulls/new.html
···
15
15
<p class="text-gray-500">
16
16
The branch you want to make your change against.
17
17
</p>
18
-
<input type="text" name="targetBranch" id="targetBranch" />
18
+
<select class="p-1 border border-gray-200 bg-white">
19
+
<option disabled selected>Select a branch</option>
20
+
{{ range .Branches }}
21
+
<option
22
+
value="{{ .Reference.Name }}"
23
+
class="py-1">
24
+
{{ .Reference.Name }}
25
+
</option>
26
+
{{ end }}
27
+
</select>
19
28
</div>
20
29
<div>
21
30
<label for="body">body</label>
-2
appview/pages/templates/settings.html
-2
appview/pages/templates/settings.html
···
12
12
{{ end }}
13
13
14
14
{{ define "profile" }}
15
-
<<<<<<< HEAD
16
15
<h2 class="text-sm font-bold py-2 px-6 uppercase">profile</h2>
17
16
<section class="rounded bg-white drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
18
17
<dl class="grid grid-cols-[auto_1fr] gap-x-4">
···
29
28
{{ end }}
30
29
31
30
{{ define "keys" }}
32
-
<<<<<<< HEAD
33
31
<h2 class="text-sm font-bold py-2 px-6 uppercase">ssh keys</h2>
34
32
<section class="rounded bg-white drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
35
33
<div id="key-list" class="flex flex-col gap-6 mb-8">
+173
appview/randomart/randomart.go
+173
appview/randomart/randomart.go
···
1
+
package randomart
2
+
3
+
/*
4
+
* Draw an ASCII-Art representing the fingerprint so human brain can
5
+
* profit from its built-in pattern recognition ability.
6
+
* This technique is called "random art" and can be found in some
7
+
* scientific publications like this original paper:
8
+
*
9
+
* "Hash Visualization: a New Technique to improve Real-World Security",
10
+
* Perrig A. and Song D., 1999, International Workshop on Cryptographic
11
+
* Techniques and E-Commerce (CrypTEC '99)
12
+
* sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
13
+
*
14
+
* The subject came up in a talk by Dan Kaminsky, too.
15
+
*
16
+
* If you see the picture is different, the key is different.
17
+
* If the picture looks the same, you still know nothing.
18
+
*
19
+
* The algorithm used here is a worm crawling over a discrete plane,
20
+
* leaving a trace (augmenting the field) everywhere it goes.
21
+
* Movement is taken from dgst_raw 2bit-wise. Bumping into walls
22
+
* makes the respective movement vector be ignored for this turn.
23
+
* Graphs are not unambiguous, because circles in graphs can be
24
+
* walked in either direction.
25
+
*/
26
+
27
+
import (
28
+
"os"
29
+
)
30
+
31
+
func MAX(a, b int) int {
32
+
if a > b {
33
+
return a
34
+
}
35
+
return b
36
+
}
37
+
38
+
func MIN(a, b int) int {
39
+
if a < b {
40
+
return a
41
+
}
42
+
return b
43
+
}
44
+
45
+
/*
46
+
* Field sizes for the random art. Have to be odd, so the starting point
47
+
* can be in the exact middle of the picture, and FLDBASE should be >=8 .
48
+
* Else pictures would be too dense, and drawing the frame would
49
+
* fail, too, because the key type would not fit in anymore.
50
+
*/
51
+
const (
52
+
FLDBASE = 8
53
+
FLDSIZE_Y = (FLDBASE + 1)
54
+
FLDSIZE_X = (FLDBASE*2 + 1)
55
+
)
56
+
57
+
func FromString(str string) string {
58
+
ch := make(chan byte)
59
+
60
+
go func() {
61
+
defer close(ch)
62
+
for _, v := range []byte(str) {
63
+
ch <- v
64
+
}
65
+
}()
66
+
return key_fingerprint_randomart(ch)
67
+
}
68
+
69
+
func FromFile(file *os.File) string {
70
+
ch := make(chan byte)
71
+
72
+
go func() {
73
+
defer close(ch)
74
+
// TODO make input a 1 element byte array
75
+
input := make([]byte, 1)
76
+
nread, err := file.Read(input)
77
+
for err == nil && nread > 0 {
78
+
ch <- input[0]
79
+
nread, err = file.Read(input)
80
+
}
81
+
}()
82
+
return key_fingerprint_randomart(ch)
83
+
}
84
+
85
+
func key_fingerprint_randomart(ch chan byte) string {
86
+
/*
87
+
* Chars to be used after each other every time the worm
88
+
* intersects with itself. Matter of taste.
89
+
*/
90
+
augment_string := " .o+=*BOX@%&#/^SE"
91
+
var field [FLDSIZE_X][FLDSIZE_Y]byte
92
+
len_aug := len(augment_string) - 1
93
+
var retval [(FLDSIZE_X + 3) * (FLDSIZE_Y + 2)]byte
94
+
95
+
/* initialize field */
96
+
x := FLDSIZE_X / 2
97
+
y := FLDSIZE_Y / 2
98
+
99
+
/* process raw key */
100
+
for input, ok := <-ch; ok; input, ok = <-ch {
101
+
/* each byte conveys four 2-bit move commands */
102
+
for b := 0; b < 4; b++ {
103
+
/* evaluate 2 bit, rest is shifted later */
104
+
if input&0x1 > 0 {
105
+
x += 1
106
+
} else {
107
+
x += -1
108
+
}
109
+
110
+
if input&0x2 > 0 {
111
+
y++
112
+
} else {
113
+
y--
114
+
}
115
+
116
+
/* assure we are still in bounds */
117
+
x = MAX(x, 0)
118
+
y = MAX(y, 0)
119
+
x = MIN(x, FLDSIZE_X-1)
120
+
y = MIN(y, FLDSIZE_Y-1)
121
+
122
+
/* augment the field */
123
+
if int(field[x][y]) < len_aug-2 {
124
+
field[x][y]++
125
+
}
126
+
input = input >> 2
127
+
}
128
+
}
129
+
130
+
/* mark starting point and end point*/
131
+
field[FLDSIZE_X/2][FLDSIZE_Y/2] = byte(len_aug - 1)
132
+
field[x][y] = byte(len_aug)
133
+
134
+
i := 0
135
+
retval[i] = '+'
136
+
i++
137
+
138
+
/* output upper border */
139
+
for x := 0; x < FLDSIZE_X; x++ {
140
+
retval[i] = '-'
141
+
i++
142
+
}
143
+
retval[i] = '+'
144
+
i++
145
+
retval[i] = '\n'
146
+
i++
147
+
148
+
/* output content */
149
+
for y := 0; y < FLDSIZE_Y; y++ {
150
+
retval[i] = '|'
151
+
i++
152
+
for x := 0; x < FLDSIZE_X; x++ {
153
+
retval[i] = augment_string[MIN(int(field[x][y]), len_aug)]
154
+
i++
155
+
}
156
+
retval[i] = '|'
157
+
i++
158
+
retval[i] = '\n'
159
+
i++
160
+
}
161
+
162
+
/* output lower border */
163
+
retval[i] = '+'
164
+
i++
165
+
for j := 0; j < FLDSIZE_X; j++ {
166
+
retval[i] = '-'
167
+
i++
168
+
}
169
+
retval[i] = '+'
170
+
i++
171
+
172
+
return string(retval[0:i])
173
+
}
+41
-12
appview/state/repo.go
+41
-12
appview/state/repo.go
···
35
35
log.Println("failed to fully resolve repo", err)
36
36
return
37
37
}
38
-
protocol := "http"
39
-
if !s.config.Dev {
40
-
protocol = "https"
41
-
}
42
38
43
-
var reqUrl string
44
-
if ref != "" {
45
-
reqUrl = fmt.Sprintf("%s://%s/%s/%s/tree/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref)
46
-
} else {
47
-
reqUrl = fmt.Sprintf("%s://%s/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName)
39
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
40
+
if err != nil {
41
+
log.Printf("failed to create unsigned client for %s", f.Knot)
42
+
s.pages.Error503(w)
43
+
return
48
44
}
49
45
50
-
resp, err := http.Get(reqUrl)
46
+
resp, err := us.Index(f.OwnerDid(), f.RepoName, ref)
51
47
if err != nil {
52
48
s.pages.Error503(w)
53
49
log.Println("failed to reach knotserver", err)
···
303
299
304
300
switch r.Method {
305
301
case http.MethodGet:
302
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
303
+
if err != nil {
304
+
log.Printf("failed to create unsigned client for %s", f.Knot)
305
+
s.pages.Error503(w)
306
+
return
307
+
}
308
+
309
+
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
310
+
if err != nil {
311
+
log.Println("failed to reach knotserver", err)
312
+
return
313
+
}
314
+
315
+
body, err := io.ReadAll(resp.Body)
316
+
if err != nil {
317
+
log.Printf("Error reading response body: %v", err)
318
+
return
319
+
}
320
+
321
+
var result types.RepoBranchesResponse
322
+
err = json.Unmarshal(body, &result)
323
+
if err != nil {
324
+
log.Println("failed to parse response:", err)
325
+
return
326
+
}
327
+
306
328
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
307
329
LoggedInUser: user,
308
330
RepoInfo: f.RepoInfo(s, user),
331
+
Branches: result.Branches,
309
332
})
310
333
case http.MethodPost:
311
334
title := r.FormValue("title")
···
347
370
return
348
371
}
349
372
client, _ := s.auth.AuthorizedClient(r)
350
-
pullId, err := db.GetPullId(s.db, f.RepoAt)
373
+
pullId, err := db.NextPullId(s.db, f.RepoAt)
351
374
if err != nil {
352
375
log.Println("failed to get pull id", err)
353
376
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
604
627
return
605
628
}
606
629
607
-
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/branches", f.Knot, f.OwnerDid(), f.RepoName))
630
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
631
+
if err != nil {
632
+
log.Println("failed to create unsigned client", err)
633
+
return
634
+
}
635
+
636
+
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
608
637
if err != nil {
609
638
log.Println("failed to reach knotserver", err)
610
639
return
+67
-3
appview/state/signer.go
+67
-3
appview/state/signer.go
···
41
41
},
42
42
}
43
43
44
-
uri := "https"
44
+
scheme := "https"
45
45
if dev {
46
-
uri = "http"
46
+
scheme = "http"
47
47
}
48
-
url, err := url.Parse(fmt.Sprintf("%s://%s", uri, domain))
48
+
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
49
49
if err != nil {
50
50
return nil, err
51
51
}
···
193
193
194
194
return s.client.Do(req)
195
195
}
196
+
197
+
type UnsignedClient struct {
198
+
Url *url.URL
199
+
client *http.Client
200
+
}
201
+
202
+
func NewUnsignedClient(domain string, dev bool) (*UnsignedClient, error) {
203
+
client := &http.Client{
204
+
Timeout: 5 * time.Second,
205
+
}
206
+
207
+
scheme := "https"
208
+
if dev {
209
+
scheme = "http"
210
+
}
211
+
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
212
+
if err != nil {
213
+
return nil, err
214
+
}
215
+
216
+
unsignedClient := &UnsignedClient{
217
+
client: client,
218
+
Url: url,
219
+
}
220
+
221
+
return unsignedClient, nil
222
+
}
223
+
224
+
func (us *UnsignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
225
+
return http.NewRequest(method, us.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
226
+
}
227
+
228
+
func (us *UnsignedClient) Index(ownerDid, repoName, ref string) (*http.Response, error) {
229
+
const (
230
+
Method = "GET"
231
+
)
232
+
233
+
endpoint := fmt.Sprintf("/%s/%s/tree/%s", ownerDid, repoName, ref)
234
+
if ref == "" {
235
+
endpoint = fmt.Sprintf("/%s/%s", ownerDid, repoName)
236
+
}
237
+
238
+
req, err := us.newRequest(Method, endpoint, nil)
239
+
if err != nil {
240
+
return nil, err
241
+
}
242
+
243
+
return us.client.Do(req)
244
+
}
245
+
246
+
func (us *UnsignedClient) Branches(ownerDid, repoName string) (*http.Response, error) {
247
+
const (
248
+
Method = "GET"
249
+
)
250
+
251
+
endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
252
+
253
+
req, err := us.newRequest(Method, endpoint, nil)
254
+
if err != nil {
255
+
return nil, err
256
+
}
257
+
258
+
return us.client.Do(req)
259
+
}