+2
-2
.air.toml
+2
-2
.air.toml
+7
-6
cmd/knotserver/main.go
+7
-6
cmd/knotserver/main.go
···
10
10
11
11
"github.com/icyphox/bild/knotserver"
12
12
"github.com/icyphox/bild/knotserver/config"
13
+
"github.com/icyphox/bild/knotserver/db"
13
14
)
14
15
15
16
func main() {
···
23
24
if err != nil {
24
25
log.Fatal(err)
25
26
}
26
-
// db, err := db.Setup(c.Server.DBPath)
27
-
// if err != nil {
28
-
// log.Fatalf("failed to setup db: %s", err)
29
-
// }
27
+
db, err := db.Setup(c.Server.DBPath)
28
+
if err != nil {
29
+
log.Fatalf("failed to setup db: %s", err)
30
+
}
30
31
31
-
mux, err := knotserver.Setup(c, nil)
32
+
mux, err := knotserver.Setup(c, db)
32
33
if err != nil {
33
34
log.Fatal(err)
34
35
}
35
36
36
-
addr := fmt.Sprintf("%s:%d", c.Host, c.Port)
37
+
addr := fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
37
38
38
39
log.Println("starting main server on", addr)
39
40
log.Fatal(http.ListenAndServe(addr, mux))
+2
go.mod
+2
go.mod
···
9
9
github.com/bluekeyes/go-gitdiff v0.8.0
10
10
github.com/bluesky-social/indigo v0.0.0-20250123072624-9e3b84fdbb20
11
11
github.com/dustin/go-humanize v1.0.1
12
+
github.com/gliderlabs/ssh v0.3.5
12
13
github.com/go-chi/chi/v5 v5.2.0
13
14
github.com/go-git/go-git/v5 v5.12.0
14
15
github.com/google/uuid v1.6.0
···
28
29
github.com/Microsoft/go-winio v0.6.2 // indirect
29
30
github.com/ProtonMail/go-crypto v1.0.0 // indirect
30
31
github.com/acomagu/bufpipe v1.0.4 // indirect
32
+
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
31
33
github.com/aymerick/douceur v0.2.0 // indirect
32
34
github.com/beorn7/perks v1.0.1 // indirect
33
35
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
+9
-5
knotserver/config/config.go
+9
-5
knotserver/config/config.go
···
12
12
MainBranch []string `env:"MAIN_BRANCH"`
13
13
}
14
14
15
-
type Config struct {
16
-
Host string `env:"KNOTSERVER_HOST, default=0.0.0.0"`
17
-
Port int `env:"KNOTSERVER_PORT, default=5555"`
18
-
Secret string `env:"KNOTSERVER_SECRET, required"`
15
+
type Server struct {
16
+
Host string `env:"HOST, default=0.0.0.0"`
17
+
Port int `env:"PORT, default=5555"`
18
+
Secret string `env:"SECRET, required"`
19
+
DBPath string `env:"DB_PATH, default=knotserver.db"`
20
+
}
19
21
20
-
Repo Repo `env:",prefix=KNOTSERVER_REPO_"`
22
+
type Config struct {
23
+
Repo Repo `env:",prefix=KNOT_REPO_"`
24
+
Server Server `env:",prefix=KNOT_SERVER_"`
21
25
}
22
26
23
27
func Load(ctx context.Context) (*Config, error) {
+51
knotserver/db/init.go
+51
knotserver/db/init.go
···
1
+
package db
2
+
3
+
import (
4
+
"database/sql"
5
+
6
+
_ "github.com/mattn/go-sqlite3"
7
+
)
8
+
9
+
type DB struct {
10
+
db *sql.DB
11
+
}
12
+
13
+
func Setup(dbPath string) (*DB, error) {
14
+
db, err := sql.Open("sqlite3", dbPath)
15
+
if err != nil {
16
+
return nil, err
17
+
}
18
+
19
+
_, err = db.Exec(`
20
+
create table if not exists public_keys (
21
+
id integer primary key autoincrement,
22
+
did text not null,
23
+
name text not null,
24
+
key text not null,
25
+
created timestamp default current_timestamp,
26
+
unique(did, name, key)
27
+
);
28
+
create table if not exists repos (
29
+
id integer primary key autoincrement,
30
+
did text not null,
31
+
name text not null,
32
+
description text not null,
33
+
created timestamp default current_timestamp,
34
+
unique(did, name)
35
+
);
36
+
create table if not exists access_levels (
37
+
id integer primary key autoincrement,
38
+
repo_id integer not null,
39
+
did text not null,
40
+
access text not null check (access in ('OWNER', 'WRITER')),
41
+
created timestamp default current_timestamp,
42
+
unique(repo_id, did),
43
+
foreign key (repo_id) references repos(id) on delete cascade
44
+
);
45
+
`)
46
+
if err != nil {
47
+
return nil, err
48
+
}
49
+
50
+
return &DB{db: db}, nil
51
+
}
+80
knotserver/db/pubkeys.go
+80
knotserver/db/pubkeys.go
···
1
+
package db
2
+
3
+
import "time"
4
+
5
+
func (d *DB) AddPublicKey(did, name, key string) error {
6
+
query := `insert into public_keys (did, name, key, created) values (?, ?, ?, ?)`
7
+
_, err := d.db.Exec(query, did, name, key, time.Now())
8
+
return err
9
+
}
10
+
11
+
func (d *DB) RemovePublicKey(did string) error {
12
+
query := `delete from public_keys where did = ?`
13
+
_, err := d.db.Exec(query, did)
14
+
return err
15
+
}
16
+
17
+
type PublicKey struct {
18
+
Key string
19
+
Name string
20
+
DID string
21
+
Created time.Time
22
+
}
23
+
24
+
func (pk *PublicKey) JSON() map[string]interface{} {
25
+
return map[string]interface{}{
26
+
pk.DID: map[string]interface{}{
27
+
"key": pk.Key,
28
+
"name": pk.Name,
29
+
"created": pk.Created,
30
+
},
31
+
}
32
+
}
33
+
34
+
func (d *DB) GetAllPublicKeys() ([]PublicKey, error) {
35
+
var keys []PublicKey
36
+
37
+
rows, err := d.db.Query(`select key, name, did, created from public_keys`)
38
+
if err != nil {
39
+
return nil, err
40
+
}
41
+
defer rows.Close()
42
+
43
+
for rows.Next() {
44
+
var publicKey PublicKey
45
+
if err := rows.Scan(&publicKey.Key, &publicKey.Name, &publicKey.DID, &publicKey.Created); err != nil {
46
+
return nil, err
47
+
}
48
+
keys = append(keys, publicKey)
49
+
}
50
+
51
+
if err := rows.Err(); err != nil {
52
+
return nil, err
53
+
}
54
+
55
+
return keys, nil
56
+
}
57
+
58
+
func (d *DB) GetPublicKeys(did string) ([]PublicKey, error) {
59
+
var keys []PublicKey
60
+
61
+
rows, err := d.db.Query(`select did, key, name, created from public_keys where did = ?`, did)
62
+
if err != nil {
63
+
return nil, err
64
+
}
65
+
defer rows.Close()
66
+
67
+
for rows.Next() {
68
+
var publicKey PublicKey
69
+
if err := rows.Scan(&publicKey.DID, &publicKey.Key, &publicKey.Name, &publicKey.Created); err != nil {
70
+
return nil, err
71
+
}
72
+
keys = append(keys, publicKey)
73
+
}
74
+
75
+
if err := rows.Err(); err != nil {
76
+
return nil, err
77
+
}
78
+
79
+
return keys, nil
80
+
}
+5
-7
knotserver/handler.go
+5
-7
knotserver/handler.go
···
5
5
"net/http"
6
6
7
7
"github.com/go-chi/chi/v5"
8
-
"github.com/icyphox/bild/db"
9
8
"github.com/icyphox/bild/knotserver/config"
9
+
"github.com/icyphox/bild/knotserver/db"
10
10
)
11
11
12
12
func Setup(c *config.Config, db *db.DB) (http.Handler, error) {
···
17
17
db: db,
18
18
}
19
19
20
-
// r.Group(func(r chi.Router) {
21
-
// r.Route("/settings", func(r chi.Router) {
22
-
// r.Get("/keys", h.Keys)
23
-
// r.Put("/keys", h.Keys)
24
-
// })
25
-
// })
20
+
r.Route("/settings", func(r chi.Router) {
21
+
r.Get("/keys", h.Keys)
22
+
r.Put("/keys", h.Keys)
23
+
})
26
24
27
25
r.Get("/", h.Index)
28
26
r.Route("/{did}", func(r chi.Router) {
+1
-1
knotserver/middleware.go
+1
-1
knotserver/middleware.go
+37
-52
knotserver/routes.go
+37
-52
knotserver/routes.go
···
15
15
"strconv"
16
16
"strings"
17
17
18
+
"github.com/gliderlabs/ssh"
18
19
"github.com/go-chi/chi/v5"
19
20
"github.com/go-git/go-git/v5/plumbing"
20
21
"github.com/go-git/go-git/v5/plumbing/object"
22
+
"github.com/icyphox/bild/db"
21
23
"github.com/icyphox/bild/knotserver/git"
22
24
"github.com/russross/blackfriday/v2"
23
25
)
···
324
326
return
325
327
}
326
328
327
-
// func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
328
-
// session, _ := h.s.Get(r, "bild-session")
329
-
// did := session.Values["did"].(string)
329
+
func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
330
+
switch r.Method {
331
+
case http.MethodGet:
332
+
keys, err := h.db.GetAllPublicKeys()
333
+
if err != nil {
334
+
writeError(w, err.Error(), http.StatusInternalServerError)
335
+
log.Println(err)
336
+
return
337
+
}
330
338
331
-
// switch r.Method {
332
-
// case http.MethodGet:
333
-
// keys, err := h.db.GetPublicKeys(did)
334
-
// if err != nil {
335
-
// h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.")
336
-
// log.Println(err)
337
-
// return
338
-
// }
339
+
data := make([]map[string]interface{}, 0)
340
+
for _, key := range keys {
341
+
j := key.JSON()
342
+
data = append(data, j)
343
+
}
344
+
writeJSON(w, data)
345
+
return
339
346
340
-
// data := make(map[string]interface{})
341
-
// data["keys"] = keys
342
-
// if err := h.t.ExecuteTemplate(w, "settings/keys", data); err != nil {
343
-
// log.Println(err)
344
-
// return
345
-
// }
346
-
// case http.MethodPut:
347
-
// key := r.FormValue("key")
348
-
// name := r.FormValue("name")
349
-
// client, _ := h.auth.AuthorizedClient(r)
347
+
case http.MethodPut:
348
+
pk := db.PublicKey{}
349
+
if err := json.NewDecoder(r.Body).Decode(&pk); err != nil {
350
+
writeError(w, "invalid request body", http.StatusBadRequest)
351
+
return
352
+
}
350
353
351
-
// _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
352
-
// if err != nil {
353
-
// h.WriteOOBNotice(w, "keys", "Invalid public key. Check your formatting and try again.")
354
-
// log.Printf("parsing public key: %s", err)
355
-
// return
356
-
// }
354
+
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pk.Key))
355
+
if err != nil {
356
+
writeError(w, "invalid pubkey", http.StatusBadRequest)
357
+
}
357
358
358
-
// if err := h.db.AddPublicKey(did, name, key); err != nil {
359
-
// h.WriteOOBNotice(w, "keys", "Failed to add key.")
360
-
// log.Printf("adding public key: %s", err)
361
-
// return
362
-
// }
359
+
if err := h.db.AddPublicKey(pk.DID, pk.Name, pk.Key); err != nil {
360
+
writeError(w, err.Error(), http.StatusInternalServerError)
361
+
log.Printf("adding public key: %s", err)
362
+
return
363
+
}
363
364
364
-
// h.WriteOOBNotice(w, "keys", "Key added!")
365
-
// return
366
-
// }
367
-
// }
365
+
w.WriteHeader(http.StatusNoContent)
366
+
return
367
+
}
368
+
}
368
369
369
370
func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
370
371
data := struct {
···
389
390
390
391
w.WriteHeader(http.StatusNoContent)
391
392
}
392
-
393
-
// func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) {
394
-
// session, err := h.s.Get(r, "bild-session")
395
-
// user := make(map[string]string)
396
-
// if err != nil || session.IsNew {
397
-
// // user is not logged in
398
-
// } else {
399
-
// user["handle"] = session.Values["handle"].(string)
400
-
// user["did"] = session.Values["did"].(string)
401
-
// }
402
-
403
-
// if err := h.t.ExecuteTemplate(w, "timeline", user); err != nil {
404
-
// log.Println(err)
405
-
// return
406
-
// }
407
-
// }
408
393
409
394
func (h *Handle) Health(w http.ResponseWriter, r *http.Request) {
410
395
log.Println("got health check")