forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at knot-xrpc 4.8 kB view raw
1package db 2 3import ( 4 "crypto/rand" 5 "database/sql" 6 "encoding/hex" 7 "fmt" 8 "log" 9 "strings" 10 "time" 11) 12 13// Registration represents a knot registration. Knot would've been a better 14// name but we're stuck with this for historical reasons. 15type Registration struct { 16 Id int64 17 Domain string 18 ByDid string 19 Created *time.Time 20 Registered *time.Time 21} 22 23func (r *Registration) Status() Status { 24 if r.Registered != nil { 25 return Registered 26 } else { 27 return Pending 28 } 29} 30 31type Status uint32 32 33const ( 34 Registered Status = iota 35 Pending 36) 37 38// returns registered status, did of owner, error 39func RegistrationsByDid(e Execer, did string) ([]Registration, error) { 40 var registrations []Registration 41 42 rows, err := e.Query(` 43 select id, domain, did, created, registered from registrations 44 where did = ? 45 `, did) 46 if err != nil { 47 return nil, err 48 } 49 50 for rows.Next() { 51 var createdAt *string 52 var registeredAt *string 53 var registration Registration 54 err = rows.Scan(&registration.Id, &registration.Domain, &registration.ByDid, &createdAt, &registeredAt) 55 56 if err != nil { 57 log.Println(err) 58 } else { 59 createdAtTime, _ := time.Parse(time.RFC3339, *createdAt) 60 var registeredAtTime *time.Time 61 if registeredAt != nil { 62 x, _ := time.Parse(time.RFC3339, *registeredAt) 63 registeredAtTime = &x 64 } 65 66 registration.Created = &createdAtTime 67 registration.Registered = registeredAtTime 68 registrations = append(registrations, registration) 69 } 70 } 71 72 return registrations, nil 73} 74 75// returns registered status, did of owner, error 76func RegistrationByDomain(e Execer, domain string) (*Registration, error) { 77 var createdAt *string 78 var registeredAt *string 79 var registration Registration 80 81 err := e.QueryRow(` 82 select id, domain, did, created, registered from registrations 83 where domain = ? 84 `, domain).Scan(&registration.Id, &registration.Domain, &registration.ByDid, &createdAt, &registeredAt) 85 86 if err != nil { 87 if err == sql.ErrNoRows { 88 return nil, nil 89 } else { 90 return nil, err 91 } 92 } 93 94 createdAtTime, _ := time.Parse(time.RFC3339, *createdAt) 95 var registeredAtTime *time.Time 96 if registeredAt != nil { 97 x, _ := time.Parse(time.RFC3339, *registeredAt) 98 registeredAtTime = &x 99 } 100 101 registration.Created = &createdAtTime 102 registration.Registered = registeredAtTime 103 104 return &registration, nil 105} 106 107func genSecret() string { 108 key := make([]byte, 32) 109 rand.Read(key) 110 return hex.EncodeToString(key) 111} 112 113func GenerateRegistrationKey(e Execer, domain, did string) (string, error) { 114 // sanity check: does this domain already have a registration? 115 reg, err := RegistrationByDomain(e, domain) 116 if err != nil { 117 return "", err 118 } 119 120 // registration is open 121 if reg != nil { 122 switch reg.Status() { 123 case Registered: 124 // already registered by `owner` 125 return "", fmt.Errorf("%s already registered by %s", domain, reg.ByDid) 126 case Pending: 127 // TODO: be loud about this 128 log.Printf("%s registered by %s, status pending", domain, reg.ByDid) 129 } 130 } 131 132 secret := genSecret() 133 134 _, err = e.Exec(` 135 insert into registrations (domain, did, secret) 136 values (?, ?, ?) 137 on conflict(domain) do update set did = excluded.did, secret = excluded.secret, created = excluded.created 138 `, domain, did, secret) 139 140 if err != nil { 141 return "", err 142 } 143 144 return secret, nil 145} 146 147func GetRegistrationKey(e Execer, domain string) (string, error) { 148 res := e.QueryRow(`select secret from registrations where domain = ?`, domain) 149 150 var secret string 151 err := res.Scan(&secret) 152 if err != nil || secret == "" { 153 return "", err 154 } 155 156 return secret, nil 157} 158 159func GetCompletedRegistrations(e Execer) ([]string, error) { 160 rows, err := e.Query(`select domain from registrations where registered not null`) 161 if err != nil { 162 return nil, err 163 } 164 165 var domains []string 166 for rows.Next() { 167 var domain string 168 err = rows.Scan(&domain) 169 170 if err != nil { 171 log.Println(err) 172 } else { 173 domains = append(domains, domain) 174 } 175 } 176 177 if err = rows.Err(); err != nil { 178 return nil, err 179 } 180 181 return domains, nil 182} 183 184func Register(e Execer, domain string) error { 185 _, err := e.Exec(` 186 update registrations 187 set registered = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 188 where domain = ?; 189 `, domain) 190 191 return err 192} 193 194func AddKnot(e Execer, domain, did string) error { 195 _, err := e.Exec(` 196 insert into registrations (domain, did) 197 values (?, ?) 198 `, domain, did) 199 return err 200} 201 202func DeleteKnot(e Execer, filters ...filter) error { 203 var conditions []string 204 var args []any 205 for _, filter := range filters { 206 conditions = append(conditions, filter.Condition()) 207 args = append(args, filter.Arg()...) 208 } 209 210 whereClause := "" 211 if conditions != nil { 212 whereClause = " where " + strings.Join(conditions, " and ") 213 } 214 215 query := fmt.Sprintf(`delete from registrations %s`, whereClause) 216 217 _, err := e.Exec(query, args...) 218 return err 219}