+90
-35
appview/db/db.go
+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(®istration.Domain, ®istration.ByDid, &createdAt, ®isteredAt)
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(®isteredBy, ®istratedAt)
113
+
`, domain).Scan(®istration.Domain, ®istration.ByDid, &createdAt, ®isteredAt)
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 ®istration, 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
+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
+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
+1
appview/pages/timeline.html
+24
-18
appview/state/state.go
+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) {