forked from tangled.org/core
this repo has no description

show branches in a dropdown

Changed files
+293 -25
appview
db
pages
templates
repo
pulls
randomart
state
+1 -7
appview/db/pulls.go
···
return pullAt, err
}
-
func GetPullId(e Execer, repoAt syntax.ATURI) (int, error) {
+
func NextPullId(e Execer, repoAt syntax.ATURI) (int, error) {
var pullId int
err := e.QueryRow(`select next_pull_id from repo_pull_seqs where repo_at = ?`, repoAt).Scan(&pullId)
return pullId - 1, err
-
}
-
-
func GetPullOwnerDid(e Execer, repoAt syntax.ATURI, pullId int) (string, error) {
-
var ownerDid string
-
err := e.QueryRow(`select owner_did from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&ownerDid)
-
return ownerDid, err
}
func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pull, error) {
+1
appview/pages/pages.go
···
type RepoNewPullParams struct {
LoggedInUser *auth.User
RepoInfo RepoInfo
+
Branches []types.Branch
Active string
}
+10 -1
appview/pages/templates/repo/pulls/new.html
···
<p class="text-gray-500">
The branch you want to make your change against.
</p>
-
<input type="text" name="targetBranch" id="targetBranch" />
+
<select class="p-1 border border-gray-200 bg-white">
+
<option disabled selected>Select a branch</option>
+
{{ range .Branches }}
+
<option
+
value="{{ .Reference.Name }}"
+
class="py-1">
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</select>
</div>
<div>
<label for="body">body</label>
-2
appview/pages/templates/settings.html
···
{{ end }}
{{ define "profile" }}
-
<<<<<<< HEAD
<h2 class="text-sm font-bold py-2 px-6 uppercase">profile</h2>
<section class="rounded bg-white drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
<dl class="grid grid-cols-[auto_1fr] gap-x-4">
···
{{ end }}
{{ define "keys" }}
-
<<<<<<< HEAD
<h2 class="text-sm font-bold py-2 px-6 uppercase">ssh keys</h2>
<section class="rounded bg-white drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
<div id="key-list" class="flex flex-col gap-6 mb-8">
+173
appview/randomart/randomart.go
···
+
package randomart
+
+
/*
+
* Draw an ASCII-Art representing the fingerprint so human brain can
+
* profit from its built-in pattern recognition ability.
+
* This technique is called "random art" and can be found in some
+
* scientific publications like this original paper:
+
*
+
* "Hash Visualization: a New Technique to improve Real-World Security",
+
* Perrig A. and Song D., 1999, International Workshop on Cryptographic
+
* Techniques and E-Commerce (CrypTEC '99)
+
* sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+
*
+
* The subject came up in a talk by Dan Kaminsky, too.
+
*
+
* If you see the picture is different, the key is different.
+
* If the picture looks the same, you still know nothing.
+
*
+
* The algorithm used here is a worm crawling over a discrete plane,
+
* leaving a trace (augmenting the field) everywhere it goes.
+
* Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+
* makes the respective movement vector be ignored for this turn.
+
* Graphs are not unambiguous, because circles in graphs can be
+
* walked in either direction.
+
*/
+
+
import (
+
"os"
+
)
+
+
func MAX(a, b int) int {
+
if a > b {
+
return a
+
}
+
return b
+
}
+
+
func MIN(a, b int) int {
+
if a < b {
+
return a
+
}
+
return b
+
}
+
+
/*
+
* Field sizes for the random art. Have to be odd, so the starting point
+
* can be in the exact middle of the picture, and FLDBASE should be >=8 .
+
* Else pictures would be too dense, and drawing the frame would
+
* fail, too, because the key type would not fit in anymore.
+
*/
+
const (
+
FLDBASE = 8
+
FLDSIZE_Y = (FLDBASE + 1)
+
FLDSIZE_X = (FLDBASE*2 + 1)
+
)
+
+
func FromString(str string) string {
+
ch := make(chan byte)
+
+
go func() {
+
defer close(ch)
+
for _, v := range []byte(str) {
+
ch <- v
+
}
+
}()
+
return key_fingerprint_randomart(ch)
+
}
+
+
func FromFile(file *os.File) string {
+
ch := make(chan byte)
+
+
go func() {
+
defer close(ch)
+
// TODO make input a 1 element byte array
+
input := make([]byte, 1)
+
nread, err := file.Read(input)
+
for err == nil && nread > 0 {
+
ch <- input[0]
+
nread, err = file.Read(input)
+
}
+
}()
+
return key_fingerprint_randomart(ch)
+
}
+
+
func key_fingerprint_randomart(ch chan byte) string {
+
/*
+
* Chars to be used after each other every time the worm
+
* intersects with itself. Matter of taste.
+
*/
+
augment_string := " .o+=*BOX@%&#/^SE"
+
var field [FLDSIZE_X][FLDSIZE_Y]byte
+
len_aug := len(augment_string) - 1
+
var retval [(FLDSIZE_X + 3) * (FLDSIZE_Y + 2)]byte
+
+
/* initialize field */
+
x := FLDSIZE_X / 2
+
y := FLDSIZE_Y / 2
+
+
/* process raw key */
+
for input, ok := <-ch; ok; input, ok = <-ch {
+
/* each byte conveys four 2-bit move commands */
+
for b := 0; b < 4; b++ {
+
/* evaluate 2 bit, rest is shifted later */
+
if input&0x1 > 0 {
+
x += 1
+
} else {
+
x += -1
+
}
+
+
if input&0x2 > 0 {
+
y++
+
} else {
+
y--
+
}
+
+
/* assure we are still in bounds */
+
x = MAX(x, 0)
+
y = MAX(y, 0)
+
x = MIN(x, FLDSIZE_X-1)
+
y = MIN(y, FLDSIZE_Y-1)
+
+
/* augment the field */
+
if int(field[x][y]) < len_aug-2 {
+
field[x][y]++
+
}
+
input = input >> 2
+
}
+
}
+
+
/* mark starting point and end point*/
+
field[FLDSIZE_X/2][FLDSIZE_Y/2] = byte(len_aug - 1)
+
field[x][y] = byte(len_aug)
+
+
i := 0
+
retval[i] = '+'
+
i++
+
+
/* output upper border */
+
for x := 0; x < FLDSIZE_X; x++ {
+
retval[i] = '-'
+
i++
+
}
+
retval[i] = '+'
+
i++
+
retval[i] = '\n'
+
i++
+
+
/* output content */
+
for y := 0; y < FLDSIZE_Y; y++ {
+
retval[i] = '|'
+
i++
+
for x := 0; x < FLDSIZE_X; x++ {
+
retval[i] = augment_string[MIN(int(field[x][y]), len_aug)]
+
i++
+
}
+
retval[i] = '|'
+
i++
+
retval[i] = '\n'
+
i++
+
}
+
+
/* output lower border */
+
retval[i] = '+'
+
i++
+
for j := 0; j < FLDSIZE_X; j++ {
+
retval[i] = '-'
+
i++
+
}
+
retval[i] = '+'
+
i++
+
+
return string(retval[0:i])
+
}
+41 -12
appview/state/repo.go
···
log.Println("failed to fully resolve repo", err)
return
}
-
protocol := "http"
-
if !s.config.Dev {
-
protocol = "https"
-
}
-
var reqUrl string
-
if ref != "" {
-
reqUrl = fmt.Sprintf("%s://%s/%s/%s/tree/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref)
-
} else {
-
reqUrl = fmt.Sprintf("%s://%s/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName)
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
if err != nil {
+
log.Printf("failed to create unsigned client for %s", f.Knot)
+
s.pages.Error503(w)
+
return
}
-
resp, err := http.Get(reqUrl)
+
resp, err := us.Index(f.OwnerDid(), f.RepoName, ref)
if err != nil {
s.pages.Error503(w)
log.Println("failed to reach knotserver", err)
···
switch r.Method {
case http.MethodGet:
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
if err != nil {
+
log.Printf("failed to create unsigned client for %s", f.Knot)
+
s.pages.Error503(w)
+
return
+
}
+
+
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
+
if err != nil {
+
log.Println("failed to reach knotserver", err)
+
return
+
}
+
+
body, err := io.ReadAll(resp.Body)
+
if err != nil {
+
log.Printf("Error reading response body: %v", err)
+
return
+
}
+
+
var result types.RepoBranchesResponse
+
err = json.Unmarshal(body, &result)
+
if err != nil {
+
log.Println("failed to parse response:", err)
+
return
+
}
+
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
+
Branches: result.Branches,
})
case http.MethodPost:
title := r.FormValue("title")
···
return
}
client, _ := s.auth.AuthorizedClient(r)
-
pullId, err := db.GetPullId(s.db, f.RepoAt)
+
pullId, err := db.NextPullId(s.db, f.RepoAt)
if err != nil {
log.Println("failed to get pull id", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
return
}
-
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/branches", f.Knot, f.OwnerDid(), f.RepoName))
+
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
if err != nil {
+
log.Println("failed to create unsigned client", err)
+
return
+
}
+
+
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
if err != nil {
log.Println("failed to reach knotserver", err)
return
+67 -3
appview/state/signer.go
···
},
}
-
uri := "https"
+
scheme := "https"
if dev {
-
uri = "http"
+
scheme = "http"
}
-
url, err := url.Parse(fmt.Sprintf("%s://%s", uri, domain))
+
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
if err != nil {
return nil, err
}
···
return s.client.Do(req)
}
+
+
type UnsignedClient struct {
+
Url *url.URL
+
client *http.Client
+
}
+
+
func NewUnsignedClient(domain string, dev bool) (*UnsignedClient, error) {
+
client := &http.Client{
+
Timeout: 5 * time.Second,
+
}
+
+
scheme := "https"
+
if dev {
+
scheme = "http"
+
}
+
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
+
if err != nil {
+
return nil, err
+
}
+
+
unsignedClient := &UnsignedClient{
+
client: client,
+
Url: url,
+
}
+
+
return unsignedClient, nil
+
}
+
+
func (us *UnsignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
+
return http.NewRequest(method, us.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
+
}
+
+
func (us *UnsignedClient) Index(ownerDid, repoName, ref string) (*http.Response, error) {
+
const (
+
Method = "GET"
+
)
+
+
endpoint := fmt.Sprintf("/%s/%s/tree/%s", ownerDid, repoName, ref)
+
if ref == "" {
+
endpoint = fmt.Sprintf("/%s/%s", ownerDid, repoName)
+
}
+
+
req, err := us.newRequest(Method, endpoint, nil)
+
if err != nil {
+
return nil, err
+
}
+
+
return us.client.Do(req)
+
}
+
+
func (us *UnsignedClient) Branches(ownerDid, repoName string) (*http.Response, error) {
+
const (
+
Method = "GET"
+
)
+
+
endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
+
+
req, err := us.newRequest(Method, endpoint, nil)
+
if err != nil {
+
return nil, err
+
}
+
+
return us.client.Do(req)
+
}