forked from tangled.org/core
Monorepo for Tangled

knotserver: add query lexicon for owner

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 6adfb3a9 4cdc2d84

verified
Changed files
+225 -151
appview
knotserver
spindle
+31
appview/db/db.go
··· 703 return err 704 }) 705 706 return &DB{db}, nil 707 } 708
··· 703 return err 704 }) 705 706 + // repurpose the read-only column to "needs-upgrade" 707 + runMigration(conn, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error { 708 + _, err := tx.Exec(` 709 + alter table registrations rename column read_only to needs_upgrade; 710 + `) 711 + return err 712 + }) 713 + 714 + // require all knots to upgrade after the release of total xrpc 715 + runMigration(conn, "migrate-knots-to-total-xrpc", func(tx *sql.Tx) error { 716 + _, err := tx.Exec(` 717 + update registrations set needs_upgrade = 1; 718 + `) 719 + return err 720 + }) 721 + 722 + // require all knots to upgrade after the release of total xrpc 723 + runMigration(conn, "migrate-spindles-to-xrpc-owner", func(tx *sql.Tx) error { 724 + _, err := tx.Exec(` 725 + alter table spindles add column needs_upgrade integer not null default 0; 726 + `) 727 + if err != nil { 728 + return err 729 + } 730 + 731 + _, err = tx.Exec(` 732 + update spindles set needs_upgrade = 1; 733 + `) 734 + return err 735 + }) 736 + 737 return &DB{db}, nil 738 } 739
+17 -17
appview/db/registration.go
··· 10 // Registration represents a knot registration. Knot would've been a better 11 // name but we're stuck with this for historical reasons. 12 type Registration struct { 13 - Id int64 14 - Domain string 15 - ByDid string 16 - Created *time.Time 17 - Registered *time.Time 18 - ReadOnly bool 19 } 20 21 func (r *Registration) Status() Status { 22 - if r.ReadOnly { 23 - return ReadOnly 24 } else if r.Registered != nil { 25 return Registered 26 } else { ··· 32 return r.Status() == Registered 33 } 34 35 - func (r *Registration) IsReadOnly() bool { 36 - return r.Status() == ReadOnly 37 } 38 39 func (r *Registration) IsPending() bool { ··· 45 const ( 46 Registered Status = iota 47 Pending 48 - ReadOnly 49 ) 50 51 func GetRegistrations(e Execer, filters ...filter) ([]Registration, error) { ··· 64 } 65 66 query := fmt.Sprintf(` 67 - select id, domain, did, created, registered, read_only 68 from registrations 69 %s 70 order by created ··· 80 for rows.Next() { 81 var createdAt string 82 var registeredAt sql.Null[string] 83 - var readOnly int 84 var reg Registration 85 86 - err = rows.Scan(&reg.Id, &reg.Domain, &reg.ByDid, &createdAt, &registeredAt, &readOnly) 87 if err != nil { 88 return nil, err 89 } ··· 98 } 99 } 100 101 - if readOnly != 0 { 102 - reg.ReadOnly = true 103 } 104 105 registrations = append(registrations, reg) ··· 116 args = append(args, filter.Arg()...) 117 } 118 119 - query := "update registrations set registered = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), read_only = 0" 120 if len(conditions) > 0 { 121 query += " where " + strings.Join(conditions, " and ") 122 }
··· 10 // Registration represents a knot registration. Knot would've been a better 11 // name but we're stuck with this for historical reasons. 12 type Registration struct { 13 + Id int64 14 + Domain string 15 + ByDid string 16 + Created *time.Time 17 + Registered *time.Time 18 + NeedsUpgrade bool 19 } 20 21 func (r *Registration) Status() Status { 22 + if r.NeedsUpgrade { 23 + return NeedsUpgrade 24 } else if r.Registered != nil { 25 return Registered 26 } else { ··· 32 return r.Status() == Registered 33 } 34 35 + func (r *Registration) IsNeedsUpgrade() bool { 36 + return r.Status() == NeedsUpgrade 37 } 38 39 func (r *Registration) IsPending() bool { ··· 45 const ( 46 Registered Status = iota 47 Pending 48 + NeedsUpgrade 49 ) 50 51 func GetRegistrations(e Execer, filters ...filter) ([]Registration, error) { ··· 64 } 65 66 query := fmt.Sprintf(` 67 + select id, domain, did, created, registered, needs_upgrade 68 from registrations 69 %s 70 order by created ··· 80 for rows.Next() { 81 var createdAt string 82 var registeredAt sql.Null[string] 83 + var needsUpgrade int 84 var reg Registration 85 86 + err = rows.Scan(&reg.Id, &reg.Domain, &reg.ByDid, &createdAt, &registeredAt, &needsUpgrade) 87 if err != nil { 88 return nil, err 89 } ··· 98 } 99 } 100 101 + if needsUpgrade != 0 { 102 + reg.NeedsUpgrade = true 103 } 104 105 registrations = append(registrations, reg) ··· 116 args = append(args, filter.Arg()...) 117 } 118 119 + query := "update registrations set registered = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), needs_upgrade = 0" 120 if len(conditions) > 0 { 121 query += " where " + strings.Join(conditions, " and ") 122 }
+14 -7
appview/db/spindle.go
··· 10 ) 11 12 type Spindle struct { 13 - Id int 14 - Owner syntax.DID 15 - Instance string 16 - Verified *time.Time 17 - Created time.Time 18 } 19 20 type SpindleMember struct { ··· 42 } 43 44 query := fmt.Sprintf( 45 - `select id, owner, instance, verified, created 46 from spindles 47 %s 48 order by created ··· 61 var spindle Spindle 62 var createdAt string 63 var verified sql.NullString 64 65 if err := rows.Scan( 66 &spindle.Id, ··· 68 &spindle.Instance, 69 &verified, 70 &createdAt, 71 ); err != nil { 72 return nil, err 73 } ··· 86 spindle.Verified = &t 87 } 88 89 spindles = append(spindles, spindle) 90 } 91 ··· 115 whereClause = " where " + strings.Join(conditions, " and ") 116 } 117 118 - query := fmt.Sprintf(`update spindles set verified = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 119 120 res, err := e.Exec(query, args...) 121 if err != nil {
··· 10 ) 11 12 type Spindle struct { 13 + Id int 14 + Owner syntax.DID 15 + Instance string 16 + Verified *time.Time 17 + Created time.Time 18 + NeedsUpgrade bool 19 } 20 21 type SpindleMember struct { ··· 43 } 44 45 query := fmt.Sprintf( 46 + `select id, owner, instance, verified, created, needs_upgrade 47 from spindles 48 %s 49 order by created ··· 62 var spindle Spindle 63 var createdAt string 64 var verified sql.NullString 65 + var needsUpgrade int 66 67 if err := rows.Scan( 68 &spindle.Id, ··· 70 &spindle.Instance, 71 &verified, 72 &createdAt, 73 + &needsUpgrade, 74 ); err != nil { 75 return nil, err 76 } ··· 89 spindle.Verified = &t 90 } 91 92 + if needsUpgrade != 0 { 93 + spindle.NeedsUpgrade = true 94 + } 95 + 96 spindles = append(spindles, spindle) 97 } 98 ··· 122 whereClause = " where " + strings.Join(conditions, " and ") 123 } 124 125 + query := fmt.Sprintf(`update spindles set verified = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now'), needs_upgrade = 0 %s`, whereClause) 126 127 res, err := e.Exec(query, args...) 128 if err != nil {
+5 -57
appview/knots/knots.go
··· 3 import ( 4 "errors" 5 "fmt" 6 - "log" 7 "log/slog" 8 "net/http" 9 "slices" ··· 17 "tangled.sh/tangled.sh/core/appview/oauth" 18 "tangled.sh/tangled.sh/core/appview/pages" 19 "tangled.sh/tangled.sh/core/appview/serververify" 20 "tangled.sh/tangled.sh/core/eventconsumer" 21 "tangled.sh/tangled.sh/core/idresolver" 22 "tangled.sh/tangled.sh/core/rbac" ··· 49 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/retry", k.retry) 50 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/add", k.addMember) 51 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/remove", k.removeMember) 52 - 53 - r.With(middleware.AuthMiddleware(k.OAuth)).Get("/upgradeBanner", k.banner) 54 55 return r 56 } ··· 399 if err != nil { 400 l.Error("verification failed", "err", err) 401 402 - if errors.Is(err, serververify.FetchError) { 403 - k.Pages.Notice(w, noticeId, "Failed to verify knot, unable to fetch owner.") 404 return 405 } 406 ··· 420 return 421 } 422 423 - // if this knot was previously read-only, then emit a record too 424 // 425 // this is part of migrating from the old knot system to the new one 426 - if registration.ReadOnly { 427 // re-announce by registering under same rkey 428 client, err := k.OAuth.AuthorizedClient(r) 429 if err != nil { ··· 484 return 485 } 486 updatedRegistration := registrations[0] 487 - 488 - log.Println(updatedRegistration) 489 490 w.Header().Set("HX-Reswap", "outerHTML") 491 k.Pages.KnotListing(w, pages.KnotListingParams{ ··· 678 // ok 679 k.Pages.HxRefresh(w) 680 } 681 - 682 - func (k *Knots) banner(w http.ResponseWriter, r *http.Request) { 683 - user := k.OAuth.GetUser(r) 684 - l := k.Logger.With("handler", "banner") 685 - l = l.With("did", user.Did) 686 - l = l.With("handle", user.Handle) 687 - 688 - allRegistrations, err := db.GetRegistrations( 689 - k.Db, 690 - db.FilterEq("did", user.Did), 691 - ) 692 - if err != nil { 693 - l.Error("non-fatal: failed to get registrations") 694 - return 695 - } 696 - 697 - httpClient := &http.Client{Timeout: 5 * time.Second} 698 - regs404 := []db.Registration{} 699 - for _, reg := range allRegistrations { 700 - healthURL := fmt.Sprintf("http://%s/xrpc/_health", reg.Domain) 701 - 702 - fmt.Println(healthURL) 703 - 704 - req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, healthURL, nil) 705 - if err != nil { 706 - l.Error("failed to create health check request", "domain", reg.Domain, "err", err) 707 - continue 708 - } 709 - 710 - resp, err := httpClient.Do(req) 711 - if err != nil { 712 - l.Error("failed to make health check request", "domain", reg.Domain, "err", err) 713 - continue 714 - } 715 - defer resp.Body.Close() 716 - 717 - if resp.StatusCode == http.StatusNotFound { 718 - regs404 = append(regs404, reg) 719 - } 720 - } 721 - if len(regs404) == 0 { 722 - return 723 - } 724 - 725 - k.Pages.KnotBanner(w, pages.KnotBannerParams{ 726 - Registrations: regs404, 727 - }) 728 - }
··· 3 import ( 4 "errors" 5 "fmt" 6 "log/slog" 7 "net/http" 8 "slices" ··· 16 "tangled.sh/tangled.sh/core/appview/oauth" 17 "tangled.sh/tangled.sh/core/appview/pages" 18 "tangled.sh/tangled.sh/core/appview/serververify" 19 + "tangled.sh/tangled.sh/core/appview/xrpcclient" 20 "tangled.sh/tangled.sh/core/eventconsumer" 21 "tangled.sh/tangled.sh/core/idresolver" 22 "tangled.sh/tangled.sh/core/rbac" ··· 49 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/retry", k.retry) 50 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/add", k.addMember) 51 r.With(middleware.AuthMiddleware(k.OAuth)).Post("/{domain}/remove", k.removeMember) 52 53 return r 54 } ··· 397 if err != nil { 398 l.Error("verification failed", "err", err) 399 400 + if errors.Is(err, xrpcclient.ErrXrpcUnsupported) { 401 + k.Pages.Notice(w, noticeId, "Failed to verify knot, XRPC queries are unsupported on this knot, consider upgrading!") 402 return 403 } 404 ··· 418 return 419 } 420 421 + // if this knot requires upgrade, then emit a record too 422 // 423 // this is part of migrating from the old knot system to the new one 424 + if registration.NeedsUpgrade { 425 // re-announce by registering under same rkey 426 client, err := k.OAuth.AuthorizedClient(r) 427 if err != nil { ··· 482 return 483 } 484 updatedRegistration := registrations[0] 485 486 w.Header().Set("HX-Reswap", "outerHTML") 487 k.Pages.KnotListing(w, pages.KnotListingParams{ ··· 674 // ok 675 k.Pages.HxRefresh(w) 676 }
+4 -3
appview/pages/pages.go
··· 351 return p.execute("user/settings/emails", w, params) 352 } 353 354 - type KnotBannerParams struct { 355 Registrations []db.Registration 356 } 357 358 - func (p *Pages) KnotBanner(w io.Writer, params KnotBannerParams) error { 359 - return p.executePlain("knots/fragments/bannerRequiresUpgrade", w, params) 360 } 361 362 type KnotsParams struct {
··· 351 return p.execute("user/settings/emails", w, params) 352 } 353 354 + type UpgradeBannerParams struct { 355 Registrations []db.Registration 356 + Spindles []db.Spindle 357 } 358 359 + func (p *Pages) UpgradeBanner(w io.Writer, params UpgradeBannerParams) error { 360 + return p.executePlain("banner", w, params) 361 } 362 363 type KnotsParams struct {
+23
appview/pages/templates/banner.html
···
··· 1 + {{ define "banner" }} 2 + <div class="px-6 py-2"> 3 + The following services that you administer will have to be upgraded to be compatible with the latest version of Tangled: 4 + {{ if .Registrations }} 5 + <ul class="list-disc mx-12 my-2"> 6 + {{range .Registrations}} 7 + <li>{{ .Domain }}</li> 8 + {{ end }} 9 + </ul> 10 + Repositories hosted on these knots may not be accessible until upgraded. 11 + {{ end }} 12 + 13 + {{ if .Spindles }} 14 + <ul class="list-disc mx-12 my-2"> 15 + {{range .Spindles}} 16 + <li>{{ .Instance }}</li> 17 + {{ end }} 18 + </ul> 19 + Pipelines may not be executed on these spindles until upgraded. 20 + {{ end }} 21 + <a href="https://tangled.sh/@tangled.sh/core/tree/master/docs/migrations/">Click to read the upgrade guide</a>. 22 + </div> 23 + {{ end }}
-8
appview/pages/templates/knots/fragments/bannerReadOnly.html
··· 1 - {{ define "knots/fragments/bannerReadOnly" }} 2 - <div class="w-full px-6 py-2 -z-15 bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-800 rounded-b drop-shadow-sm"> 3 - A knot ({{range $i, $r := .Registrations}}{{if ne $i 0}}, {{end}}{{ $r.Domain }}{{ end }}) 4 - that you administer is presently read-only. Consider upgrading this knot to 5 - continue creating repositories on it. 6 - <a href="https://tangled.sh/@tangled.sh/core/blob/master/docs/migrations/knot-1.7.0.md">Click to read the upgrade guide</a>. 7 - </div> 8 - {{ end }}
···
-12
appview/pages/templates/knots/fragments/bannerRequiresUpgrade.html
··· 1 - {{ define "knots/fragments/bannerRequiresUpgrade" }} 2 - <div class="px-6 py-2"> 3 - The following knots that you administer will have to be upgraded to be compatible with the latest version of Tangled: 4 - <ul class="list-disc mx-12 my-2"> 5 - {{range $i, $r := .Registrations}} 6 - <li>{{ $r.Domain }}</li> 7 - {{ end }} 8 - </ul> 9 - Repositories hosted on these knots will not be accessible until upgraded. 10 - <a href="https://tangled.sh/@tangled.sh/core/blob/master/docs/migrations/knot-1.8.0.md">Click to read the upgrade guide</a>. 11 - </div> 12 - {{ end }}
···
+2 -2
appview/pages/templates/knots/fragments/knotListing.html
··· 36 </span> 37 {{ template "knots/fragments/addMemberModal" . }} 38 {{ block "knotDeleteButton" . }} {{ end }} 39 - {{ else if .IsReadOnly }} 40 <span class="bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 {{$style}}"> 41 - {{ i "shield-alert" "w-4 h-4" }} read-only 42 </span> 43 {{ block "knotRetryButton" . }} {{ end }} 44 {{ block "knotDeleteButton" . }} {{ end }}
··· 36 </span> 37 {{ template "knots/fragments/addMemberModal" . }} 38 {{ block "knotDeleteButton" . }} {{ end }} 39 + {{ else if .IsNeedsUpgrade }} 40 <span class="bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 {{$style}}"> 41 + {{ i "shield-alert" "w-4 h-4" }} needs upgrade 42 </span> 43 {{ block "knotRetryButton" . }} {{ end }} 44 {{ block "knotDeleteButton" . }} {{ end }}
+1 -1
appview/pages/templates/layouts/base.html
··· 53 {{ if .LoggedInUser }} 54 <div id="upgrade-banner" 55 class="z-50 fixed bottom-0 left-0 right-0 w-full flex justify-center bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-800 rounded-b drop-shadow-sm" 56 - hx-get="/knots/upgradeBanner" 57 hx-trigger="load" 58 hx-swap="innerHTML"> 59 </div>
··· 53 {{ if .LoggedInUser }} 54 <div id="upgrade-banner" 55 class="z-50 fixed bottom-0 left-0 right-0 w-full flex justify-center bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-800 rounded-b drop-shadow-sm" 56 + hx-get="/banner" 57 hx-trigger="load" 58 hx-swap="innerHTML"> 59 </div>
+11 -27
appview/serververify/verify.go
··· 4 "context" 5 "errors" 6 "fmt" 7 - "io" 8 - "net/http" 9 - "strings" 10 - "time" 11 12 "tangled.sh/tangled.sh/core/appview/db" 13 "tangled.sh/tangled.sh/core/rbac" 14 ) 15 ··· 24 scheme = "http" 25 } 26 27 - url := fmt.Sprintf("%s://%s/owner", scheme, domain) 28 - req, err := http.NewRequest("GET", url, nil) 29 - if err != nil { 30 - return "", err 31 - } 32 - 33 - client := &http.Client{ 34 - Timeout: 1 * time.Second, 35 - } 36 - 37 - resp, err := client.Do(req.WithContext(ctx)) 38 - if err != nil || resp.StatusCode != 200 { 39 - return "", fmt.Errorf("failed to fetch /owner") 40 } 41 42 - body, err := io.ReadAll(io.LimitReader(resp.Body, 1024)) // read atmost 1kb of data 43 - if err != nil { 44 - return "", fmt.Errorf("failed to read /owner response: %w", err) 45 } 46 47 - did := strings.TrimSpace(string(body)) 48 - if did == "" { 49 - return "", fmt.Errorf("empty DID in /owner response") 50 - } 51 - 52 - return did, nil 53 } 54 55 type OwnerMismatch struct { ··· 65 func RunVerification(ctx context.Context, domain, expectedOwner string, dev bool) error { 66 observedOwner, err := fetchOwner(ctx, domain, dev) 67 if err != nil { 68 - return fmt.Errorf("%w: %w", FetchError, err) 69 } 70 71 if observedOwner != expectedOwner {
··· 4 "context" 5 "errors" 6 "fmt" 7 8 + indigoxrpc "github.com/bluesky-social/indigo/xrpc" 9 + "tangled.sh/tangled.sh/core/api/tangled" 10 "tangled.sh/tangled.sh/core/appview/db" 11 + "tangled.sh/tangled.sh/core/appview/xrpcclient" 12 "tangled.sh/tangled.sh/core/rbac" 13 ) 14 ··· 23 scheme = "http" 24 } 25 26 + host := fmt.Sprintf("%s://%s", scheme, domain) 27 + xrpcc := &indigoxrpc.Client{ 28 + Host: host, 29 } 30 31 + res, err := tangled.Owner(ctx, xrpcc) 32 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 33 + return "", xrpcerr 34 } 35 36 + return res.Owner, nil 37 } 38 39 type OwnerMismatch struct { ··· 49 func RunVerification(ctx context.Context, domain, expectedOwner string, dev bool) error { 50 observedOwner, err := fetchOwner(ctx, domain, dev) 51 if err != nil { 52 + return err 53 } 54 55 if observedOwner != expectedOwner {
+4 -3
appview/spindles/spindles.go
··· 16 "tangled.sh/tangled.sh/core/appview/oauth" 17 "tangled.sh/tangled.sh/core/appview/pages" 18 "tangled.sh/tangled.sh/core/appview/serververify" 19 "tangled.sh/tangled.sh/core/idresolver" 20 "tangled.sh/tangled.sh/core/rbac" 21 "tangled.sh/tangled.sh/core/tid" ··· 404 if err != nil { 405 l.Error("verification failed", "err", err) 406 407 - if errors.Is(err, serververify.FetchError) { 408 - s.Pages.Notice(w, noticeId, "Failed to verify knot, unable to fetch owner.") 409 return 410 } 411 ··· 442 } 443 444 w.Header().Set("HX-Reswap", "outerHTML") 445 - s.Pages.SpindleListing(w, pages.SpindleListingParams{verifiedSpindle[0]}) 446 } 447 448 func (s *Spindles) addMember(w http.ResponseWriter, r *http.Request) {
··· 16 "tangled.sh/tangled.sh/core/appview/oauth" 17 "tangled.sh/tangled.sh/core/appview/pages" 18 "tangled.sh/tangled.sh/core/appview/serververify" 19 + "tangled.sh/tangled.sh/core/appview/xrpcclient" 20 "tangled.sh/tangled.sh/core/idresolver" 21 "tangled.sh/tangled.sh/core/rbac" 22 "tangled.sh/tangled.sh/core/tid" ··· 405 if err != nil { 406 l.Error("verification failed", "err", err) 407 408 + if errors.Is(err, xrpcclient.ErrXrpcUnsupported) { 409 + s.Pages.Notice(w, noticeId, "Failed to verify spindle, XRPC queries are unsupported on this spindle, consider upgrading!") 410 return 411 } 412 ··· 443 } 444 445 w.Header().Set("HX-Reswap", "outerHTML") 446 + s.Pages.SpindleListing(w, pages.SpindleListingParams{Spindle: verifiedSpindle[0]}) 447 } 448 449 func (s *Spindles) addMember(w http.ResponseWriter, r *http.Request) {
+1
appview/state/router.go
··· 113 114 r.Get("/", s.HomeOrTimeline) 115 r.Get("/timeline", s.Timeline) 116 117 r.Route("/repo", func(r chi.Router) { 118 r.Route("/new", func(r chi.Router) {
··· 113 114 r.Get("/", s.HomeOrTimeline) 115 r.Get("/timeline", s.Timeline) 116 + r.With(middleware.AuthMiddleware(s.oauth)).Get("/upgradeBanner", s.UpgradeBanner) 117 118 r.Route("/repo", func(r chi.Router) { 119 r.Route("/new", func(r chi.Router) {
+37 -1
appview/state/state.go
··· 228 }) 229 } 230 231 func (s *State) Home(w http.ResponseWriter, r *http.Request) { 232 timeline, err := db.MakeTimeline(s.db, 5) 233 if err != nil { ··· 278 279 for _, k := range pubKeys { 280 key := strings.TrimRight(k.Key, "\n") 281 - w.Write([]byte(fmt.Sprintln(key))) 282 } 283 } 284
··· 228 }) 229 } 230 231 + func (s *State) UpgradeBanner(w http.ResponseWriter, r *http.Request) { 232 + user := s.oauth.GetUser(r) 233 + l := s.logger.With("handler", "UpgradeBanner") 234 + l = l.With("did", user.Did) 235 + l = l.With("handle", user.Handle) 236 + 237 + regs, err := db.GetRegistrations( 238 + s.db, 239 + db.FilterEq("did", user.Did), 240 + db.FilterEq("needs_upgrade", 1), 241 + ) 242 + if err != nil { 243 + l.Error("non-fatal: failed to get registrations") 244 + return 245 + } 246 + 247 + spindles, err := db.GetSpindles( 248 + s.db, 249 + db.FilterEq("did", user.Did), 250 + db.FilterEq("needs_upgrade", 1), 251 + ) 252 + if err != nil { 253 + l.Error("non-fatal: failed to get spindles") 254 + return 255 + } 256 + 257 + if regs == nil && spindles == nil { 258 + return 259 + } 260 + 261 + s.pages.UpgradeBanner(w, pages.UpgradeBannerParams{ 262 + Registrations: regs, 263 + Spindles: spindles, 264 + }) 265 + } 266 + 267 func (s *State) Home(w http.ResponseWriter, r *http.Request) { 268 timeline, err := db.MakeTimeline(s.db, 5) 269 if err != nil { ··· 314 315 for _, k := range pubKeys { 316 key := strings.TrimRight(k.Key, "\n") 317 + fmt.Fprintln(w, key) 318 } 319 } 320
-7
knotserver/router.go
··· 72 w.Write([]byte("This is a knot server. More info at https://tangled.sh")) 73 }) 74 75 - owner := func(w http.ResponseWriter, r *http.Request) { 76 - w.Write([]byte(h.c.Server.Owner)) 77 - } 78 - // Deprecated: the sh.tangled.knot.owner xrpc call should be used instead 79 - r.Get("/owner", owner) 80 - 81 r.Route("/{did}", func(r chi.Router) { 82 r.Route("/{name}", func(r chi.Router) { 83 // routes for git operations ··· 90 // xrpc apis 91 r.Route("/xrpc", func(r chi.Router) { 92 r.Get("/_health", h.Version) 93 - r.Get("/sh.tangled.knot.owner", owner) 94 r.Mount("/", h.XrpcRouter()) 95 }) 96
··· 72 w.Write([]byte("This is a knot server. More info at https://tangled.sh")) 73 }) 74 75 r.Route("/{did}", func(r chi.Router) { 76 r.Route("/{name}", func(r chi.Router) { 77 // routes for git operations ··· 84 // xrpc apis 85 r.Route("/xrpc", func(r chi.Router) { 86 r.Get("/_health", h.Version) 87 r.Mount("/", h.XrpcRouter()) 88 }) 89
+31
knotserver/xrpc/owner.go
···
··· 1 + package xrpc 2 + 3 + import ( 4 + "encoding/json" 5 + "net/http" 6 + 7 + "tangled.sh/tangled.sh/core/api/tangled" 8 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 9 + ) 10 + 11 + func (x *Xrpc) Owner(w http.ResponseWriter, r *http.Request) { 12 + owner := x.Config.Server.Owner 13 + if owner == "" { 14 + writeError(w, xrpcerr.OwnerNotFoundError, http.StatusInternalServerError) 15 + return 16 + } 17 + 18 + response := tangled.Owner_Output{ 19 + Owner: owner, 20 + } 21 + 22 + w.Header().Set("Content-Type", "application/json") 23 + if err := json.NewEncoder(w).Encode(response); err != nil { 24 + x.Logger.Error("failed to encode response", "error", err) 25 + writeError(w, xrpcerr.NewXrpcError( 26 + xrpcerr.WithTag("InternalServerError"), 27 + xrpcerr.WithMessage("failed to encode response"), 28 + ), http.StatusInternalServerError) 29 + return 30 + } 31 + }
+3
knotserver/xrpc/xrpc.go
··· 70 // knot query endpoints (no auth required) 71 r.Get("/"+tangled.KnotListKeysNSID, x.ListKeys) 72 73 return r 74 } 75
··· 70 // knot query endpoints (no auth required) 71 r.Get("/"+tangled.KnotListKeysNSID, x.ListKeys) 72 73 + // service query endpoints (no auth required) 74 + r.Get("/"+tangled.OwnerNSID, x.Owner) 75 + 76 return r 77 } 78
-3
spindle/server.go
··· 203 w.Write(motd) 204 }) 205 mux.HandleFunc("/events", s.Events) 206 - mux.HandleFunc("/owner", func(w http.ResponseWriter, r *http.Request) { 207 - w.Write([]byte(s.cfg.Server.Owner)) 208 - }) 209 mux.HandleFunc("/logs/{knot}/{rkey}/{name}", s.Logs) 210 211 mux.Mount("/xrpc", s.XrpcRouter())
··· 203 w.Write(motd) 204 }) 205 mux.HandleFunc("/events", s.Events) 206 mux.HandleFunc("/logs/{knot}/{rkey}/{name}", s.Logs) 207 208 mux.Mount("/xrpc", s.XrpcRouter())
+31
spindle/xrpc/owner.go
···
··· 1 + package xrpc 2 + 3 + import ( 4 + "encoding/json" 5 + "net/http" 6 + 7 + "tangled.sh/tangled.sh/core/api/tangled" 8 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 9 + ) 10 + 11 + func (x *Xrpc) Owner(w http.ResponseWriter, r *http.Request) { 12 + owner := x.Config.Server.Owner 13 + if owner == "" { 14 + writeError(w, xrpcerr.OwnerNotFoundError, http.StatusInternalServerError) 15 + return 16 + } 17 + 18 + response := tangled.Owner_Output{ 19 + Owner: owner, 20 + } 21 + 22 + w.Header().Set("Content-Type", "application/json") 23 + if err := json.NewEncoder(w).Encode(response); err != nil { 24 + x.Logger.Error("failed to encode response", "error", err) 25 + writeError(w, xrpcerr.NewXrpcError( 26 + xrpcerr.WithTag("InternalServerError"), 27 + xrpcerr.WithMessage("failed to encode response"), 28 + ), http.StatusInternalServerError) 29 + return 30 + } 31 + }
+10 -3
spindle/xrpc/xrpc.go
··· 35 func (x *Xrpc) Router() http.Handler { 36 r := chi.NewRouter() 37 38 - r.With(x.ServiceAuth.VerifyServiceAuth).Post("/"+tangled.RepoAddSecretNSID, x.AddSecret) 39 - r.With(x.ServiceAuth.VerifyServiceAuth).Post("/"+tangled.RepoRemoveSecretNSID, x.RemoveSecret) 40 - r.With(x.ServiceAuth.VerifyServiceAuth).Get("/"+tangled.RepoListSecretsNSID, x.ListSecrets) 41 42 return r 43 }
··· 35 func (x *Xrpc) Router() http.Handler { 36 r := chi.NewRouter() 37 38 + r.Group(func(r chi.Router) { 39 + r.Use(x.ServiceAuth.VerifyServiceAuth) 40 + 41 + r.Post("/"+tangled.RepoAddSecretNSID, x.AddSecret) 42 + r.Post("/"+tangled.RepoRemoveSecretNSID, x.RemoveSecret) 43 + r.Get("/"+tangled.RepoListSecretsNSID, x.ListSecrets) 44 + }) 45 + 46 + // service query endpoints (no auth required) 47 + r.Get("/"+tangled.OwnerNSID, x.Owner) 48 49 return r 50 }