this repo has no description

add knot mgmt page

Akshay 81cece43 22193609

Changed files
+158 -53
appview
+90 -35
appview/db/db.go
··· 4 4 "database/sql" 5 5 "fmt" 6 6 "log" 7 + "time" 7 8 8 9 "github.com/google/uuid" 9 - 10 10 _ "github.com/mattn/go-sqlite3" 11 11 ) 12 12 ··· 25 25 domain text not null unique, 26 26 did text not null, 27 27 secret text not null, 28 - created integer default (strftime('%s', 'now')), 29 - registered integer); 28 + created timestamp default current_timestamp, 29 + registered timestamp); 30 30 create table if not exists public_keys ( 31 31 id integer primary key autoincrement, 32 32 did text not null, ··· 42 42 return &DB{db: db}, nil 43 43 } 44 44 45 - type RegStatus uint32 45 + type Registration struct { 46 + Domain string 47 + ByDid string 48 + Created *time.Time 49 + Registered *time.Time 50 + } 51 + 52 + func (r *Registration) Status() Status { 53 + if r.Registered != nil { 54 + return Registered 55 + } else { 56 + return Pending 57 + } 58 + } 59 + 60 + type Status uint32 46 61 47 62 const ( 48 - Registered RegStatus = iota 49 - Unregistered 63 + Registered Status = iota 50 64 Pending 51 65 ) 52 66 53 67 // returns registered status, did of owner, error 54 - func (d *DB) RegistrationStatus(domain string) (RegStatus, string, error) { 55 - var registeredBy string 56 - var registratedAt *uint64 68 + func (d *DB) RegistrationsByDid(did string) ([]Registration, error) { 69 + var registrations []Registration 70 + 71 + rows, err := d.db.Query(` 72 + select domain, did, created, registered from registrations 73 + where did = ? 74 + `, did) 75 + if err != nil { 76 + return nil, err 77 + } 78 + 79 + for rows.Next() { 80 + var createdAt *int64 81 + var registeredAt *int64 82 + var registration Registration 83 + err = rows.Scan(&registration.Domain, &registration.ByDid, &createdAt, &registeredAt) 84 + 85 + if err != nil { 86 + log.Println(err) 87 + } else { 88 + createdAtTime := time.Unix(*createdAt, 0) 89 + 90 + var registeredAtTime *time.Time 91 + if registeredAt != nil { 92 + x := time.Unix(*registeredAt, 0) 93 + registeredAtTime = &x 94 + } 95 + 96 + registration.Created = &createdAtTime 97 + registration.Registered = registeredAtTime 98 + registrations = append(registrations, registration) 99 + } 100 + } 101 + 102 + return registrations, nil 103 + } 104 + 105 + // returns registered status, did of owner, error 106 + func (d *DB) RegistrationByDomain(domain string) (*Registration, error) { 107 + var createdAt *int64 108 + var registeredAt *int64 109 + var registration Registration 57 110 err := d.db.QueryRow(` 58 - select did, registered from registrations 111 + select domain, did, created, registered from registrations 59 112 where domain = ? 60 - `, domain).Scan(&registeredBy, &registratedAt) 113 + `, domain).Scan(&registration.Domain, &registration.ByDid, &createdAt, &registeredAt) 114 + 115 + createdAtTime := time.Unix(*createdAt, 0) 116 + var registeredAtTime *time.Time 117 + if registeredAt != nil { 118 + x := time.Unix(*registeredAt, 0) 119 + registeredAtTime = &x 120 + } 121 + 122 + registration.Created = &createdAtTime 123 + registration.Registered = registeredAtTime 124 + 61 125 if err != nil { 62 126 if err == sql.ErrNoRows { 63 - return Unregistered, "", nil 127 + return nil, nil 64 128 } else { 65 - return Unregistered, "", err 129 + return nil, err 66 130 } 67 131 } 68 132 69 - if registratedAt != nil { 70 - return Registered, registeredBy, nil 71 - } else { 72 - return Pending, registeredBy, nil 73 - } 133 + return &registration, nil 74 134 } 75 135 76 136 func (d *DB) GenerateRegistrationKey(domain, did string) (string, error) { 77 137 // sanity check: does this domain already have a registration? 78 - status, owner, err := d.RegistrationStatus(domain) 138 + reg, err := d.RegistrationByDomain(domain) 79 139 if err != nil { 80 140 return "", err 81 141 } 82 - switch status { 83 - case Registered: 84 - // already registered by `owner` 85 - return "", fmt.Errorf("%s already registered by %s", domain, owner) 86 - case Pending: 87 - log.Printf("%s registered by %s, status pending", domain, owner) 88 - // TODO: provide a warning here, and allow the current user to overwrite 89 - // the registration, this prevents users from registering domains that they 90 - // do not own 91 - default: 92 - // ok, we can register this domain 142 + 143 + // registration is open 144 + if reg != nil { 145 + switch reg.Status() { 146 + case Registered: 147 + // already registered by `owner` 148 + return "", fmt.Errorf("%s already registered by %s", domain, reg.ByDid) 149 + case Pending: 150 + // TODO: be loud about this 151 + log.Printf("%s registered by %s, status pending", domain, reg.ByDid) 152 + } 93 153 } 94 154 95 155 secret := uuid.New().String() ··· 134 194 135 195 return nil 136 196 } 137 - 138 - // type Registration struct { 139 - // status RegStatus 140 - // } 141 - // func (d *DB) RegistrationsForDid(did string) ()
+34
appview/pages/knots.html
··· 1 + {{define "title"}}knots{{end}} 2 + 3 + {{define "content"}} 4 + <a href="/">back to timeline</a> 5 + <h1>knots</h1> 6 + 7 + <h2>register</h2> 8 + put in a domain, and use the key while booting up your knotserver 9 + <form hx-post="/knots/key"> 10 + <label for="domain">domain:</label> 11 + <input type="text" id="domain" name="domain" required> 12 + 13 + <button type="domain">generate key</button> 14 + </form> 15 + 16 + <h3>existing registrations</h3> 17 + <ul id="registrations"> 18 + {{range .Registrations}} 19 + <li> 20 + <code>domain: {{.Domain}}</code><br> 21 + <code>opened by: {{.ByDid}}</code><br> 22 + <code>on: {{.Created}}</code><br> 23 + {{if .Registered}} 24 + <code>registered on: {{.Registered}}</code> 25 + {{else}} 26 + <code>pending registration</code> 27 + <button hx-post="/knots/init/{{.Domain}}">initialize</button> 28 + {{end}} 29 + </li> 30 + {{else}} 31 + <p>no registrations yet</p> 32 + {{end}} 33 + </ul> 34 + {{end}}
+9
appview/pages/pages.go
··· 57 57 func Settings(w io.Writer, p SettingsParams) error { 58 58 return parse("settings.html").Execute(w, p) 59 59 } 60 + 61 + type KnotsParams struct { 62 + User *auth.User 63 + Registrations []db.Registration 64 + } 65 + 66 + func Knots(w io.Writer, p KnotsParams) error { 67 + return parse("knots.html").Execute(w, p) 68 + }
+1
appview/pages/timeline.html
··· 6 6 {{ if .User }} 7 7 <p>logged in as {{ .User.Handle }}</p> 8 8 <a href="/settings">settings</a> 9 + <a href="/knots">knots</a> 9 10 {{ end }} 10 11 {{end}}
+24 -18
appview/state/state.go
··· 87 87 88 88 func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 89 89 user := s.auth.GetUser(r) 90 - fmt.Printf("%+v\n", user) 91 90 pages.Timeline(w, pages.TimelineParams{ 92 91 User: user, 93 92 }) ··· 142 141 User: user, 143 142 PubKeys: pubKeys, 144 143 }) 145 - return 146 - 147 144 } 148 145 149 146 func (s *State) Keys(w http.ResponseWriter, r *http.Request) { ··· 194 191 } 195 192 196 193 // create a signed request and check if a node responds to that 197 - // 198 - // we should also rate limit these checks to avoid ddosing knotservers 199 - func (s *State) Check(w http.ResponseWriter, r *http.Request) { 200 - domain := r.FormValue("domain") 194 + func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) { 195 + domain := chi.URLParam(r, "domain") 201 196 if domain == "" { 202 - http.Error(w, "Invalid form", http.StatusBadRequest) 197 + http.Error(w, "malformed url", http.StatusBadRequest) 203 198 return 204 199 } 205 200 ··· 261 256 } 262 257 263 258 // set permissions for this did as owner 264 - _, did, err := s.db.RegistrationStatus(domain) 259 + reg, err := s.db.RegistrationByDomain(domain) 265 260 if err != nil { 266 261 log.Println("failed to register domain", err) 267 262 http.Error(w, err.Error(), http.StatusInternalServerError) ··· 277 272 } 278 273 279 274 // add this did as owner of this domain 280 - err = s.enforcer.AddOwner(domain, did) 275 + err = s.enforcer.AddOwner(domain, reg.ByDid) 281 276 if err != nil { 282 277 log.Println("failed to setup owner of domain", err) 283 278 http.Error(w, err.Error(), http.StatusInternalServerError) ··· 285 280 } 286 281 287 282 w.Write([]byte("check success")) 283 + } 288 284 289 - return 285 + // get knots registered by this user 286 + func (s *State) Knots(w http.ResponseWriter, r *http.Request) { 287 + // for now, this is just pubkeys 288 + user := s.auth.GetUser(r) 289 + registrations, err := s.db.RegistrationsByDid(user.Did) 290 + if err != nil { 291 + log.Println(err) 292 + } 293 + 294 + pages.Knots(w, pages.KnotsParams{ 295 + User: user, 296 + Registrations: registrations, 297 + }) 290 298 } 291 299 292 300 func buildPingRequest(url, secret string) (*http.Request, error) { ··· 315 323 r.Get("/login", s.Login) 316 324 r.Post("/login", s.Login) 317 325 318 - r.Route("/node", func(r chi.Router) { 319 - r.Post("/check", s.Check) 320 - 321 - r.Group(func(r chi.Router) { 322 - r.Use(AuthMiddleware(s)) 323 - r.Post("/key", s.RegistrationKey) 324 - }) 326 + r.Route("/knots", func(r chi.Router) { 327 + r.Use(AuthMiddleware(s)) 328 + r.Get("/", s.Knots) 329 + r.Post("/init/{domain}", s.InitKnotServer) 330 + r.Post("/key", s.RegistrationKey) 325 331 }) 326 332 327 333 r.Group(func(r chi.Router) {