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

knotserver: get/put ssh keys

Changed files
+194 -73
cmd
knotserver
knotserver
+2 -2
.air.toml
···
[build]
-
cmd = "go build -o bild ./cmd/legit/main.go"
-
bin = "bild"
+
cmd = "go build -o knot ./cmd/knotserver/main.go"
+
bin = "knot"
root = "."
exclude_regex = [".*_templ.go"]
+7 -6
cmd/knotserver/main.go
···
"github.com/icyphox/bild/knotserver"
"github.com/icyphox/bild/knotserver/config"
+
"github.com/icyphox/bild/knotserver/db"
)
func main() {
···
if err != nil {
log.Fatal(err)
}
-
// db, err := db.Setup(c.Server.DBPath)
-
// if err != nil {
-
// log.Fatalf("failed to setup db: %s", err)
-
// }
+
db, err := db.Setup(c.Server.DBPath)
+
if err != nil {
+
log.Fatalf("failed to setup db: %s", err)
+
}
-
mux, err := knotserver.Setup(c, nil)
+
mux, err := knotserver.Setup(c, db)
if err != nil {
log.Fatal(err)
}
-
addr := fmt.Sprintf("%s:%d", c.Host, c.Port)
+
addr := fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
log.Println("starting main server on", addr)
log.Fatal(http.ListenAndServe(addr, mux))
+2
go.mod
···
github.com/bluekeyes/go-gitdiff v0.8.0
github.com/bluesky-social/indigo v0.0.0-20250123072624-9e3b84fdbb20
github.com/dustin/go-humanize v1.0.1
+
github.com/gliderlabs/ssh v0.3.5
github.com/go-chi/chi/v5 v5.2.0
github.com/go-git/go-git/v5 v5.12.0
github.com/google/uuid v1.6.0
···
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
+
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
+9 -5
knotserver/config/config.go
···
MainBranch []string `env:"MAIN_BRANCH"`
}
-
type Config struct {
-
Host string `env:"KNOTSERVER_HOST, default=0.0.0.0"`
-
Port int `env:"KNOTSERVER_PORT, default=5555"`
-
Secret string `env:"KNOTSERVER_SECRET, required"`
+
type Server struct {
+
Host string `env:"HOST, default=0.0.0.0"`
+
Port int `env:"PORT, default=5555"`
+
Secret string `env:"SECRET, required"`
+
DBPath string `env:"DB_PATH, default=knotserver.db"`
+
}
-
Repo Repo `env:",prefix=KNOTSERVER_REPO_"`
+
type Config struct {
+
Repo Repo `env:",prefix=KNOT_REPO_"`
+
Server Server `env:",prefix=KNOT_SERVER_"`
}
func Load(ctx context.Context) (*Config, error) {
+51
knotserver/db/init.go
···
+
package db
+
+
import (
+
"database/sql"
+
+
_ "github.com/mattn/go-sqlite3"
+
)
+
+
type DB struct {
+
db *sql.DB
+
}
+
+
func Setup(dbPath string) (*DB, error) {
+
db, err := sql.Open("sqlite3", dbPath)
+
if err != nil {
+
return nil, err
+
}
+
+
_, err = db.Exec(`
+
create table if not exists public_keys (
+
id integer primary key autoincrement,
+
did text not null,
+
name text not null,
+
key text not null,
+
created timestamp default current_timestamp,
+
unique(did, name, key)
+
);
+
create table if not exists repos (
+
id integer primary key autoincrement,
+
did text not null,
+
name text not null,
+
description text not null,
+
created timestamp default current_timestamp,
+
unique(did, name)
+
);
+
create table if not exists access_levels (
+
id integer primary key autoincrement,
+
repo_id integer not null,
+
did text not null,
+
access text not null check (access in ('OWNER', 'WRITER')),
+
created timestamp default current_timestamp,
+
unique(repo_id, did),
+
foreign key (repo_id) references repos(id) on delete cascade
+
);
+
`)
+
if err != nil {
+
return nil, err
+
}
+
+
return &DB{db: db}, nil
+
}
+80
knotserver/db/pubkeys.go
···
+
package db
+
+
import "time"
+
+
func (d *DB) AddPublicKey(did, name, key string) error {
+
query := `insert into public_keys (did, name, key, created) values (?, ?, ?, ?)`
+
_, err := d.db.Exec(query, did, name, key, time.Now())
+
return err
+
}
+
+
func (d *DB) RemovePublicKey(did string) error {
+
query := `delete from public_keys where did = ?`
+
_, err := d.db.Exec(query, did)
+
return err
+
}
+
+
type PublicKey struct {
+
Key string
+
Name string
+
DID string
+
Created time.Time
+
}
+
+
func (pk *PublicKey) JSON() map[string]interface{} {
+
return map[string]interface{}{
+
pk.DID: map[string]interface{}{
+
"key": pk.Key,
+
"name": pk.Name,
+
"created": pk.Created,
+
},
+
}
+
}
+
+
func (d *DB) GetAllPublicKeys() ([]PublicKey, error) {
+
var keys []PublicKey
+
+
rows, err := d.db.Query(`select key, name, did, created from public_keys`)
+
if err != nil {
+
return nil, err
+
}
+
defer rows.Close()
+
+
for rows.Next() {
+
var publicKey PublicKey
+
if err := rows.Scan(&publicKey.Key, &publicKey.Name, &publicKey.DID, &publicKey.Created); err != nil {
+
return nil, err
+
}
+
keys = append(keys, publicKey)
+
}
+
+
if err := rows.Err(); err != nil {
+
return nil, err
+
}
+
+
return keys, nil
+
}
+
+
func (d *DB) GetPublicKeys(did string) ([]PublicKey, error) {
+
var keys []PublicKey
+
+
rows, err := d.db.Query(`select did, key, name, created from public_keys where did = ?`, did)
+
if err != nil {
+
return nil, err
+
}
+
defer rows.Close()
+
+
for rows.Next() {
+
var publicKey PublicKey
+
if err := rows.Scan(&publicKey.DID, &publicKey.Key, &publicKey.Name, &publicKey.Created); err != nil {
+
return nil, err
+
}
+
keys = append(keys, publicKey)
+
}
+
+
if err := rows.Err(); err != nil {
+
return nil, err
+
}
+
+
return keys, nil
+
}
+5 -7
knotserver/handler.go
···
"net/http"
"github.com/go-chi/chi/v5"
-
"github.com/icyphox/bild/db"
"github.com/icyphox/bild/knotserver/config"
+
"github.com/icyphox/bild/knotserver/db"
)
func Setup(c *config.Config, db *db.DB) (http.Handler, error) {
···
db: db,
}
-
// r.Group(func(r chi.Router) {
-
// r.Route("/settings", func(r chi.Router) {
-
// r.Get("/keys", h.Keys)
-
// r.Put("/keys", h.Keys)
-
// })
-
// })
+
r.Route("/settings", func(r chi.Router) {
+
r.Get("/keys", h.Keys)
+
r.Put("/keys", h.Keys)
+
})
r.Get("/", h.Index)
r.Route("/{did}", func(r chi.Router) {
+1 -1
knotserver/middleware.go
···
}
func (h *Handle) verifyHMAC(signature string, r *http.Request) bool {
-
secret := h.c.Secret
+
secret := h.c.Server.Secret
timestamp := r.Header.Get("X-Timestamp")
if timestamp == "" {
return false
+37 -52
knotserver/routes.go
···
"strconv"
"strings"
+
"github.com/gliderlabs/ssh"
"github.com/go-chi/chi/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
+
"github.com/icyphox/bild/db"
"github.com/icyphox/bild/knotserver/git"
"github.com/russross/blackfriday/v2"
)
···
return
}
-
// func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
-
// session, _ := h.s.Get(r, "bild-session")
-
// did := session.Values["did"].(string)
+
func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
+
switch r.Method {
+
case http.MethodGet:
+
keys, err := h.db.GetAllPublicKeys()
+
if err != nil {
+
writeError(w, err.Error(), http.StatusInternalServerError)
+
log.Println(err)
+
return
+
}
-
// switch r.Method {
-
// case http.MethodGet:
-
// keys, err := h.db.GetPublicKeys(did)
-
// if err != nil {
-
// h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.")
-
// log.Println(err)
-
// return
-
// }
+
data := make([]map[string]interface{}, 0)
+
for _, key := range keys {
+
j := key.JSON()
+
data = append(data, j)
+
}
+
writeJSON(w, data)
+
return
-
// data := make(map[string]interface{})
-
// data["keys"] = keys
-
// if err := h.t.ExecuteTemplate(w, "settings/keys", data); err != nil {
-
// log.Println(err)
-
// return
-
// }
-
// case http.MethodPut:
-
// key := r.FormValue("key")
-
// name := r.FormValue("name")
-
// client, _ := h.auth.AuthorizedClient(r)
+
case http.MethodPut:
+
pk := db.PublicKey{}
+
if err := json.NewDecoder(r.Body).Decode(&pk); err != nil {
+
writeError(w, "invalid request body", http.StatusBadRequest)
+
return
+
}
-
// _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
-
// if err != nil {
-
// h.WriteOOBNotice(w, "keys", "Invalid public key. Check your formatting and try again.")
-
// log.Printf("parsing public key: %s", err)
-
// return
-
// }
+
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pk.Key))
+
if err != nil {
+
writeError(w, "invalid pubkey", http.StatusBadRequest)
+
}
-
// if err := h.db.AddPublicKey(did, name, key); err != nil {
-
// h.WriteOOBNotice(w, "keys", "Failed to add key.")
-
// log.Printf("adding public key: %s", err)
-
// return
-
// }
+
if err := h.db.AddPublicKey(pk.DID, pk.Name, pk.Key); err != nil {
+
writeError(w, err.Error(), http.StatusInternalServerError)
+
log.Printf("adding public key: %s", err)
+
return
+
}
-
// h.WriteOOBNotice(w, "keys", "Key added!")
-
// return
-
// }
-
// }
+
w.WriteHeader(http.StatusNoContent)
+
return
+
}
+
}
func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
data := struct {
···
w.WriteHeader(http.StatusNoContent)
}
-
-
// func (h *Handle) Timeline(w http.ResponseWriter, r *http.Request) {
-
// session, err := h.s.Get(r, "bild-session")
-
// user := make(map[string]string)
-
// if err != nil || session.IsNew {
-
// // user is not logged in
-
// } else {
-
// user["handle"] = session.Values["handle"].(string)
-
// user["did"] = session.Values["did"].(string)
-
// }
-
-
// if err := h.t.ExecuteTemplate(w, "timeline", user); err != nil {
-
// log.Println(err)
-
// return
-
// }
-
// }
func (h *Handle) Health(w http.ResponseWriter, r *http.Request) {
log.Println("got health check")