+36
-28
appview/db/db.go
+36
-28
appview/db/db.go
···
4
"context"
5
"database/sql"
6
"fmt"
7
-
"log"
8
"reflect"
9
"strings"
10
11
_ "github.com/mattn/go-sqlite3"
12
)
13
14
type DB struct {
15
*sql.DB
16
}
17
18
type Execer interface {
···
26
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
27
}
28
29
-
func Make(dbPath string) (*DB, error) {
30
// https://github.com/mattn/go-sqlite3#connection-string
31
opts := []string{
32
"_foreign_keys=1",
···
35
"_auto_vacuum=incremental",
36
}
37
38
db, err := sql.Open("sqlite3", dbPath+"?"+strings.Join(opts, "&"))
39
if err != nil {
40
return nil, err
41
}
42
-
43
-
ctx := context.Background()
44
45
conn, err := db.Conn(ctx)
46
if err != nil {
···
574
}
575
576
// run migrations
577
-
runMigration(conn, "add-description-to-repos", func(tx *sql.Tx) error {
578
tx.Exec(`
579
alter table repos add column description text check (length(description) <= 200);
580
`)
581
return nil
582
})
583
584
-
runMigration(conn, "add-rkey-to-pubkeys", func(tx *sql.Tx) error {
585
// add unconstrained column
586
_, err := tx.Exec(`
587
alter table public_keys
···
604
return nil
605
})
606
607
-
runMigration(conn, "add-rkey-to-comments", func(tx *sql.Tx) error {
608
_, err := tx.Exec(`
609
alter table comments drop column comment_at;
610
alter table comments add column rkey text;
···
612
return err
613
})
614
615
-
runMigration(conn, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error {
616
_, err := tx.Exec(`
617
alter table comments add column deleted text; -- timestamp
618
alter table comments add column edited text; -- timestamp
···
620
return err
621
})
622
623
-
runMigration(conn, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error {
624
_, err := tx.Exec(`
625
alter table pulls add column source_branch text;
626
alter table pulls add column source_repo_at text;
···
629
return err
630
})
631
632
-
runMigration(conn, "add-source-to-repos", func(tx *sql.Tx) error {
633
_, err := tx.Exec(`
634
alter table repos add column source text;
635
`)
···
641
//
642
// [0]: https://sqlite.org/pragma.html#pragma_foreign_keys
643
conn.ExecContext(ctx, "pragma foreign_keys = off;")
644
-
runMigration(conn, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error {
645
_, err := tx.Exec(`
646
create table pulls_new (
647
-- identifiers
···
698
})
699
conn.ExecContext(ctx, "pragma foreign_keys = on;")
700
701
-
runMigration(conn, "add-spindle-to-repos", func(tx *sql.Tx) error {
702
tx.Exec(`
703
alter table repos add column spindle text;
704
`)
···
708
// drop all knot secrets, add unique constraint to knots
709
//
710
// knots will henceforth use service auth for signed requests
711
-
runMigration(conn, "no-more-secrets", func(tx *sql.Tx) error {
712
_, err := tx.Exec(`
713
create table registrations_new (
714
id integer primary key autoincrement,
···
731
})
732
733
// recreate and add rkey + created columns with default constraint
734
-
runMigration(conn, "rework-collaborators-table", func(tx *sql.Tx) error {
735
// create new table
736
// - repo_at instead of repo integer
737
// - rkey field
···
785
return err
786
})
787
788
-
runMigration(conn, "add-rkey-to-issues", func(tx *sql.Tx) error {
789
_, err := tx.Exec(`
790
alter table issues add column rkey text not null default '';
791
···
797
})
798
799
// repurpose the read-only column to "needs-upgrade"
800
-
runMigration(conn, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error {
801
_, err := tx.Exec(`
802
alter table registrations rename column read_only to needs_upgrade;
803
`)
···
805
})
806
807
// require all knots to upgrade after the release of total xrpc
808
-
runMigration(conn, "migrate-knots-to-total-xrpc", func(tx *sql.Tx) error {
809
_, err := tx.Exec(`
810
update registrations set needs_upgrade = 1;
811
`)
···
813
})
814
815
// require all knots to upgrade after the release of total xrpc
816
-
runMigration(conn, "migrate-spindles-to-xrpc-owner", func(tx *sql.Tx) error {
817
_, err := tx.Exec(`
818
alter table spindles add column needs_upgrade integer not null default 0;
819
`)
···
831
//
832
// disable foreign-keys for the next migration
833
conn.ExecContext(ctx, "pragma foreign_keys = off;")
834
-
runMigration(conn, "remove-issue-at-from-issues", func(tx *sql.Tx) error {
835
_, err := tx.Exec(`
836
create table if not exists issues_new (
837
-- identifiers
···
901
// - new columns
902
// * column "reply_to" which can be any other comment
903
// * column "at-uri" which is a generated column
904
-
runMigration(conn, "rework-issue-comments", func(tx *sql.Tx) error {
905
_, err := tx.Exec(`
906
create table if not exists issue_comments (
907
-- identifiers
···
961
//
962
// disable foreign-keys for the next migration
963
conn.ExecContext(ctx, "pragma foreign_keys = off;")
964
-
runMigration(conn, "add-at-uri-to-pulls", func(tx *sql.Tx) error {
965
_, err := tx.Exec(`
966
create table if not exists pulls_new (
967
-- identifiers
···
1042
//
1043
// disable foreign-keys for the next migration
1044
conn.ExecContext(ctx, "pragma foreign_keys = off;")
1045
-
runMigration(conn, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error {
1046
_, err := tx.Exec(`
1047
create table if not exists pull_submissions_new (
1048
-- identifiers
···
1094
})
1095
conn.ExecContext(ctx, "pragma foreign_keys = on;")
1096
1097
-
return &DB{db}, nil
1098
}
1099
1100
type migrationFn = func(*sql.Tx) error
1101
1102
-
func runMigration(c *sql.Conn, name string, migrationFn migrationFn) error {
1103
tx, err := c.BeginTx(context.Background(), nil)
1104
if err != nil {
1105
return err
···
1116
// run migration
1117
err = migrationFn(tx)
1118
if err != nil {
1119
-
log.Printf("Failed to run migration %s: %v", name, err)
1120
return err
1121
}
1122
1123
// mark migration as complete
1124
_, err = tx.Exec("insert into migrations (name) values (?)", name)
1125
if err != nil {
1126
-
log.Printf("Failed to mark migration %s as complete: %v", name, err)
1127
return err
1128
}
1129
···
1132
return err
1133
}
1134
1135
-
log.Printf("migration %s applied successfully", name)
1136
} else {
1137
-
log.Printf("skipped migration %s, already applied", name)
1138
}
1139
1140
return nil
···
4
"context"
5
"database/sql"
6
"fmt"
7
+
"log/slog"
8
"reflect"
9
"strings"
10
11
_ "github.com/mattn/go-sqlite3"
12
+
"tangled.org/core/log"
13
)
14
15
type DB struct {
16
*sql.DB
17
+
logger *slog.Logger
18
}
19
20
type Execer interface {
···
28
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
29
}
30
31
+
func Make(ctx context.Context, dbPath string) (*DB, error) {
32
// https://github.com/mattn/go-sqlite3#connection-string
33
opts := []string{
34
"_foreign_keys=1",
···
37
"_auto_vacuum=incremental",
38
}
39
40
+
logger := log.FromContext(ctx)
41
+
logger = log.SubLogger(logger, "db")
42
+
43
db, err := sql.Open("sqlite3", dbPath+"?"+strings.Join(opts, "&"))
44
if err != nil {
45
return nil, err
46
}
47
48
conn, err := db.Conn(ctx)
49
if err != nil {
···
577
}
578
579
// run migrations
580
+
runMigration(conn, logger, "add-description-to-repos", func(tx *sql.Tx) error {
581
tx.Exec(`
582
alter table repos add column description text check (length(description) <= 200);
583
`)
584
return nil
585
})
586
587
+
runMigration(conn, logger, "add-rkey-to-pubkeys", func(tx *sql.Tx) error {
588
// add unconstrained column
589
_, err := tx.Exec(`
590
alter table public_keys
···
607
return nil
608
})
609
610
+
runMigration(conn, logger, "add-rkey-to-comments", func(tx *sql.Tx) error {
611
_, err := tx.Exec(`
612
alter table comments drop column comment_at;
613
alter table comments add column rkey text;
···
615
return err
616
})
617
618
+
runMigration(conn, logger, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error {
619
_, err := tx.Exec(`
620
alter table comments add column deleted text; -- timestamp
621
alter table comments add column edited text; -- timestamp
···
623
return err
624
})
625
626
+
runMigration(conn, logger, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error {
627
_, err := tx.Exec(`
628
alter table pulls add column source_branch text;
629
alter table pulls add column source_repo_at text;
···
632
return err
633
})
634
635
+
runMigration(conn, logger, "add-source-to-repos", func(tx *sql.Tx) error {
636
_, err := tx.Exec(`
637
alter table repos add column source text;
638
`)
···
644
//
645
// [0]: https://sqlite.org/pragma.html#pragma_foreign_keys
646
conn.ExecContext(ctx, "pragma foreign_keys = off;")
647
+
runMigration(conn, logger, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error {
648
_, err := tx.Exec(`
649
create table pulls_new (
650
-- identifiers
···
701
})
702
conn.ExecContext(ctx, "pragma foreign_keys = on;")
703
704
+
runMigration(conn, logger, "add-spindle-to-repos", func(tx *sql.Tx) error {
705
tx.Exec(`
706
alter table repos add column spindle text;
707
`)
···
711
// drop all knot secrets, add unique constraint to knots
712
//
713
// knots will henceforth use service auth for signed requests
714
+
runMigration(conn, logger, "no-more-secrets", func(tx *sql.Tx) error {
715
_, err := tx.Exec(`
716
create table registrations_new (
717
id integer primary key autoincrement,
···
734
})
735
736
// recreate and add rkey + created columns with default constraint
737
+
runMigration(conn, logger, "rework-collaborators-table", func(tx *sql.Tx) error {
738
// create new table
739
// - repo_at instead of repo integer
740
// - rkey field
···
788
return err
789
})
790
791
+
runMigration(conn, logger, "add-rkey-to-issues", func(tx *sql.Tx) error {
792
_, err := tx.Exec(`
793
alter table issues add column rkey text not null default '';
794
···
800
})
801
802
// repurpose the read-only column to "needs-upgrade"
803
+
runMigration(conn, logger, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error {
804
_, err := tx.Exec(`
805
alter table registrations rename column read_only to needs_upgrade;
806
`)
···
808
})
809
810
// require all knots to upgrade after the release of total xrpc
811
+
runMigration(conn, logger, "migrate-knots-to-total-xrpc", func(tx *sql.Tx) error {
812
_, err := tx.Exec(`
813
update registrations set needs_upgrade = 1;
814
`)
···
816
})
817
818
// require all knots to upgrade after the release of total xrpc
819
+
runMigration(conn, logger, "migrate-spindles-to-xrpc-owner", func(tx *sql.Tx) error {
820
_, err := tx.Exec(`
821
alter table spindles add column needs_upgrade integer not null default 0;
822
`)
···
834
//
835
// disable foreign-keys for the next migration
836
conn.ExecContext(ctx, "pragma foreign_keys = off;")
837
+
runMigration(conn, logger, "remove-issue-at-from-issues", func(tx *sql.Tx) error {
838
_, err := tx.Exec(`
839
create table if not exists issues_new (
840
-- identifiers
···
904
// - new columns
905
// * column "reply_to" which can be any other comment
906
// * column "at-uri" which is a generated column
907
+
runMigration(conn, logger, "rework-issue-comments", func(tx *sql.Tx) error {
908
_, err := tx.Exec(`
909
create table if not exists issue_comments (
910
-- identifiers
···
964
//
965
// disable foreign-keys for the next migration
966
conn.ExecContext(ctx, "pragma foreign_keys = off;")
967
+
runMigration(conn, logger, "add-at-uri-to-pulls", func(tx *sql.Tx) error {
968
_, err := tx.Exec(`
969
create table if not exists pulls_new (
970
-- identifiers
···
1045
//
1046
// disable foreign-keys for the next migration
1047
conn.ExecContext(ctx, "pragma foreign_keys = off;")
1048
+
runMigration(conn, logger, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error {
1049
_, err := tx.Exec(`
1050
create table if not exists pull_submissions_new (
1051
-- identifiers
···
1097
})
1098
conn.ExecContext(ctx, "pragma foreign_keys = on;")
1099
1100
+
return &DB{
1101
+
db,
1102
+
logger,
1103
+
}, nil
1104
}
1105
1106
type migrationFn = func(*sql.Tx) error
1107
1108
+
func runMigration(c *sql.Conn, logger *slog.Logger, name string, migrationFn migrationFn) error {
1109
+
logger = logger.With("migration", name)
1110
+
1111
tx, err := c.BeginTx(context.Background(), nil)
1112
if err != nil {
1113
return err
···
1124
// run migration
1125
err = migrationFn(tx)
1126
if err != nil {
1127
+
logger.Error("failed to run migration", "err", err)
1128
return err
1129
}
1130
1131
// mark migration as complete
1132
_, err = tx.Exec("insert into migrations (name) values (?)", name)
1133
if err != nil {
1134
+
logger.Error("failed to mark migration as complete", "err", err)
1135
return err
1136
}
1137
···
1140
return err
1141
}
1142
1143
+
logger.Info("migration applied successfully")
1144
} else {
1145
+
logger.Warn("skipped migration, already applied")
1146
}
1147
1148
return nil
+1
-1
appview/ingester.go
+1
-1
appview/ingester.go
+27
-26
appview/issues/issues.go
+27
-26
appview/issues/issues.go
···
5
"database/sql"
6
"errors"
7
"fmt"
8
-
"log"
9
"log/slog"
10
"net/http"
11
"slices"
···
28
"tangled.org/core/appview/reporesolver"
29
"tangled.org/core/appview/validator"
30
"tangled.org/core/idresolver"
31
-
tlog "tangled.org/core/log"
32
"tangled.org/core/tid"
33
)
34
···
53
config *config.Config,
54
notifier notify.Notifier,
55
validator *validator.Validator,
56
) *Issues {
57
return &Issues{
58
oauth: oauth,
···
62
db: db,
63
config: config,
64
notifier: notifier,
65
-
logger: tlog.New("issues"),
66
validator: validator,
67
}
68
}
···
72
user := rp.oauth.GetUser(r)
73
f, err := rp.repoResolver.Resolve(r)
74
if err != nil {
75
-
log.Println("failed to get repo and knot", err)
76
return
77
}
78
···
99
db.FilterContains("scope", tangled.RepoIssueNSID),
100
)
101
if err != nil {
102
-
log.Println("failed to fetch labels", err)
103
rp.pages.Error503(w)
104
return
105
}
···
126
user := rp.oauth.GetUser(r)
127
f, err := rp.repoResolver.Resolve(r)
128
if err != nil {
129
-
log.Println("failed to get repo and knot", err)
130
return
131
}
132
···
199
200
err = db.PutIssue(tx, newIssue)
201
if err != nil {
202
-
log.Println("failed to edit issue", err)
203
rp.pages.Notice(w, "issues", "Failed to edit issue.")
204
return
205
}
···
237
// delete from PDS
238
client, err := rp.oauth.AuthorizedClient(r)
239
if err != nil {
240
-
log.Println("failed to get authorized client", err)
241
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
242
return
243
}
···
282
283
collaborators, err := f.Collaborators(r.Context())
284
if err != nil {
285
-
log.Println("failed to fetch repo collaborators: %w", err)
286
}
287
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
288
return user.Did == collab.Did
···
296
db.FilterEq("id", issue.Id),
297
)
298
if err != nil {
299
-
log.Println("failed to close issue", err)
300
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
301
return
302
}
···
307
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
308
return
309
} else {
310
-
log.Println("user is not permitted to close issue")
311
http.Error(w, "for biden", http.StatusUnauthorized)
312
return
313
}
···
318
user := rp.oauth.GetUser(r)
319
f, err := rp.repoResolver.Resolve(r)
320
if err != nil {
321
-
log.Println("failed to get repo and knot", err)
322
return
323
}
324
···
331
332
collaborators, err := f.Collaborators(r.Context())
333
if err != nil {
334
-
log.Println("failed to fetch repo collaborators: %w", err)
335
}
336
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
337
return user.Did == collab.Did
···
344
db.FilterEq("id", issue.Id),
345
)
346
if err != nil {
347
-
log.Println("failed to reopen issue", err)
348
rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
349
return
350
}
351
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
352
return
353
} else {
354
-
log.Println("user is not the owner of the repo")
355
http.Error(w, "forbidden", http.StatusUnauthorized)
356
return
357
}
···
538
newBody := r.FormValue("body")
539
client, err := rp.oauth.AuthorizedClient(r)
540
if err != nil {
541
-
log.Println("failed to get authorized client", err)
542
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
543
return
544
}
···
551
552
_, err = db.AddIssueComment(rp.db, newComment)
553
if err != nil {
554
-
log.Println("failed to perferom update-description query", err)
555
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
556
return
557
}
···
561
// update the record on pds
562
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, comment.Rkey)
563
if err != nil {
564
-
log.Println("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey)
565
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
566
return
567
}
···
729
if comment.Rkey != "" {
730
client, err := rp.oauth.AuthorizedClient(r)
731
if err != nil {
732
-
log.Println("failed to get authorized client", err)
733
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
734
return
735
}
···
739
Rkey: comment.Rkey,
740
})
741
if err != nil {
742
-
log.Println(err)
743
}
744
}
745
···
757
}
758
759
func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) {
760
params := r.URL.Query()
761
state := params.Get("state")
762
isOpen := true
···
771
772
page, ok := r.Context().Value("page").(pagination.Page)
773
if !ok {
774
-
log.Println("failed to get page")
775
page = pagination.FirstPage()
776
}
777
778
user := rp.oauth.GetUser(r)
779
f, err := rp.repoResolver.Resolve(r)
780
if err != nil {
781
-
log.Println("failed to get repo and knot", err)
782
return
783
}
784
···
793
db.FilterEq("open", openVal),
794
)
795
if err != nil {
796
-
log.Println("failed to get issues", err)
797
rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
798
return
799
}
···
804
db.FilterContains("scope", tangled.RepoIssueNSID),
805
)
806
if err != nil {
807
-
log.Println("failed to fetch labels", err)
808
rp.pages.Error503(w)
809
return
810
}
···
901
902
err = db.PutIssue(tx, issue)
903
if err != nil {
904
-
log.Println("failed to create issue", err)
905
rp.pages.Notice(w, "issues", "Failed to create issue.")
906
return
907
}
908
909
if err = tx.Commit(); err != nil {
910
-
log.Println("failed to create issue", err)
911
rp.pages.Notice(w, "issues", "Failed to create issue.")
912
return
913
}
···
5
"database/sql"
6
"errors"
7
"fmt"
8
"log/slog"
9
"net/http"
10
"slices"
···
27
"tangled.org/core/appview/reporesolver"
28
"tangled.org/core/appview/validator"
29
"tangled.org/core/idresolver"
30
"tangled.org/core/tid"
31
)
32
···
51
config *config.Config,
52
notifier notify.Notifier,
53
validator *validator.Validator,
54
+
logger *slog.Logger,
55
) *Issues {
56
return &Issues{
57
oauth: oauth,
···
61
db: db,
62
config: config,
63
notifier: notifier,
64
+
logger: logger,
65
validator: validator,
66
}
67
}
···
71
user := rp.oauth.GetUser(r)
72
f, err := rp.repoResolver.Resolve(r)
73
if err != nil {
74
+
l.Error("failed to get repo and knot", "err", err)
75
return
76
}
77
···
98
db.FilterContains("scope", tangled.RepoIssueNSID),
99
)
100
if err != nil {
101
+
l.Error("failed to fetch labels", "err", err)
102
rp.pages.Error503(w)
103
return
104
}
···
125
user := rp.oauth.GetUser(r)
126
f, err := rp.repoResolver.Resolve(r)
127
if err != nil {
128
+
l.Error("failed to get repo and knot", "err", err)
129
return
130
}
131
···
198
199
err = db.PutIssue(tx, newIssue)
200
if err != nil {
201
+
l.Error("failed to edit issue", "err", err)
202
rp.pages.Notice(w, "issues", "Failed to edit issue.")
203
return
204
}
···
236
// delete from PDS
237
client, err := rp.oauth.AuthorizedClient(r)
238
if err != nil {
239
+
l.Error("failed to get authorized client", "err", err)
240
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
241
return
242
}
···
281
282
collaborators, err := f.Collaborators(r.Context())
283
if err != nil {
284
+
l.Error("failed to fetch repo collaborators", "err", err)
285
}
286
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
287
return user.Did == collab.Did
···
295
db.FilterEq("id", issue.Id),
296
)
297
if err != nil {
298
+
l.Error("failed to close issue", "err", err)
299
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
300
return
301
}
···
306
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
307
return
308
} else {
309
+
l.Error("user is not permitted to close issue")
310
http.Error(w, "for biden", http.StatusUnauthorized)
311
return
312
}
···
317
user := rp.oauth.GetUser(r)
318
f, err := rp.repoResolver.Resolve(r)
319
if err != nil {
320
+
l.Error("failed to get repo and knot", "err", err)
321
return
322
}
323
···
330
331
collaborators, err := f.Collaborators(r.Context())
332
if err != nil {
333
+
l.Error("failed to fetch repo collaborators", "err", err)
334
}
335
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
336
return user.Did == collab.Did
···
343
db.FilterEq("id", issue.Id),
344
)
345
if err != nil {
346
+
l.Error("failed to reopen issue", "err", err)
347
rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
348
return
349
}
350
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
351
return
352
} else {
353
+
l.Error("user is not the owner of the repo")
354
http.Error(w, "forbidden", http.StatusUnauthorized)
355
return
356
}
···
537
newBody := r.FormValue("body")
538
client, err := rp.oauth.AuthorizedClient(r)
539
if err != nil {
540
+
l.Error("failed to get authorized client", "err", err)
541
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
542
return
543
}
···
550
551
_, err = db.AddIssueComment(rp.db, newComment)
552
if err != nil {
553
+
l.Error("failed to perferom update-description query", "err", err)
554
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
555
return
556
}
···
560
// update the record on pds
561
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, comment.Rkey)
562
if err != nil {
563
+
l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey)
564
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
565
return
566
}
···
728
if comment.Rkey != "" {
729
client, err := rp.oauth.AuthorizedClient(r)
730
if err != nil {
731
+
l.Error("failed to get authorized client", "err", err)
732
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
733
return
734
}
···
738
Rkey: comment.Rkey,
739
})
740
if err != nil {
741
+
l.Error("failed to delete from PDS", "err", err)
742
}
743
}
744
···
756
}
757
758
func (rp *Issues) RepoIssues(w http.ResponseWriter, r *http.Request) {
759
+
l := rp.logger.With("handler", "RepoIssues")
760
+
761
params := r.URL.Query()
762
state := params.Get("state")
763
isOpen := true
···
772
773
page, ok := r.Context().Value("page").(pagination.Page)
774
if !ok {
775
+
l.Error("failed to get page")
776
page = pagination.FirstPage()
777
}
778
779
user := rp.oauth.GetUser(r)
780
f, err := rp.repoResolver.Resolve(r)
781
if err != nil {
782
+
l.Error("failed to get repo and knot", "err", err)
783
return
784
}
785
···
794
db.FilterEq("open", openVal),
795
)
796
if err != nil {
797
+
l.Error("failed to get issues", "err", err)
798
rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
799
return
800
}
···
805
db.FilterContains("scope", tangled.RepoIssueNSID),
806
)
807
if err != nil {
808
+
l.Error("failed to fetch labels", "err", err)
809
rp.pages.Error503(w)
810
return
811
}
···
902
903
err = db.PutIssue(tx, issue)
904
if err != nil {
905
+
l.Error("failed to create issue", "err", err)
906
rp.pages.Notice(w, "issues", "Failed to create issue.")
907
return
908
}
909
910
if err = tx.Commit(); err != nil {
911
+
l.Error("failed to create issue", "err", err)
912
rp.pages.Notice(w, "issues", "Failed to create issue.")
913
return
914
}
+1
-3
appview/labels/labels.go
+1
-3
appview/labels/labels.go
···
16
"tangled.org/core/appview/oauth"
17
"tangled.org/core/appview/pages"
18
"tangled.org/core/appview/validator"
19
-
"tangled.org/core/log"
20
"tangled.org/core/rbac"
21
"tangled.org/core/tid"
22
···
42
db *db.DB,
43
validator *validator.Validator,
44
enforcer *rbac.Enforcer,
45
) *Labels {
46
-
logger := log.New("labels")
47
-
48
return &Labels{
49
oauth: oauth,
50
pages: pages,
···
16
"tangled.org/core/appview/oauth"
17
"tangled.org/core/appview/pages"
18
"tangled.org/core/appview/validator"
19
"tangled.org/core/rbac"
20
"tangled.org/core/tid"
21
···
41
db *db.DB,
42
validator *validator.Validator,
43
enforcer *rbac.Enforcer,
44
+
logger *slog.Logger,
45
) *Labels {
46
return &Labels{
47
oauth: oauth,
48
pages: pages,
+15
-12
appview/notifications/notifications.go
+15
-12
appview/notifications/notifications.go
···
1
package notifications
2
3
import (
4
-
"log"
5
"net/http"
6
"strconv"
7
···
14
)
15
16
type Notifications struct {
17
-
db *db.DB
18
-
oauth *oauth.OAuth
19
-
pages *pages.Pages
20
}
21
22
-
func New(database *db.DB, oauthHandler *oauth.OAuth, pagesHandler *pages.Pages) *Notifications {
23
return &Notifications{
24
-
db: database,
25
-
oauth: oauthHandler,
26
-
pages: pagesHandler,
27
}
28
}
29
···
44
}
45
46
func (n *Notifications) notificationsPage(w http.ResponseWriter, r *http.Request) {
47
user := n.oauth.GetUser(r)
48
49
page, ok := r.Context().Value("page").(pagination.Page)
50
if !ok {
51
-
log.Println("failed to get page")
52
page = pagination.FirstPage()
53
}
54
···
57
db.FilterEq("recipient_did", user.Did),
58
)
59
if err != nil {
60
-
log.Println("failed to get total notifications:", err)
61
n.pages.Error500(w)
62
return
63
}
···
68
db.FilterEq("recipient_did", user.Did),
69
)
70
if err != nil {
71
-
log.Println("failed to get notifications:", err)
72
n.pages.Error500(w)
73
return
74
}
75
76
err = n.db.MarkAllNotificationsRead(r.Context(), user.Did)
77
if err != nil {
78
-
log.Println("failed to mark notifications as read:", err)
79
}
80
81
unreadCount := 0
···
1
package notifications
2
3
import (
4
+
"log/slog"
5
"net/http"
6
"strconv"
7
···
14
)
15
16
type Notifications struct {
17
+
db *db.DB
18
+
oauth *oauth.OAuth
19
+
pages *pages.Pages
20
+
logger *slog.Logger
21
}
22
23
+
func New(database *db.DB, oauthHandler *oauth.OAuth, pagesHandler *pages.Pages, logger *slog.Logger) *Notifications {
24
return &Notifications{
25
+
db: database,
26
+
oauth: oauthHandler,
27
+
pages: pagesHandler,
28
+
logger: logger,
29
}
30
}
31
···
46
}
47
48
func (n *Notifications) notificationsPage(w http.ResponseWriter, r *http.Request) {
49
+
l := n.logger.With("handler", "notificationsPage")
50
user := n.oauth.GetUser(r)
51
52
page, ok := r.Context().Value("page").(pagination.Page)
53
if !ok {
54
+
l.Error("failed to get page")
55
page = pagination.FirstPage()
56
}
57
···
60
db.FilterEq("recipient_did", user.Did),
61
)
62
if err != nil {
63
+
l.Error("failed to get total notifications", "err", err)
64
n.pages.Error500(w)
65
return
66
}
···
71
db.FilterEq("recipient_did", user.Did),
72
)
73
if err != nil {
74
+
l.Error("failed to get notifications", "err", err)
75
n.pages.Error500(w)
76
return
77
}
78
79
err = n.db.MarkAllNotificationsRead(r.Context(), user.Did)
80
if err != nil {
81
+
l.Error("failed to mark notifications as read", "err", err)
82
}
83
84
unreadCount := 0
+2
-2
appview/pages/pages.go
+2
-2
appview/pages/pages.go
···
54
logger *slog.Logger
55
}
56
57
-
func NewPages(config *config.Config, res *idresolver.Resolver) *Pages {
58
// initialized with safe defaults, can be overriden per use
59
rctx := &markup.RenderContext{
60
IsDev: config.Core.Dev,
···
72
rctx: rctx,
73
resolver: res,
74
templateDir: "appview/pages",
75
-
logger: slog.Default().With("component", "pages"),
76
}
77
78
if p.dev {
···
54
logger *slog.Logger
55
}
56
57
+
func NewPages(config *config.Config, res *idresolver.Resolver, logger *slog.Logger) *Pages {
58
// initialized with safe defaults, can be overriden per use
59
rctx := &markup.RenderContext{
60
IsDev: config.Core.Dev,
···
72
rctx: rctx,
73
resolver: res,
74
templateDir: "appview/pages",
75
+
logger: logger,
76
}
77
78
if p.dev {
+1
-3
appview/pipelines/pipelines.go
+1
-3
appview/pipelines/pipelines.go
···
16
"tangled.org/core/appview/reporesolver"
17
"tangled.org/core/eventconsumer"
18
"tangled.org/core/idresolver"
19
-
"tangled.org/core/log"
20
"tangled.org/core/rbac"
21
spindlemodel "tangled.org/core/spindle/models"
22
···
45
db *db.DB,
46
config *config.Config,
47
enforcer *rbac.Enforcer,
48
) *Pipelines {
49
-
logger := log.New("pipelines")
50
-
51
return &Pipelines{
52
oauth: oauth,
53
repoResolver: repoResolver,
···
16
"tangled.org/core/appview/reporesolver"
17
"tangled.org/core/eventconsumer"
18
"tangled.org/core/idresolver"
19
"tangled.org/core/rbac"
20
spindlemodel "tangled.org/core/spindle/models"
21
···
44
db *db.DB,
45
config *config.Config,
46
enforcer *rbac.Enforcer,
47
+
logger *slog.Logger,
48
) *Pipelines {
49
return &Pipelines{
50
oauth: oauth,
51
repoResolver: repoResolver,
+4
appview/pulls/pulls.go
+4
appview/pulls/pulls.go
···
6
"errors"
7
"fmt"
8
"log"
9
"net/http"
10
"slices"
11
"sort"
···
46
config *config.Config
47
notifier notify.Notifier
48
enforcer *rbac.Enforcer
49
}
50
51
func New(
···
57
config *config.Config,
58
notifier notify.Notifier,
59
enforcer *rbac.Enforcer,
60
) *Pulls {
61
return &Pulls{
62
oauth: oauth,
···
67
config: config,
68
notifier: notifier,
69
enforcer: enforcer,
70
}
71
}
72
···
6
"errors"
7
"fmt"
8
"log"
9
+
"log/slog"
10
"net/http"
11
"slices"
12
"sort"
···
47
config *config.Config
48
notifier notify.Notifier
49
enforcer *rbac.Enforcer
50
+
logger *slog.Logger
51
}
52
53
func New(
···
59
config *config.Config,
60
notifier notify.Notifier,
61
enforcer *rbac.Enforcer,
62
+
logger *slog.Logger,
63
) *Pulls {
64
return &Pulls{
65
oauth: oauth,
···
70
config: config,
71
notifier: notifier,
72
enforcer: enforcer,
73
+
logger: logger,
74
}
75
}
76
+14
-11
appview/repo/index.go
+14
-11
appview/repo/index.go
···
3
import (
4
"errors"
5
"fmt"
6
-
"log"
7
"net/http"
8
"net/url"
9
"slices"
···
31
)
32
33
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
34
ref := chi.URLParam(r, "ref")
35
ref, _ = url.PathUnescape(ref)
36
37
f, err := rp.repoResolver.Resolve(r)
38
if err != nil {
39
-
log.Println("failed to fully resolve repo", err)
40
return
41
}
42
···
56
result, err := rp.buildIndexResponse(r.Context(), xrpcc, f, ref)
57
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
58
if errors.Is(xrpcerr, xrpcclient.ErrXrpcUnsupported) {
59
-
log.Println("failed to call XRPC repo.index", err)
60
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
61
LoggedInUser: user,
62
NeedsKnotUpgrade: true,
···
66
}
67
68
rp.pages.Error503(w)
69
-
log.Println("failed to build index response", err)
70
return
71
}
72
···
119
emails := uniqueEmails(commitsTrunc)
120
emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true)
121
if err != nil {
122
-
log.Println("failed to get email to did map", err)
123
}
124
125
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc)
126
if err != nil {
127
-
log.Println(err)
128
}
129
130
// TODO: a bit dirty
131
-
languageInfo, err := rp.getLanguageInfo(r.Context(), f, xrpcc, result.Ref, ref == "")
132
if err != nil {
133
-
log.Printf("failed to compute language percentages: %s", err)
134
// non-fatal
135
}
136
···
140
}
141
pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas)
142
if err != nil {
143
-
log.Printf("failed to fetch pipeline statuses: %s", err)
144
// non-fatal
145
}
146
···
162
163
func (rp *Repo) getLanguageInfo(
164
ctx context.Context,
165
f *reporesolver.ResolvedRepo,
166
xrpcc *indigoxrpc.Client,
167
currentRef string,
···
180
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
181
if err != nil {
182
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
183
-
log.Println("failed to call XRPC repo.languages", xrpcerr)
184
return nil, xrpcerr
185
}
186
return nil, err
···
210
err = db.UpdateRepoLanguages(tx, f.RepoAt(), currentRef, langs)
211
if err != nil {
212
// non-fatal
213
-
log.Println("failed to cache lang results", err)
214
}
215
216
err = tx.Commit()
···
3
import (
4
"errors"
5
"fmt"
6
+
"log/slog"
7
"net/http"
8
"net/url"
9
"slices"
···
31
)
32
33
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
34
+
l := rp.logger.With("handler", "RepoIndex")
35
+
36
ref := chi.URLParam(r, "ref")
37
ref, _ = url.PathUnescape(ref)
38
39
f, err := rp.repoResolver.Resolve(r)
40
if err != nil {
41
+
l.Error("failed to fully resolve repo", "err", err)
42
return
43
}
44
···
58
result, err := rp.buildIndexResponse(r.Context(), xrpcc, f, ref)
59
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
60
if errors.Is(xrpcerr, xrpcclient.ErrXrpcUnsupported) {
61
+
l.Error("failed to call XRPC repo.index", "err", err)
62
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
63
LoggedInUser: user,
64
NeedsKnotUpgrade: true,
···
68
}
69
70
rp.pages.Error503(w)
71
+
l.Error("failed to build index response", "err", err)
72
return
73
}
74
···
121
emails := uniqueEmails(commitsTrunc)
122
emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true)
123
if err != nil {
124
+
l.Error("failed to get email to did map", "err", err)
125
}
126
127
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc)
128
if err != nil {
129
+
l.Error("failed to GetVerifiedObjectCommits", "err", err)
130
}
131
132
// TODO: a bit dirty
133
+
languageInfo, err := rp.getLanguageInfo(r.Context(), l, f, xrpcc, result.Ref, ref == "")
134
if err != nil {
135
+
l.Warn("failed to compute language percentages", "err", err)
136
// non-fatal
137
}
138
···
142
}
143
pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas)
144
if err != nil {
145
+
l.Error("failed to fetch pipeline statuses", "err", err)
146
// non-fatal
147
}
148
···
164
165
func (rp *Repo) getLanguageInfo(
166
ctx context.Context,
167
+
l *slog.Logger,
168
f *reporesolver.ResolvedRepo,
169
xrpcc *indigoxrpc.Client,
170
currentRef string,
···
183
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
184
if err != nil {
185
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
186
+
l.Error("failed to call XRPC repo.languages", "err", xrpcerr)
187
return nil, xrpcerr
188
}
189
return nil, err
···
213
err = db.UpdateRepoLanguages(tx, f.RepoAt(), currentRef, langs)
214
if err != nil {
215
// non-fatal
216
+
l.Error("failed to cache lang results", "err", err)
217
}
218
219
err = tx.Commit()
+130
-92
appview/repo/repo.go
+130
-92
appview/repo/repo.go
···
7
"errors"
8
"fmt"
9
"io"
10
-
"log"
11
"log/slog"
12
"net/http"
13
"net/url"
···
90
}
91
92
func (rp *Repo) DownloadArchive(w http.ResponseWriter, r *http.Request) {
93
ref := chi.URLParam(r, "ref")
94
ref, _ = url.PathUnescape(ref)
95
96
f, err := rp.repoResolver.Resolve(r)
97
if err != nil {
98
-
log.Println("failed to get repo and knot", err)
99
return
100
}
101
···
111
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
112
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo)
113
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
114
-
log.Println("failed to call XRPC repo.archive", xrpcerr)
115
rp.pages.Error503(w)
116
return
117
}
···
128
}
129
130
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {
131
f, err := rp.repoResolver.Resolve(r)
132
if err != nil {
133
-
log.Println("failed to fully resolve repo", err)
134
return
135
}
136
···
165
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
166
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
167
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
168
-
log.Println("failed to call XRPC repo.log", xrpcerr)
169
rp.pages.Error503(w)
170
return
171
}
172
173
var xrpcResp types.RepoLogResponse
174
if err := json.Unmarshal(xrpcBytes, &xrpcResp); err != nil {
175
-
log.Println("failed to decode XRPC response", err)
176
rp.pages.Error503(w)
177
return
178
}
179
180
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
181
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
182
-
log.Println("failed to call XRPC repo.tags", xrpcerr)
183
rp.pages.Error503(w)
184
return
185
}
···
196
197
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
198
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
199
-
log.Println("failed to call XRPC repo.branches", xrpcerr)
200
rp.pages.Error503(w)
201
return
202
}
···
214
215
emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(xrpcResp.Commits), true)
216
if err != nil {
217
-
log.Println("failed to fetch email to did mapping", err)
218
}
219
220
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, xrpcResp.Commits)
221
if err != nil {
222
-
log.Println(err)
223
}
224
225
repoInfo := f.RepoInfo(user)
···
230
}
231
pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas)
232
if err != nil {
233
-
log.Println(err)
234
// non-fatal
235
}
236
···
246
}
247
248
func (rp *Repo) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
249
f, err := rp.repoResolver.Resolve(r)
250
if err != nil {
251
-
log.Println("failed to get repo and knot", err)
252
w.WriteHeader(http.StatusBadRequest)
253
return
254
}
···
260
}
261
262
func (rp *Repo) RepoDescription(w http.ResponseWriter, r *http.Request) {
263
f, err := rp.repoResolver.Resolve(r)
264
if err != nil {
265
-
log.Println("failed to get repo and knot", err)
266
w.WriteHeader(http.StatusBadRequest)
267
return
268
}
···
270
repoAt := f.RepoAt()
271
rkey := repoAt.RecordKey().String()
272
if rkey == "" {
273
-
log.Println("invalid aturi for repo", err)
274
w.WriteHeader(http.StatusInternalServerError)
275
return
276
}
···
287
newDescription := r.FormValue("description")
288
client, err := rp.oauth.AuthorizedClient(r)
289
if err != nil {
290
-
log.Println("failed to get client")
291
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
292
return
293
}
···
295
// optimistic update
296
err = db.UpdateDescription(rp.db, string(repoAt), newDescription)
297
if err != nil {
298
-
log.Println("failed to perferom update-description query", err)
299
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
300
return
301
}
···
324
})
325
326
if err != nil {
327
-
log.Println("failed to perferom update-description query", err)
328
// failed to get record
329
rp.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
330
return
···
341
}
342
343
func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) {
344
f, err := rp.repoResolver.Resolve(r)
345
if err != nil {
346
-
log.Println("failed to fully resolve repo", err)
347
return
348
}
349
ref := chi.URLParam(r, "ref")
···
371
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
372
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
373
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
374
-
log.Println("failed to call XRPC repo.diff", xrpcerr)
375
rp.pages.Error503(w)
376
return
377
}
378
379
var result types.RepoCommitResponse
380
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
381
-
log.Println("failed to decode XRPC response", err)
382
rp.pages.Error503(w)
383
return
384
}
385
386
emailToDidMap, err := db.GetEmailToDid(rp.db, []string{result.Diff.Commit.Committer.Email, result.Diff.Commit.Author.Email}, true)
387
if err != nil {
388
-
log.Println("failed to get email to did mapping:", err)
389
}
390
391
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.NiceDiff{*result.Diff})
392
if err != nil {
393
-
log.Println(err)
394
}
395
396
user := rp.oauth.GetUser(r)
397
repoInfo := f.RepoInfo(user)
398
pipelines, err := getPipelineStatuses(rp.db, repoInfo, []string{result.Diff.Commit.This})
399
if err != nil {
400
-
log.Println(err)
401
// non-fatal
402
}
403
var pipeline *models.Pipeline
···
417
}
418
419
func (rp *Repo) RepoTree(w http.ResponseWriter, r *http.Request) {
420
f, err := rp.repoResolver.Resolve(r)
421
if err != nil {
422
-
log.Println("failed to fully resolve repo", err)
423
return
424
}
425
···
444
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
445
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
446
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
447
-
log.Println("failed to call XRPC repo.tree", xrpcerr)
448
rp.pages.Error503(w)
449
return
450
}
···
519
}
520
521
func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) {
522
f, err := rp.repoResolver.Resolve(r)
523
if err != nil {
524
-
log.Println("failed to get repo and knot", err)
525
return
526
}
527
···
537
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
538
xrpcBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
539
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
540
-
log.Println("failed to call XRPC repo.tags", xrpcerr)
541
rp.pages.Error503(w)
542
return
543
}
544
545
var result types.RepoTagsResponse
546
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
547
-
log.Println("failed to decode XRPC response", err)
548
rp.pages.Error503(w)
549
return
550
}
551
552
artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt()))
553
if err != nil {
554
-
log.Println("failed grab artifacts", err)
555
return
556
}
557
···
588
}
589
590
func (rp *Repo) RepoBranches(w http.ResponseWriter, r *http.Request) {
591
f, err := rp.repoResolver.Resolve(r)
592
if err != nil {
593
-
log.Println("failed to get repo and knot", err)
594
return
595
}
596
···
606
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
607
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
608
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
609
-
log.Println("failed to call XRPC repo.branches", xrpcerr)
610
rp.pages.Error503(w)
611
return
612
}
613
614
var result types.RepoBranchesResponse
615
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
616
-
log.Println("failed to decode XRPC response", err)
617
rp.pages.Error503(w)
618
return
619
}
···
629
}
630
631
func (rp *Repo) DeleteBranch(w http.ResponseWriter, r *http.Request) {
632
f, err := rp.repoResolver.Resolve(r)
633
if err != nil {
634
-
log.Println("failed to get repo and knot", err)
635
return
636
}
637
638
noticeId := "delete-branch-error"
639
fail := func(msg string, err error) {
640
-
log.Println(msg, "err", err)
641
rp.pages.Notice(w, noticeId, msg)
642
}
643
···
670
fail(fmt.Sprintf("Failed to delete branch: %s", err), err)
671
return
672
}
673
-
log.Println("deleted branch from knot", "branch", branch, "repo", f.RepoAt())
674
675
rp.pages.HxRefresh(w)
676
}
677
678
func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) {
679
f, err := rp.repoResolver.Resolve(r)
680
if err != nil {
681
-
log.Println("failed to get repo and knot", err)
682
return
683
}
684
···
700
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
701
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
702
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
703
-
log.Println("failed to call XRPC repo.blob", xrpcerr)
704
rp.pages.Error503(w)
705
return
706
}
···
800
}
801
802
func (rp *Repo) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
803
f, err := rp.repoResolver.Resolve(r)
804
if err != nil {
805
-
log.Println("failed to get repo and knot", err)
806
w.WriteHeader(http.StatusBadRequest)
807
return
808
}
···
834
835
req, err := http.NewRequest("GET", blobURL, nil)
836
if err != nil {
837
-
log.Println("failed to create request", err)
838
return
839
}
840
···
846
client := &http.Client{}
847
resp, err := client.Do(req)
848
if err != nil {
849
-
log.Println("failed to reach knotserver", err)
850
rp.pages.Error503(w)
851
return
852
}
···
859
}
860
861
if resp.StatusCode != http.StatusOK {
862
-
log.Printf("knotserver returned non-OK status for raw blob %s: %d", blobURL, resp.StatusCode)
863
w.WriteHeader(resp.StatusCode)
864
_, _ = io.Copy(w, resp.Body)
865
return
···
868
contentType := resp.Header.Get("Content-Type")
869
body, err := io.ReadAll(resp.Body)
870
if err != nil {
871
-
log.Printf("error reading response body from knotserver: %v", err)
872
w.WriteHeader(http.StatusInternalServerError)
873
return
874
}
···
1443
db.FilterContains("scope", subject.Collection().String()),
1444
)
1445
if err != nil {
1446
-
log.Println("failed to fetch label defs", err)
1447
return
1448
}
1449
···
1454
1455
states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject))
1456
if err != nil {
1457
-
log.Println("failed to build label state", err)
1458
return
1459
}
1460
state := states[subject]
···
1491
db.FilterContains("scope", subject.Collection().String()),
1492
)
1493
if err != nil {
1494
-
log.Println("failed to fetch labels", err)
1495
return
1496
}
1497
···
1502
1503
states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject))
1504
if err != nil {
1505
-
log.Println("failed to build label state", err)
1506
return
1507
}
1508
state := states[subject]
···
1649
1650
func (rp *Repo) DeleteRepo(w http.ResponseWriter, r *http.Request) {
1651
user := rp.oauth.GetUser(r)
1652
1653
noticeId := "operation-error"
1654
f, err := rp.repoResolver.Resolve(r)
1655
if err != nil {
1656
-
log.Println("failed to get repo and knot", err)
1657
return
1658
}
1659
1660
// remove record from pds
1661
atpClient, err := rp.oauth.AuthorizedClient(r)
1662
if err != nil {
1663
-
log.Println("failed to get authorized client", err)
1664
return
1665
}
1666
_, err = comatproto.RepoDeleteRecord(r.Context(), atpClient, &comatproto.RepoDeleteRecord_Input{
···
1669
Rkey: f.Rkey,
1670
})
1671
if err != nil {
1672
-
log.Printf("failed to delete record: %s", err)
1673
rp.pages.Notice(w, noticeId, "Failed to delete repository from PDS.")
1674
return
1675
}
1676
-
log.Println("removed repo record ", f.RepoAt().String())
1677
1678
client, err := rp.oauth.ServiceClient(
1679
r,
···
1682
oauth.WithDev(rp.config.Core.Dev),
1683
)
1684
if err != nil {
1685
-
log.Println("failed to connect to knot server:", err)
1686
return
1687
}
1688
···
1699
rp.pages.Notice(w, noticeId, err.Error())
1700
return
1701
}
1702
-
log.Println("deleted repo from knot")
1703
1704
tx, err := rp.db.BeginTx(r.Context(), nil)
1705
if err != nil {
1706
-
log.Println("failed to start tx")
1707
w.Write(fmt.Append(nil, "failed to add collaborator: ", err))
1708
return
1709
}
···
1711
tx.Rollback()
1712
err = rp.enforcer.E.LoadPolicy()
1713
if err != nil {
1714
-
log.Println("failed to rollback policies")
1715
}
1716
}()
1717
···
1725
did := c[0]
1726
rp.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
1727
}
1728
-
log.Println("removed collaborators")
1729
1730
// remove repo RBAC
1731
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
···
1740
rp.pages.Notice(w, noticeId, "Failed to update appview")
1741
return
1742
}
1743
-
log.Println("removed repo from db")
1744
1745
err = tx.Commit()
1746
if err != nil {
1747
-
log.Println("failed to commit changes", err)
1748
http.Error(w, err.Error(), http.StatusInternalServerError)
1749
return
1750
}
1751
1752
err = rp.enforcer.E.SavePolicy()
1753
if err != nil {
1754
-
log.Println("failed to update ACLs", err)
1755
http.Error(w, err.Error(), http.StatusInternalServerError)
1756
return
1757
}
···
1760
}
1761
1762
func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
1763
f, err := rp.repoResolver.Resolve(r)
1764
if err != nil {
1765
-
log.Println("failed to get repo and knot", err)
1766
return
1767
}
1768
···
1780
oauth.WithDev(rp.config.Core.Dev),
1781
)
1782
if err != nil {
1783
-
log.Println("failed to connect to knot server:", err)
1784
rp.pages.Notice(w, noticeId, "Failed to connect to knot server.")
1785
return
1786
}
···
1794
},
1795
)
1796
if err := xrpcclient.HandleXrpcErr(xe); err != nil {
1797
-
log.Println("xrpc failed", "err", xe)
1798
rp.pages.Notice(w, noticeId, err.Error())
1799
return
1800
}
···
1809
1810
f, err := rp.repoResolver.Resolve(r)
1811
if err != nil {
1812
-
log.Println("failed to get repo and knot", err)
1813
return
1814
}
1815
1816
if f.Spindle == "" {
1817
-
log.Println("empty spindle cannot add/rm secret", err)
1818
return
1819
}
1820
···
1831
oauth.WithDev(rp.config.Core.Dev),
1832
)
1833
if err != nil {
1834
-
log.Println("failed to create spindle client", err)
1835
return
1836
}
1837
···
1917
}
1918
1919
func (rp *Repo) generalSettings(w http.ResponseWriter, r *http.Request) {
1920
f, err := rp.repoResolver.Resolve(r)
1921
user := rp.oauth.GetUser(r)
1922
···
1932
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1933
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1934
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1935
-
log.Println("failed to call XRPC repo.branches", xrpcerr)
1936
rp.pages.Error503(w)
1937
return
1938
}
1939
1940
var result types.RepoBranchesResponse
1941
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
1942
-
log.Println("failed to decode XRPC response", err)
1943
rp.pages.Error503(w)
1944
return
1945
}
1946
1947
defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", models.DefaultLabelDefs()))
1948
if err != nil {
1949
-
log.Println("failed to fetch labels", err)
1950
rp.pages.Error503(w)
1951
return
1952
}
1953
1954
labels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", f.Repo.Labels))
1955
if err != nil {
1956
-
log.Println("failed to fetch labels", err)
1957
rp.pages.Error503(w)
1958
return
1959
}
···
2001
}
2002
2003
func (rp *Repo) accessSettings(w http.ResponseWriter, r *http.Request) {
2004
f, err := rp.repoResolver.Resolve(r)
2005
user := rp.oauth.GetUser(r)
2006
2007
repoCollaborators, err := f.Collaborators(r.Context())
2008
if err != nil {
2009
-
log.Println("failed to get collaborators", err)
2010
}
2011
2012
rp.pages.RepoAccessSettings(w, pages.RepoAccessSettingsParams{
···
2019
}
2020
2021
func (rp *Repo) pipelineSettings(w http.ResponseWriter, r *http.Request) {
2022
f, err := rp.repoResolver.Resolve(r)
2023
user := rp.oauth.GetUser(r)
2024
2025
// all spindles that the repo owner is a member of
2026
spindles, err := rp.enforcer.GetSpindlesForUser(f.OwnerDid())
2027
if err != nil {
2028
-
log.Println("failed to fetch spindles", err)
2029
return
2030
}
2031
···
2038
oauth.WithExp(60),
2039
oauth.WithDev(rp.config.Core.Dev),
2040
); err != nil {
2041
-
log.Println("failed to create spindle client", err)
2042
} else if resp, err := tangled.RepoListSecrets(r.Context(), spindleClient, f.RepoAt().String()); err != nil {
2043
-
log.Println("failed to fetch secrets", err)
2044
} else {
2045
secrets = resp.Secrets
2046
}
···
2080
}
2081
2082
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
2083
ref := chi.URLParam(r, "ref")
2084
ref, _ = url.PathUnescape(ref)
2085
2086
user := rp.oauth.GetUser(r)
2087
f, err := rp.repoResolver.Resolve(r)
2088
if err != nil {
2089
-
log.Printf("failed to resolve source repo: %v", err)
2090
return
2091
}
2092
···
2130
}
2131
2132
func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) {
2133
user := rp.oauth.GetUser(r)
2134
f, err := rp.repoResolver.Resolve(r)
2135
if err != nil {
2136
-
log.Printf("failed to resolve source repo: %v", err)
2137
return
2138
}
2139
···
2184
)
2185
if err != nil {
2186
if !errors.Is(err, sql.ErrNoRows) {
2187
-
log.Println("error fetching existing repo from db", "err", err)
2188
rp.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
2189
return
2190
}
···
2299
2300
err = db.AddRepo(tx, repo)
2301
if err != nil {
2302
-
log.Println(err)
2303
rp.pages.Notice(w, "repo", "Failed to save repository information.")
2304
return
2305
}
···
2308
p, _ := securejoin.SecureJoin(user.Did, forkName)
2309
err = rp.enforcer.AddRepo(user.Did, targetKnot, p)
2310
if err != nil {
2311
-
log.Println(err)
2312
rp.pages.Notice(w, "repo", "Failed to set up repository permissions.")
2313
return
2314
}
2315
2316
err = tx.Commit()
2317
if err != nil {
2318
-
log.Println("failed to commit changes", err)
2319
http.Error(w, err.Error(), http.StatusInternalServerError)
2320
return
2321
}
2322
2323
err = rp.enforcer.E.SavePolicy()
2324
if err != nil {
2325
-
log.Println("failed to update ACLs", err)
2326
http.Error(w, err.Error(), http.StatusInternalServerError)
2327
return
2328
}
···
2358
}
2359
2360
func (rp *Repo) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
2361
user := rp.oauth.GetUser(r)
2362
f, err := rp.repoResolver.Resolve(r)
2363
if err != nil {
2364
-
log.Println("failed to get repo and knot", err)
2365
return
2366
}
2367
···
2377
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
2378
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
2379
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2380
-
log.Println("failed to call XRPC repo.branches", xrpcerr)
2381
rp.pages.Error503(w)
2382
return
2383
}
2384
2385
var branchResult types.RepoBranchesResponse
2386
if err := json.Unmarshal(branchBytes, &branchResult); err != nil {
2387
-
log.Println("failed to decode XRPC branches response", err)
2388
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2389
return
2390
}
···
2414
2415
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
2416
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2417
-
log.Println("failed to call XRPC repo.tags", xrpcerr)
2418
rp.pages.Error503(w)
2419
return
2420
}
2421
2422
var tags types.RepoTagsResponse
2423
if err := json.Unmarshal(tagBytes, &tags); err != nil {
2424
-
log.Println("failed to decode XRPC tags response", err)
2425
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2426
return
2427
}
···
2439
}
2440
2441
func (rp *Repo) RepoCompare(w http.ResponseWriter, r *http.Request) {
2442
user := rp.oauth.GetUser(r)
2443
f, err := rp.repoResolver.Resolve(r)
2444
if err != nil {
2445
-
log.Println("failed to get repo and knot", err)
2446
return
2447
}
2448
···
2469
head, _ = url.PathUnescape(head)
2470
2471
if base == "" || head == "" {
2472
-
log.Printf("invalid comparison")
2473
rp.pages.Error404(w)
2474
return
2475
}
···
2487
2488
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
2489
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2490
-
log.Println("failed to call XRPC repo.branches", xrpcerr)
2491
rp.pages.Error503(w)
2492
return
2493
}
2494
2495
var branches types.RepoBranchesResponse
2496
if err := json.Unmarshal(branchBytes, &branches); err != nil {
2497
-
log.Println("failed to decode XRPC branches response", err)
2498
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2499
return
2500
}
2501
2502
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
2503
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2504
-
log.Println("failed to call XRPC repo.tags", xrpcerr)
2505
rp.pages.Error503(w)
2506
return
2507
}
2508
2509
var tags types.RepoTagsResponse
2510
if err := json.Unmarshal(tagBytes, &tags); err != nil {
2511
-
log.Println("failed to decode XRPC tags response", err)
2512
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2513
return
2514
}
2515
2516
compareBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, base, head)
2517
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2518
-
log.Println("failed to call XRPC repo.compare", xrpcerr)
2519
rp.pages.Error503(w)
2520
return
2521
}
2522
2523
var formatPatch types.RepoFormatPatchResponse
2524
if err := json.Unmarshal(compareBytes, &formatPatch); err != nil {
2525
-
log.Println("failed to decode XRPC compare response", err)
2526
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2527
return
2528
}
···
7
"errors"
8
"fmt"
9
"io"
10
"log/slog"
11
"net/http"
12
"net/url"
···
89
}
90
91
func (rp *Repo) DownloadArchive(w http.ResponseWriter, r *http.Request) {
92
+
l := rp.logger.With("handler", "DownloadArchive")
93
+
94
ref := chi.URLParam(r, "ref")
95
ref, _ = url.PathUnescape(ref)
96
97
f, err := rp.repoResolver.Resolve(r)
98
if err != nil {
99
+
l.Error("failed to get repo and knot", "err", err)
100
return
101
}
102
···
112
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
113
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo)
114
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
115
+
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
116
rp.pages.Error503(w)
117
return
118
}
···
129
}
130
131
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {
132
+
l := rp.logger.With("handler", "RepoLog")
133
+
134
f, err := rp.repoResolver.Resolve(r)
135
if err != nil {
136
+
l.Error("failed to fully resolve repo", "err", err)
137
return
138
}
139
···
168
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
169
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
170
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
171
+
l.Error("failed to call XRPC repo.log", "err", xrpcerr)
172
rp.pages.Error503(w)
173
return
174
}
175
176
var xrpcResp types.RepoLogResponse
177
if err := json.Unmarshal(xrpcBytes, &xrpcResp); err != nil {
178
+
l.Error("failed to decode XRPC response", "err", err)
179
rp.pages.Error503(w)
180
return
181
}
182
183
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
184
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
185
+
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
186
rp.pages.Error503(w)
187
return
188
}
···
199
200
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
201
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
202
+
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
203
rp.pages.Error503(w)
204
return
205
}
···
217
218
emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(xrpcResp.Commits), true)
219
if err != nil {
220
+
l.Error("failed to fetch email to did mapping", "err", err)
221
}
222
223
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, xrpcResp.Commits)
224
if err != nil {
225
+
l.Error("failed to GetVerifiedObjectCommits", "err", err)
226
}
227
228
repoInfo := f.RepoInfo(user)
···
233
}
234
pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas)
235
if err != nil {
236
+
l.Error("failed to getPipelineStatuses", "err", err)
237
// non-fatal
238
}
239
···
249
}
250
251
func (rp *Repo) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
252
+
l := rp.logger.With("handler", "RepoDescriptionEdit")
253
+
254
f, err := rp.repoResolver.Resolve(r)
255
if err != nil {
256
+
l.Error("failed to get repo and knot", "err", err)
257
w.WriteHeader(http.StatusBadRequest)
258
return
259
}
···
265
}
266
267
func (rp *Repo) RepoDescription(w http.ResponseWriter, r *http.Request) {
268
+
l := rp.logger.With("handler", "RepoDescription")
269
+
270
f, err := rp.repoResolver.Resolve(r)
271
if err != nil {
272
+
l.Error("failed to get repo and knot", "err", err)
273
w.WriteHeader(http.StatusBadRequest)
274
return
275
}
···
277
repoAt := f.RepoAt()
278
rkey := repoAt.RecordKey().String()
279
if rkey == "" {
280
+
l.Error("invalid aturi for repo", "err", err)
281
w.WriteHeader(http.StatusInternalServerError)
282
return
283
}
···
294
newDescription := r.FormValue("description")
295
client, err := rp.oauth.AuthorizedClient(r)
296
if err != nil {
297
+
l.Error("failed to get client")
298
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
299
return
300
}
···
302
// optimistic update
303
err = db.UpdateDescription(rp.db, string(repoAt), newDescription)
304
if err != nil {
305
+
l.Error("failed to perform update-description query", "err", err)
306
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
307
return
308
}
···
331
})
332
333
if err != nil {
334
+
l.Error("failed to perferom update-description query", "err", err)
335
// failed to get record
336
rp.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
337
return
···
348
}
349
350
func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) {
351
+
l := rp.logger.With("handler", "RepoCommit")
352
+
353
f, err := rp.repoResolver.Resolve(r)
354
if err != nil {
355
+
l.Error("failed to fully resolve repo", "err", err)
356
return
357
}
358
ref := chi.URLParam(r, "ref")
···
380
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
381
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
382
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
383
+
l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
384
rp.pages.Error503(w)
385
return
386
}
387
388
var result types.RepoCommitResponse
389
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
390
+
l.Error("failed to decode XRPC response", "err", err)
391
rp.pages.Error503(w)
392
return
393
}
394
395
emailToDidMap, err := db.GetEmailToDid(rp.db, []string{result.Diff.Commit.Committer.Email, result.Diff.Commit.Author.Email}, true)
396
if err != nil {
397
+
l.Error("failed to get email to did mapping", "err", err)
398
}
399
400
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.NiceDiff{*result.Diff})
401
if err != nil {
402
+
l.Error("failed to GetVerifiedCommits", "err", err)
403
}
404
405
user := rp.oauth.GetUser(r)
406
repoInfo := f.RepoInfo(user)
407
pipelines, err := getPipelineStatuses(rp.db, repoInfo, []string{result.Diff.Commit.This})
408
if err != nil {
409
+
l.Error("failed to getPipelineStatuses", "err", err)
410
// non-fatal
411
}
412
var pipeline *models.Pipeline
···
426
}
427
428
func (rp *Repo) RepoTree(w http.ResponseWriter, r *http.Request) {
429
+
l := rp.logger.With("handler", "RepoTree")
430
+
431
f, err := rp.repoResolver.Resolve(r)
432
if err != nil {
433
+
l.Error("failed to fully resolve repo", "err", err)
434
return
435
}
436
···
455
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
456
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
457
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
458
+
l.Error("failed to call XRPC repo.tree", "err", xrpcerr)
459
rp.pages.Error503(w)
460
return
461
}
···
530
}
531
532
func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) {
533
+
l := rp.logger.With("handler", "RepoTags")
534
+
535
f, err := rp.repoResolver.Resolve(r)
536
if err != nil {
537
+
l.Error("failed to get repo and knot", "err", err)
538
return
539
}
540
···
550
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
551
xrpcBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
552
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
553
+
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
554
rp.pages.Error503(w)
555
return
556
}
557
558
var result types.RepoTagsResponse
559
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
560
+
l.Error("failed to decode XRPC response", "err", err)
561
rp.pages.Error503(w)
562
return
563
}
564
565
artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt()))
566
if err != nil {
567
+
l.Error("failed grab artifacts", "err", err)
568
return
569
}
570
···
601
}
602
603
func (rp *Repo) RepoBranches(w http.ResponseWriter, r *http.Request) {
604
+
l := rp.logger.With("handler", "RepoBranches")
605
+
606
f, err := rp.repoResolver.Resolve(r)
607
if err != nil {
608
+
l.Error("failed to get repo and knot", "err", err)
609
return
610
}
611
···
621
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
622
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
623
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
624
+
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
625
rp.pages.Error503(w)
626
return
627
}
628
629
var result types.RepoBranchesResponse
630
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
631
+
l.Error("failed to decode XRPC response", "err", err)
632
rp.pages.Error503(w)
633
return
634
}
···
644
}
645
646
func (rp *Repo) DeleteBranch(w http.ResponseWriter, r *http.Request) {
647
+
l := rp.logger.With("handler", "DeleteBranch")
648
+
649
f, err := rp.repoResolver.Resolve(r)
650
if err != nil {
651
+
l.Error("failed to get repo and knot", "err", err)
652
return
653
}
654
655
noticeId := "delete-branch-error"
656
fail := func(msg string, err error) {
657
+
l.Error(msg, "err", err)
658
rp.pages.Notice(w, noticeId, msg)
659
}
660
···
687
fail(fmt.Sprintf("Failed to delete branch: %s", err), err)
688
return
689
}
690
+
l.Error("deleted branch from knot", "branch", branch, "repo", f.RepoAt())
691
692
rp.pages.HxRefresh(w)
693
}
694
695
func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) {
696
+
l := rp.logger.With("handler", "RepoBlob")
697
+
698
f, err := rp.repoResolver.Resolve(r)
699
if err != nil {
700
+
l.Error("failed to get repo and knot", "err", err)
701
return
702
}
703
···
719
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
720
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
721
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
722
+
l.Error("failed to call XRPC repo.blob", "err", xrpcerr)
723
rp.pages.Error503(w)
724
return
725
}
···
819
}
820
821
func (rp *Repo) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
822
+
l := rp.logger.With("handler", "RepoBlobRaw")
823
+
824
f, err := rp.repoResolver.Resolve(r)
825
if err != nil {
826
+
l.Error("failed to get repo and knot", err)
827
w.WriteHeader(http.StatusBadRequest)
828
return
829
}
···
855
856
req, err := http.NewRequest("GET", blobURL, nil)
857
if err != nil {
858
+
l.Error("failed to create request", "err", err)
859
return
860
}
861
···
867
client := &http.Client{}
868
resp, err := client.Do(req)
869
if err != nil {
870
+
l.Error("failed to reach knotserver", "err", err)
871
rp.pages.Error503(w)
872
return
873
}
···
880
}
881
882
if resp.StatusCode != http.StatusOK {
883
+
l.Error("knotserver returned non-OK status for raw blob", "url", blobURL, "statuscode", resp.StatusCode)
884
w.WriteHeader(resp.StatusCode)
885
_, _ = io.Copy(w, resp.Body)
886
return
···
889
contentType := resp.Header.Get("Content-Type")
890
body, err := io.ReadAll(resp.Body)
891
if err != nil {
892
+
l.Error("error reading response body from knotserver", "err", err)
893
w.WriteHeader(http.StatusInternalServerError)
894
return
895
}
···
1464
db.FilterContains("scope", subject.Collection().String()),
1465
)
1466
if err != nil {
1467
+
l.Error("failed to fetch label defs", "err", err)
1468
return
1469
}
1470
···
1475
1476
states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject))
1477
if err != nil {
1478
+
l.Error("failed to build label state", "err", err)
1479
return
1480
}
1481
state := states[subject]
···
1512
db.FilterContains("scope", subject.Collection().String()),
1513
)
1514
if err != nil {
1515
+
l.Error("failed to fetch labels", "err", err)
1516
return
1517
}
1518
···
1523
1524
states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject))
1525
if err != nil {
1526
+
l.Error("failed to build label state", "err", err)
1527
return
1528
}
1529
state := states[subject]
···
1670
1671
func (rp *Repo) DeleteRepo(w http.ResponseWriter, r *http.Request) {
1672
user := rp.oauth.GetUser(r)
1673
+
l := rp.logger.With("handler", "DeleteRepo")
1674
1675
noticeId := "operation-error"
1676
f, err := rp.repoResolver.Resolve(r)
1677
if err != nil {
1678
+
l.Error("failed to get repo and knot", "err", err)
1679
return
1680
}
1681
1682
// remove record from pds
1683
atpClient, err := rp.oauth.AuthorizedClient(r)
1684
if err != nil {
1685
+
l.Error("failed to get authorized client", "err", err)
1686
return
1687
}
1688
_, err = comatproto.RepoDeleteRecord(r.Context(), atpClient, &comatproto.RepoDeleteRecord_Input{
···
1691
Rkey: f.Rkey,
1692
})
1693
if err != nil {
1694
+
l.Error("failed to delete record", "err", err)
1695
rp.pages.Notice(w, noticeId, "Failed to delete repository from PDS.")
1696
return
1697
}
1698
+
l.Info("removed repo record", "aturi", f.RepoAt().String())
1699
1700
client, err := rp.oauth.ServiceClient(
1701
r,
···
1704
oauth.WithDev(rp.config.Core.Dev),
1705
)
1706
if err != nil {
1707
+
l.Error("failed to connect to knot server", "err", err)
1708
return
1709
}
1710
···
1721
rp.pages.Notice(w, noticeId, err.Error())
1722
return
1723
}
1724
+
l.Info("deleted repo from knot")
1725
1726
tx, err := rp.db.BeginTx(r.Context(), nil)
1727
if err != nil {
1728
+
l.Error("failed to start tx")
1729
w.Write(fmt.Append(nil, "failed to add collaborator: ", err))
1730
return
1731
}
···
1733
tx.Rollback()
1734
err = rp.enforcer.E.LoadPolicy()
1735
if err != nil {
1736
+
l.Error("failed to rollback policies")
1737
}
1738
}()
1739
···
1747
did := c[0]
1748
rp.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
1749
}
1750
+
l.Info("removed collaborators")
1751
1752
// remove repo RBAC
1753
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
···
1762
rp.pages.Notice(w, noticeId, "Failed to update appview")
1763
return
1764
}
1765
+
l.Info("removed repo from db")
1766
1767
err = tx.Commit()
1768
if err != nil {
1769
+
l.Error("failed to commit changes", "err", err)
1770
http.Error(w, err.Error(), http.StatusInternalServerError)
1771
return
1772
}
1773
1774
err = rp.enforcer.E.SavePolicy()
1775
if err != nil {
1776
+
l.Error("failed to update ACLs", "err", err)
1777
http.Error(w, err.Error(), http.StatusInternalServerError)
1778
return
1779
}
···
1782
}
1783
1784
func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
1785
+
l := rp.logger.With("handler", "SetDefaultBranch")
1786
+
1787
f, err := rp.repoResolver.Resolve(r)
1788
if err != nil {
1789
+
l.Error("failed to get repo and knot", "err", err)
1790
return
1791
}
1792
···
1804
oauth.WithDev(rp.config.Core.Dev),
1805
)
1806
if err != nil {
1807
+
l.Error("failed to connect to knot server", "err", err)
1808
rp.pages.Notice(w, noticeId, "Failed to connect to knot server.")
1809
return
1810
}
···
1818
},
1819
)
1820
if err := xrpcclient.HandleXrpcErr(xe); err != nil {
1821
+
l.Error("xrpc failed", "err", xe)
1822
rp.pages.Notice(w, noticeId, err.Error())
1823
return
1824
}
···
1833
1834
f, err := rp.repoResolver.Resolve(r)
1835
if err != nil {
1836
+
l.Error("failed to get repo and knot", "err", err)
1837
return
1838
}
1839
1840
if f.Spindle == "" {
1841
+
l.Error("empty spindle cannot add/rm secret", "err", err)
1842
return
1843
}
1844
···
1855
oauth.WithDev(rp.config.Core.Dev),
1856
)
1857
if err != nil {
1858
+
l.Error("failed to create spindle client", "err", err)
1859
return
1860
}
1861
···
1941
}
1942
1943
func (rp *Repo) generalSettings(w http.ResponseWriter, r *http.Request) {
1944
+
l := rp.logger.With("handler", "generalSettings")
1945
+
1946
f, err := rp.repoResolver.Resolve(r)
1947
user := rp.oauth.GetUser(r)
1948
···
1958
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1959
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1960
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
1961
+
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
1962
rp.pages.Error503(w)
1963
return
1964
}
1965
1966
var result types.RepoBranchesResponse
1967
if err := json.Unmarshal(xrpcBytes, &result); err != nil {
1968
+
l.Error("failed to decode XRPC response", "err", err)
1969
rp.pages.Error503(w)
1970
return
1971
}
1972
1973
defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", models.DefaultLabelDefs()))
1974
if err != nil {
1975
+
l.Error("failed to fetch labels", "err", err)
1976
rp.pages.Error503(w)
1977
return
1978
}
1979
1980
labels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", f.Repo.Labels))
1981
if err != nil {
1982
+
l.Error("failed to fetch labels", "err", err)
1983
rp.pages.Error503(w)
1984
return
1985
}
···
2027
}
2028
2029
func (rp *Repo) accessSettings(w http.ResponseWriter, r *http.Request) {
2030
+
l := rp.logger.With("handler", "accessSettings")
2031
+
2032
f, err := rp.repoResolver.Resolve(r)
2033
user := rp.oauth.GetUser(r)
2034
2035
repoCollaborators, err := f.Collaborators(r.Context())
2036
if err != nil {
2037
+
l.Error("failed to get collaborators", "err", err)
2038
}
2039
2040
rp.pages.RepoAccessSettings(w, pages.RepoAccessSettingsParams{
···
2047
}
2048
2049
func (rp *Repo) pipelineSettings(w http.ResponseWriter, r *http.Request) {
2050
+
l := rp.logger.With("handler", "pipelineSettings")
2051
+
2052
f, err := rp.repoResolver.Resolve(r)
2053
user := rp.oauth.GetUser(r)
2054
2055
// all spindles that the repo owner is a member of
2056
spindles, err := rp.enforcer.GetSpindlesForUser(f.OwnerDid())
2057
if err != nil {
2058
+
l.Error("failed to fetch spindles", "err", err)
2059
return
2060
}
2061
···
2068
oauth.WithExp(60),
2069
oauth.WithDev(rp.config.Core.Dev),
2070
); err != nil {
2071
+
l.Error("failed to create spindle client", "err", err)
2072
} else if resp, err := tangled.RepoListSecrets(r.Context(), spindleClient, f.RepoAt().String()); err != nil {
2073
+
l.Error("failed to fetch secrets", "err", err)
2074
} else {
2075
secrets = resp.Secrets
2076
}
···
2110
}
2111
2112
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
2113
+
l := rp.logger.With("handler", "SyncRepoFork")
2114
+
2115
ref := chi.URLParam(r, "ref")
2116
ref, _ = url.PathUnescape(ref)
2117
2118
user := rp.oauth.GetUser(r)
2119
f, err := rp.repoResolver.Resolve(r)
2120
if err != nil {
2121
+
l.Error("failed to resolve source repo", "err", err)
2122
return
2123
}
2124
···
2162
}
2163
2164
func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) {
2165
+
l := rp.logger.With("handler", "ForkRepo")
2166
+
2167
user := rp.oauth.GetUser(r)
2168
f, err := rp.repoResolver.Resolve(r)
2169
if err != nil {
2170
+
l.Error("failed to resolve source repo", "err", err)
2171
return
2172
}
2173
···
2218
)
2219
if err != nil {
2220
if !errors.Is(err, sql.ErrNoRows) {
2221
+
l.Error("error fetching existing repo from db", "err", err)
2222
rp.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
2223
return
2224
}
···
2333
2334
err = db.AddRepo(tx, repo)
2335
if err != nil {
2336
+
l.Error("failed to AddRepo", "err", err)
2337
rp.pages.Notice(w, "repo", "Failed to save repository information.")
2338
return
2339
}
···
2342
p, _ := securejoin.SecureJoin(user.Did, forkName)
2343
err = rp.enforcer.AddRepo(user.Did, targetKnot, p)
2344
if err != nil {
2345
+
l.Error("failed to add ACLs", "err", err)
2346
rp.pages.Notice(w, "repo", "Failed to set up repository permissions.")
2347
return
2348
}
2349
2350
err = tx.Commit()
2351
if err != nil {
2352
+
l.Error("failed to commit changes", "err", err)
2353
http.Error(w, err.Error(), http.StatusInternalServerError)
2354
return
2355
}
2356
2357
err = rp.enforcer.E.SavePolicy()
2358
if err != nil {
2359
+
l.Error("failed to update ACLs", "err", err)
2360
http.Error(w, err.Error(), http.StatusInternalServerError)
2361
return
2362
}
···
2392
}
2393
2394
func (rp *Repo) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
2395
+
l := rp.logger.With("handler", "RepoCompareNew")
2396
+
2397
user := rp.oauth.GetUser(r)
2398
f, err := rp.repoResolver.Resolve(r)
2399
if err != nil {
2400
+
l.Error("failed to get repo and knot", "err", err)
2401
return
2402
}
2403
···
2413
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
2414
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
2415
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2416
+
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
2417
rp.pages.Error503(w)
2418
return
2419
}
2420
2421
var branchResult types.RepoBranchesResponse
2422
if err := json.Unmarshal(branchBytes, &branchResult); err != nil {
2423
+
l.Error("failed to decode XRPC branches response", "err", err)
2424
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2425
return
2426
}
···
2450
2451
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
2452
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2453
+
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
2454
rp.pages.Error503(w)
2455
return
2456
}
2457
2458
var tags types.RepoTagsResponse
2459
if err := json.Unmarshal(tagBytes, &tags); err != nil {
2460
+
l.Error("failed to decode XRPC tags response", "err", err)
2461
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2462
return
2463
}
···
2475
}
2476
2477
func (rp *Repo) RepoCompare(w http.ResponseWriter, r *http.Request) {
2478
+
l := rp.logger.With("handler", "RepoCompare")
2479
+
2480
user := rp.oauth.GetUser(r)
2481
f, err := rp.repoResolver.Resolve(r)
2482
if err != nil {
2483
+
l.Error("failed to get repo and knot", "err", err)
2484
return
2485
}
2486
···
2507
head, _ = url.PathUnescape(head)
2508
2509
if base == "" || head == "" {
2510
+
l.Error("invalid comparison")
2511
rp.pages.Error404(w)
2512
return
2513
}
···
2525
2526
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
2527
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2528
+
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
2529
rp.pages.Error503(w)
2530
return
2531
}
2532
2533
var branches types.RepoBranchesResponse
2534
if err := json.Unmarshal(branchBytes, &branches); err != nil {
2535
+
l.Error("failed to decode XRPC branches response", "err", err)
2536
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2537
return
2538
}
2539
2540
tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
2541
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2542
+
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
2543
rp.pages.Error503(w)
2544
return
2545
}
2546
2547
var tags types.RepoTagsResponse
2548
if err := json.Unmarshal(tagBytes, &tags); err != nil {
2549
+
l.Error("failed to decode XRPC tags response", "err", err)
2550
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2551
return
2552
}
2553
2554
compareBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, base, head)
2555
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
2556
+
l.Error("failed to call XRPC repo.compare", "err", xrpcerr)
2557
rp.pages.Error503(w)
2558
return
2559
}
2560
2561
var formatPatch types.RepoFormatPatchResponse
2562
if err := json.Unmarshal(compareBytes, &formatPatch); err != nil {
2563
+
l.Error("failed to decode XRPC compare response", "err", err)
2564
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2565
return
2566
}
+1
-1
appview/signup/signup.go
+1
-1
appview/signup/signup.go
+3
-1
appview/state/knotstream.go
+3
-1
appview/state/knotstream.go
···
25
)
26
27
func Knotstream(ctx context.Context, c *config.Config, d *db.DB, enforcer *rbac.Enforcer, posthog posthog.Client) (*ec.Consumer, error) {
28
knots, err := db.GetRegistrations(
29
d,
30
db.FilterIsNot("registered", "null"),
···
39
srcs[s] = struct{}{}
40
}
41
42
-
logger := log.New("knotstream")
43
cache := cache.New(c.Redis.Addr)
44
cursorStore := cursor.NewRedisCursorStore(cache)
45
···
25
)
26
27
func Knotstream(ctx context.Context, c *config.Config, d *db.DB, enforcer *rbac.Enforcer, posthog posthog.Client) (*ec.Consumer, error) {
28
+
logger := log.FromContext(ctx)
29
+
logger = log.SubLogger(logger, "knotstream")
30
+
31
knots, err := db.GetRegistrations(
32
d,
33
db.FilterIsNot("registered", "null"),
···
42
srcs[s] = struct{}{}
43
}
44
45
cache := cache.New(c.Redis.Addr)
46
cursorStore := cursor.NewRedisCursorStore(cache)
47
+7
-4
appview/state/login.go
+7
-4
appview/state/login.go
···
2
3
import (
4
"fmt"
5
-
"log"
6
"net/http"
7
"strings"
8
···
10
)
11
12
func (s *State) Login(w http.ResponseWriter, r *http.Request) {
13
switch r.Method {
14
case http.MethodGet:
15
returnURL := r.URL.Query().Get("return_url")
···
32
33
// basic handle validation
34
if !strings.Contains(handle, ".") {
35
-
log.Println("invalid handle format", "raw", handle)
36
s.pages.Notice(
37
w,
38
"login-msg",
···
52
}
53
54
func (s *State) Logout(w http.ResponseWriter, r *http.Request) {
55
err := s.oauth.DeleteSession(w, r)
56
if err != nil {
57
-
log.Println("failed to logout", "err", err)
58
} else {
59
-
log.Println("logged out successfully")
60
}
61
62
s.pages.HxRedirect(w, "/login")
···
2
3
import (
4
"fmt"
5
"net/http"
6
"strings"
7
···
9
)
10
11
func (s *State) Login(w http.ResponseWriter, r *http.Request) {
12
+
l := s.logger.With("handler", "Login")
13
+
14
switch r.Method {
15
case http.MethodGet:
16
returnURL := r.URL.Query().Get("return_url")
···
33
34
// basic handle validation
35
if !strings.Contains(handle, ".") {
36
+
l.Error("invalid handle format", "raw", handle)
37
s.pages.Notice(
38
w,
39
"login-msg",
···
53
}
54
55
func (s *State) Logout(w http.ResponseWriter, r *http.Request) {
56
+
l := s.logger.With("handler", "Logout")
57
+
58
err := s.oauth.DeleteSession(w, r)
59
if err != nil {
60
+
l.Error("failed to logout", "err", err)
61
} else {
62
+
l.Info("logged out successfully")
63
}
64
65
s.pages.HxRedirect(w, "/login")
+59
-13
appview/state/router.go
+59
-13
appview/state/router.go
···
205
}
206
207
func (s *State) SpindlesRouter() http.Handler {
208
-
logger := log.New("spindles")
209
210
spindles := &spindles.Spindles{
211
Db: s.db,
···
221
}
222
223
func (s *State) KnotsRouter() http.Handler {
224
-
logger := log.New("knots")
225
226
knots := &knots.Knots{
227
Db: s.db,
···
238
}
239
240
func (s *State) StringsRouter(mw *middleware.Middleware) http.Handler {
241
-
logger := log.New("strings")
242
243
strs := &avstrings.Strings{
244
Db: s.db,
···
253
}
254
255
func (s *State) IssuesRouter(mw *middleware.Middleware) http.Handler {
256
-
issues := issues.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.notifier, s.validator)
257
return issues.Router(mw)
258
}
259
260
func (s *State) PullsRouter(mw *middleware.Middleware) http.Handler {
261
-
pulls := pulls.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.notifier, s.enforcer)
262
return pulls.Router(mw)
263
}
264
265
func (s *State) RepoRouter(mw *middleware.Middleware) http.Handler {
266
-
logger := log.New("repo")
267
-
repo := repo.New(s.oauth, s.repoResolver, s.pages, s.spindlestream, s.idResolver, s.db, s.config, s.notifier, s.enforcer, logger, s.validator)
268
return repo.Router(mw)
269
}
270
271
func (s *State) PipelinesRouter(mw *middleware.Middleware) http.Handler {
272
-
pipes := pipelines.New(s.oauth, s.repoResolver, s.pages, s.spindlestream, s.idResolver, s.db, s.config, s.enforcer)
273
return pipes.Router(mw)
274
}
275
276
func (s *State) LabelsRouter(mw *middleware.Middleware) http.Handler {
277
-
ls := labels.New(s.oauth, s.pages, s.db, s.validator, s.enforcer)
278
return ls.Router(mw)
279
}
280
281
func (s *State) NotificationsRouter(mw *middleware.Middleware) http.Handler {
282
-
notifs := notifications.New(s.db, s.oauth, s.pages)
283
return notifs.Router(mw)
284
}
285
286
func (s *State) SignupRouter() http.Handler {
287
-
logger := log.New("signup")
288
-
289
-
sig := signup.New(s.config, s.db, s.posthog, s.idResolver, s.pages, logger)
290
return sig.Router()
291
}
···
205
}
206
207
func (s *State) SpindlesRouter() http.Handler {
208
+
logger := log.SubLogger(s.logger, "spindles")
209
210
spindles := &spindles.Spindles{
211
Db: s.db,
···
221
}
222
223
func (s *State) KnotsRouter() http.Handler {
224
+
logger := log.SubLogger(s.logger, "knots")
225
226
knots := &knots.Knots{
227
Db: s.db,
···
238
}
239
240
func (s *State) StringsRouter(mw *middleware.Middleware) http.Handler {
241
+
logger := log.SubLogger(s.logger, "strings")
242
243
strs := &avstrings.Strings{
244
Db: s.db,
···
253
}
254
255
func (s *State) IssuesRouter(mw *middleware.Middleware) http.Handler {
256
+
issues := issues.New(
257
+
s.oauth,
258
+
s.repoResolver,
259
+
s.pages,
260
+
s.idResolver,
261
+
s.db,
262
+
s.config,
263
+
s.notifier,
264
+
s.validator,
265
+
log.SubLogger(s.logger, "issues"),
266
+
)
267
return issues.Router(mw)
268
}
269
270
func (s *State) PullsRouter(mw *middleware.Middleware) http.Handler {
271
+
pulls := pulls.New(
272
+
s.oauth,
273
+
s.repoResolver,
274
+
s.pages,
275
+
s.idResolver,
276
+
s.db,
277
+
s.config,
278
+
s.notifier,
279
+
s.enforcer,
280
+
log.SubLogger(s.logger, "pulls"),
281
+
)
282
return pulls.Router(mw)
283
}
284
285
func (s *State) RepoRouter(mw *middleware.Middleware) http.Handler {
286
+
repo := repo.New(
287
+
s.oauth,
288
+
s.repoResolver,
289
+
s.pages,
290
+
s.spindlestream,
291
+
s.idResolver,
292
+
s.db,
293
+
s.config,
294
+
s.notifier,
295
+
s.enforcer,
296
+
log.SubLogger(s.logger, "repo"),
297
+
s.validator,
298
+
)
299
return repo.Router(mw)
300
}
301
302
func (s *State) PipelinesRouter(mw *middleware.Middleware) http.Handler {
303
+
pipes := pipelines.New(
304
+
s.oauth,
305
+
s.repoResolver,
306
+
s.pages,
307
+
s.spindlestream,
308
+
s.idResolver,
309
+
s.db,
310
+
s.config,
311
+
s.enforcer,
312
+
log.SubLogger(s.logger, "pipelines"),
313
+
)
314
return pipes.Router(mw)
315
}
316
317
func (s *State) LabelsRouter(mw *middleware.Middleware) http.Handler {
318
+
ls := labels.New(
319
+
s.oauth,
320
+
s.pages,
321
+
s.db,
322
+
s.validator,
323
+
s.enforcer,
324
+
log.SubLogger(s.logger, "labels"),
325
+
)
326
return ls.Router(mw)
327
}
328
329
func (s *State) NotificationsRouter(mw *middleware.Middleware) http.Handler {
330
+
notifs := notifications.New(s.db, s.oauth, s.pages, log.SubLogger(s.logger, "notifications"))
331
return notifs.Router(mw)
332
}
333
334
func (s *State) SignupRouter() http.Handler {
335
+
sig := signup.New(s.config, s.db, s.posthog, s.idResolver, s.pages, log.SubLogger(s.logger, "signup"))
336
return sig.Router()
337
}
+3
-1
appview/state/spindlestream.go
+3
-1
appview/state/spindlestream.go
···
22
)
23
24
func Spindlestream(ctx context.Context, c *config.Config, d *db.DB, enforcer *rbac.Enforcer) (*ec.Consumer, error) {
25
spindles, err := db.GetSpindles(
26
d,
27
db.FilterIsNot("verified", "null"),
···
36
srcs[src] = struct{}{}
37
}
38
39
-
logger := log.New("spindlestream")
40
cache := cache.New(c.Redis.Addr)
41
cursorStore := cursor.NewRedisCursorStore(cache)
42
···
22
)
23
24
func Spindlestream(ctx context.Context, c *config.Config, d *db.DB, enforcer *rbac.Enforcer) (*ec.Consumer, error) {
25
+
logger := log.FromContext(ctx)
26
+
logger = log.SubLogger(logger, "spindlestream")
27
+
28
spindles, err := db.GetSpindles(
29
d,
30
db.FilterIsNot("verified", "null"),
···
39
srcs[src] = struct{}{}
40
}
41
42
cache := cache.New(c.Redis.Addr)
43
cursorStore := cursor.NewRedisCursorStore(cache)
44
+15
-19
appview/state/state.go
+15
-19
appview/state/state.go
···
5
"database/sql"
6
"errors"
7
"fmt"
8
-
"log"
9
"log/slog"
10
"net/http"
11
"strings"
···
13
14
"tangled.org/core/api/tangled"
15
"tangled.org/core/appview"
16
-
"tangled.org/core/appview/cache"
17
-
"tangled.org/core/appview/cache/session"
18
"tangled.org/core/appview/config"
19
"tangled.org/core/appview/db"
20
"tangled.org/core/appview/models"
···
29
"tangled.org/core/eventconsumer"
30
"tangled.org/core/idresolver"
31
"tangled.org/core/jetstream"
32
tlog "tangled.org/core/log"
33
"tangled.org/core/rbac"
34
"tangled.org/core/tid"
···
48
oauth *oauth.OAuth
49
enforcer *rbac.Enforcer
50
pages *pages.Pages
51
-
sess *session.SessionStore
52
idResolver *idresolver.Resolver
53
posthog posthog.Client
54
jc *jetstream.JetstreamClient
···
61
}
62
63
func Make(ctx context.Context, config *config.Config) (*State, error) {
64
-
d, err := db.Make(config.Core.DbPath)
65
if err != nil {
66
return nil, fmt.Errorf("failed to create db: %w", err)
67
}
···
73
74
res, err := idresolver.RedisResolver(config.Redis.ToURL())
75
if err != nil {
76
-
log.Printf("failed to create redis resolver: %v", err)
77
res = idresolver.DefaultResolver()
78
}
79
···
82
return nil, fmt.Errorf("failed to create posthog client: %w", err)
83
}
84
85
-
pages := pages.NewPages(config, res)
86
-
cache := cache.New(config.Redis.Addr)
87
-
sess := session.New(cache)
88
-
oauth2, err := oauth.New(config, posthog, d, enforcer, res)
89
if err != nil {
90
return nil, fmt.Errorf("failed to start oauth handler: %w", err)
91
}
···
112
tangled.LabelOpNSID,
113
},
114
nil,
115
-
slog.Default(),
116
wrapper,
117
false,
118
···
133
Enforcer: enforcer,
134
IdResolver: res,
135
Config: config,
136
-
Logger: tlog.New("ingester"),
137
Validator: validator,
138
}
139
err = jc.StartJetstream(ctx, ingester.Ingest())
···
167
state := &State{
168
d,
169
notifier,
170
-
oauth2,
171
enforcer,
172
pages,
173
-
sess,
174
res,
175
posthog,
176
jc,
···
178
repoResolver,
179
knotstream,
180
spindlestream,
181
-
slog.Default(),
182
validator,
183
}
184
···
277
}
278
timeline, err := db.MakeTimeline(s.db, 50, userDid, filtered)
279
if err != nil {
280
-
log.Println(err)
281
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
282
}
283
284
repos, err := db.GetTopStarredReposLastWeek(s.db)
285
if err != nil {
286
-
log.Println(err)
287
s.pages.Notice(w, "topstarredrepos", "Unable to load.")
288
return
289
}
···
344
345
timeline, err := db.MakeTimeline(s.db, 5, "", filtered)
346
if err != nil {
347
-
log.Println(err)
348
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
349
return
350
}
351
352
repos, err := db.GetTopStarredReposLastWeek(s.db)
353
if err != nil {
354
-
log.Println(err)
355
s.pages.Notice(w, "topstarredrepos", "Unable to load.")
356
return
357
}
···
5
"database/sql"
6
"errors"
7
"fmt"
8
"log/slog"
9
"net/http"
10
"strings"
···
12
13
"tangled.org/core/api/tangled"
14
"tangled.org/core/appview"
15
"tangled.org/core/appview/config"
16
"tangled.org/core/appview/db"
17
"tangled.org/core/appview/models"
···
26
"tangled.org/core/eventconsumer"
27
"tangled.org/core/idresolver"
28
"tangled.org/core/jetstream"
29
+
"tangled.org/core/log"
30
tlog "tangled.org/core/log"
31
"tangled.org/core/rbac"
32
"tangled.org/core/tid"
···
46
oauth *oauth.OAuth
47
enforcer *rbac.Enforcer
48
pages *pages.Pages
49
idResolver *idresolver.Resolver
50
posthog posthog.Client
51
jc *jetstream.JetstreamClient
···
58
}
59
60
func Make(ctx context.Context, config *config.Config) (*State, error) {
61
+
logger := tlog.FromContext(ctx)
62
+
63
+
d, err := db.Make(ctx, config.Core.DbPath)
64
if err != nil {
65
return nil, fmt.Errorf("failed to create db: %w", err)
66
}
···
72
73
res, err := idresolver.RedisResolver(config.Redis.ToURL())
74
if err != nil {
75
+
logger.Error("failed to create redis resolver", "err", err)
76
res = idresolver.DefaultResolver()
77
}
78
···
81
return nil, fmt.Errorf("failed to create posthog client: %w", err)
82
}
83
84
+
pages := pages.NewPages(config, res, log.SubLogger(logger, "pages"))
85
+
oauth, err := oauth.New(config, posthog, d, enforcer, res)
86
if err != nil {
87
return nil, fmt.Errorf("failed to start oauth handler: %w", err)
88
}
···
109
tangled.LabelOpNSID,
110
},
111
nil,
112
+
tlog.SubLogger(logger, "jetstream"),
113
wrapper,
114
false,
115
···
130
Enforcer: enforcer,
131
IdResolver: res,
132
Config: config,
133
+
Logger: log.SubLogger(logger, "ingester"),
134
Validator: validator,
135
}
136
err = jc.StartJetstream(ctx, ingester.Ingest())
···
164
state := &State{
165
d,
166
notifier,
167
+
oauth,
168
enforcer,
169
pages,
170
res,
171
posthog,
172
jc,
···
174
repoResolver,
175
knotstream,
176
spindlestream,
177
+
logger,
178
validator,
179
}
180
···
273
}
274
timeline, err := db.MakeTimeline(s.db, 50, userDid, filtered)
275
if err != nil {
276
+
s.logger.Error("failed to make timeline", "err", err)
277
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
278
}
279
280
repos, err := db.GetTopStarredReposLastWeek(s.db)
281
if err != nil {
282
+
s.logger.Error("failed to get top starred repos", "err", err)
283
s.pages.Notice(w, "topstarredrepos", "Unable to load.")
284
return
285
}
···
340
341
timeline, err := db.MakeTimeline(s.db, 5, "", filtered)
342
if err != nil {
343
+
s.logger.Error("failed to make timeline", "err", err)
344
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
345
return
346
}
347
348
repos, err := db.GetTopStarredReposLastWeek(s.db)
349
if err != nil {
350
+
s.logger.Error("failed to get top starred repos", "err", err)
351
s.pages.Notice(w, "topstarredrepos", "Unable to load.")
352
return
353
}
+14
-9
cmd/appview/main.go
+14
-9
cmd/appview/main.go
···
2
3
import (
4
"context"
5
-
"log"
6
-
"log/slog"
7
"net/http"
8
"os"
9
10
"tangled.org/core/appview/config"
11
"tangled.org/core/appview/state"
12
)
13
14
func main() {
15
-
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))
16
-
17
ctx := context.Background()
18
19
c, err := config.LoadConfig(ctx)
20
if err != nil {
21
-
log.Println("failed to load config", "error", err)
22
return
23
}
24
25
state, err := state.Make(ctx, c)
26
defer func() {
27
-
log.Println(state.Close())
28
}()
29
30
if err != nil {
31
-
log.Fatal(err)
32
}
33
34
-
log.Println("starting server on", c.Core.ListenAddr)
35
-
log.Println(http.ListenAndServe(c.Core.ListenAddr, state.Router()))
36
}
···
2
3
import (
4
"context"
5
"net/http"
6
"os"
7
8
"tangled.org/core/appview/config"
9
"tangled.org/core/appview/state"
10
+
tlog "tangled.org/core/log"
11
)
12
13
func main() {
14
ctx := context.Background()
15
+
logger := tlog.New("appview")
16
+
ctx = tlog.IntoContext(ctx, logger)
17
18
c, err := config.LoadConfig(ctx)
19
if err != nil {
20
+
logger.Error("failed to load config", "error", err)
21
return
22
}
23
24
state, err := state.Make(ctx, c)
25
defer func() {
26
+
if err := state.Close(); err != nil {
27
+
logger.Error("failed to close state", "err", err)
28
+
}
29
}()
30
31
if err != nil {
32
+
logger.Error("failed to start appview", "err", err)
33
+
os.Exit(-1)
34
}
35
36
+
logger.Info("starting server", "address", c.Core.ListenAddr)
37
+
38
+
if err := http.ListenAndServe(c.Core.ListenAddr, state.Router()); err != nil {
39
+
logger.Error("failed to start appview", "err", err)
40
+
}
41
}
+9
-3
cmd/spindle/main.go
+9
-3
cmd/spindle/main.go
···
2
3
import (
4
"context"
5
"os"
6
7
-
"tangled.org/core/log"
8
"tangled.org/core/spindle"
9
_ "tangled.org/core/tid"
10
)
11
12
func main() {
13
-
ctx := log.NewContext(context.Background(), "spindle")
14
err := spindle.Run(ctx)
15
if err != nil {
16
-
log.FromContext(ctx).Error("error running spindle", "error", err)
17
os.Exit(-1)
18
}
19
}
···
2
3
import (
4
"context"
5
+
"log/slog"
6
"os"
7
8
+
tlog "tangled.org/core/log"
9
"tangled.org/core/spindle"
10
_ "tangled.org/core/tid"
11
)
12
13
func main() {
14
+
logger := tlog.New("spindl3")
15
+
slog.SetDefault(logger)
16
+
17
+
ctx := context.Background()
18
+
ctx = tlog.IntoContext(ctx, logger)
19
+
20
err := spindle.Run(ctx)
21
if err != nil {
22
+
logger.Error("error running spindle", "error", err)
23
os.Exit(-1)
24
}
25
}
+13
go.mod
+13
go.mod
···
60
github.com/ProtonMail/go-crypto v1.3.0 // indirect
61
github.com/alecthomas/repr v0.4.0 // indirect
62
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
63
github.com/aymerick/douceur v0.2.0 // indirect
64
github.com/beorn7/perks v1.0.1 // indirect
65
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
66
github.com/casbin/govaluate v1.3.0 // indirect
67
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
68
github.com/cespare/xxhash/v2 v2.3.0 // indirect
69
github.com/cloudflare/circl v1.6.2-0.20250618153321-aa837fd1539d // indirect
70
github.com/containerd/errdefs v1.0.0 // indirect
71
github.com/containerd/errdefs/pkg v0.3.0 // indirect
···
84
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
85
github.com/go-git/go-billy/v5 v5.6.2 // indirect
86
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
87
github.com/go-logr/logr v1.4.3 // indirect
88
github.com/go-logr/stdr v1.2.2 // indirect
89
github.com/go-redis/cache/v9 v9.0.0 // indirect
···
126
github.com/lestrrat-go/httprc v1.0.6 // indirect
127
github.com/lestrrat-go/iter v1.0.2 // indirect
128
github.com/lestrrat-go/option v1.0.1 // indirect
129
github.com/mattn/go-isatty v0.0.20 // indirect
130
github.com/minio/sha256-simd v1.0.1 // indirect
131
github.com/mitchellh/mapstructure v1.5.0 // indirect
132
github.com/moby/docker-image-spec v1.3.1 // indirect
···
134
github.com/moby/term v0.5.2 // indirect
135
github.com/morikuni/aec v1.0.0 // indirect
136
github.com/mr-tron/base58 v1.2.0 // indirect
137
github.com/multiformats/go-base32 v0.1.0 // indirect
138
github.com/multiformats/go-base36 v0.2.0 // indirect
139
github.com/multiformats/go-multibase v0.2.0 // indirect
···
152
github.com/prometheus/client_model v0.6.2 // indirect
153
github.com/prometheus/common v0.64.0 // indirect
154
github.com/prometheus/procfs v0.16.1 // indirect
155
github.com/ryanuber/go-glob v1.0.0 // indirect
156
github.com/segmentio/asm v1.2.0 // indirect
157
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
···
160
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
161
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
162
github.com/wyatt915/treeblood v0.1.15 // indirect
163
gitlab.com/staticnoise/goldmark-callout v0.0.0-20240609120641-6366b799e4ab // indirect
164
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
165
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
···
60
github.com/ProtonMail/go-crypto v1.3.0 // indirect
61
github.com/alecthomas/repr v0.4.0 // indirect
62
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
63
+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
64
github.com/aymerick/douceur v0.2.0 // indirect
65
github.com/beorn7/perks v1.0.1 // indirect
66
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
67
github.com/casbin/govaluate v1.3.0 // indirect
68
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
69
github.com/cespare/xxhash/v2 v2.3.0 // indirect
70
+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
71
+
github.com/charmbracelet/lipgloss v1.1.0 // indirect
72
+
github.com/charmbracelet/log v0.4.2 // indirect
73
+
github.com/charmbracelet/x/ansi v0.8.0 // indirect
74
+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
75
+
github.com/charmbracelet/x/term v0.2.1 // indirect
76
github.com/cloudflare/circl v1.6.2-0.20250618153321-aa837fd1539d // indirect
77
github.com/containerd/errdefs v1.0.0 // indirect
78
github.com/containerd/errdefs/pkg v0.3.0 // indirect
···
91
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
92
github.com/go-git/go-billy/v5 v5.6.2 // indirect
93
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
94
+
github.com/go-logfmt/logfmt v0.6.0 // indirect
95
github.com/go-logr/logr v1.4.3 // indirect
96
github.com/go-logr/stdr v1.2.2 // indirect
97
github.com/go-redis/cache/v9 v9.0.0 // indirect
···
134
github.com/lestrrat-go/httprc v1.0.6 // indirect
135
github.com/lestrrat-go/iter v1.0.2 // indirect
136
github.com/lestrrat-go/option v1.0.1 // indirect
137
+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
138
github.com/mattn/go-isatty v0.0.20 // indirect
139
+
github.com/mattn/go-runewidth v0.0.16 // indirect
140
github.com/minio/sha256-simd v1.0.1 // indirect
141
github.com/mitchellh/mapstructure v1.5.0 // indirect
142
github.com/moby/docker-image-spec v1.3.1 // indirect
···
144
github.com/moby/term v0.5.2 // indirect
145
github.com/morikuni/aec v1.0.0 // indirect
146
github.com/mr-tron/base58 v1.2.0 // indirect
147
+
github.com/muesli/termenv v0.16.0 // indirect
148
github.com/multiformats/go-base32 v0.1.0 // indirect
149
github.com/multiformats/go-base36 v0.2.0 // indirect
150
github.com/multiformats/go-multibase v0.2.0 // indirect
···
163
github.com/prometheus/client_model v0.6.2 // indirect
164
github.com/prometheus/common v0.64.0 // indirect
165
github.com/prometheus/procfs v0.16.1 // indirect
166
+
github.com/rivo/uniseg v0.4.7 // indirect
167
github.com/ryanuber/go-glob v1.0.0 // indirect
168
github.com/segmentio/asm v1.2.0 // indirect
169
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
···
172
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
173
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
174
github.com/wyatt915/treeblood v0.1.15 // indirect
175
+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
176
gitlab.com/staticnoise/goldmark-callout v0.0.0-20240609120641-6366b799e4ab // indirect
177
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
178
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
+27
go.sum
+27
go.sum
···
19
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
20
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
21
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
22
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
23
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
24
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
···
48
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
49
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
50
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
51
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
52
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
53
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
···
120
github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03/go.mod h1:hMKrMnUE4W0SJ7bFyM00dyz/HoknZoptGWzrj6M+dEM=
121
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
122
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
123
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
124
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
125
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
···
276
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
277
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
278
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
279
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
280
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
281
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
282
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
283
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
284
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
285
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
···
300
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
301
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
302
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
303
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
304
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
305
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
···
377
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
378
github.com/resend/resend-go/v2 v2.15.0 h1:B6oMEPf8IEQwn2Ovx/9yymkESLDSeNfLFaNMw+mzHhE=
379
github.com/resend/resend-go/v2 v2.15.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
380
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
381
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
382
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
···
434
github.com/wyatt915/goldmark-treeblood v0.0.0-20250825231212-5dcbdb2f4b57/go.mod h1:BxSCWByWSRSuembL3cDG1IBUbkBoO/oW/6tF19aA4hs=
435
github.com/wyatt915/treeblood v0.1.15 h1:3KZ3o2LpcKZAzOLqMoW9qeUzKEaKArKpbcPpTkNfQC8=
436
github.com/wyatt915/treeblood v0.1.15/go.mod h1:i7+yhhmzdDP17/97pIsOSffw74EK/xk+qJ0029cSXUY=
437
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
438
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
439
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
···
19
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
20
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
21
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
22
+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
23
+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
24
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
25
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
26
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
···
50
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
51
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
52
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
53
+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
54
+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
55
+
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
56
+
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
57
+
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
58
+
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
59
+
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
60
+
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
61
+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
62
+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
63
+
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
64
+
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
65
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
66
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
67
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
···
134
github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03/go.mod h1:hMKrMnUE4W0SJ7bFyM00dyz/HoknZoptGWzrj6M+dEM=
135
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
136
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
137
+
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
138
+
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
139
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
140
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
141
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
···
292
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
293
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
294
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
295
+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
296
+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
297
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
298
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
299
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
300
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
301
+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
302
+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
303
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
304
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
305
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
···
320
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
321
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
322
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
323
+
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
324
+
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
325
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
326
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
327
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
···
399
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
400
github.com/resend/resend-go/v2 v2.15.0 h1:B6oMEPf8IEQwn2Ovx/9yymkESLDSeNfLFaNMw+mzHhE=
401
github.com/resend/resend-go/v2 v2.15.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
402
+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
403
+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
404
+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
405
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
406
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
407
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
···
459
github.com/wyatt915/goldmark-treeblood v0.0.0-20250825231212-5dcbdb2f4b57/go.mod h1:BxSCWByWSRSuembL3cDG1IBUbkBoO/oW/6tF19aA4hs=
460
github.com/wyatt915/treeblood v0.1.15 h1:3KZ3o2LpcKZAzOLqMoW9qeUzKEaKArKpbcPpTkNfQC8=
461
github.com/wyatt915/treeblood v0.1.15/go.mod h1:i7+yhhmzdDP17/97pIsOSffw74EK/xk+qJ0029cSXUY=
462
+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
463
+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
464
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
465
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
466
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+1
-1
jetstream/jetstream.go
+1
-1
jetstream/jetstream.go
+39
nix/gomod2nix.toml
+39
nix/gomod2nix.toml
···
29
[mod."github.com/avast/retry-go/v4"]
30
version = "v4.6.1"
31
hash = "sha256-PeZc8k4rDV64+k8nZt/oy1YNVbLevltXP3ZD1jf6Z6k="
32
[mod."github.com/aymerick/douceur"]
33
version = "v0.2.0"
34
hash = "sha256-NiBX8EfOvLXNiK3pJaZX4N73YgfzdrzRXdiBFe3X3sE="
···
63
[mod."github.com/cespare/xxhash/v2"]
64
version = "v2.3.0"
65
hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY="
66
[mod."github.com/cloudflare/circl"]
67
version = "v1.6.2-0.20250618153321-aa837fd1539d"
68
hash = "sha256-0s/i/XmMcuvPQ+qK9OIU5KxwYZyLVXRtdlYvIXRJT3Y="
···
145
[mod."github.com/go-jose/go-jose/v3"]
146
version = "v3.0.4"
147
hash = "sha256-RrLHCu9l6k0XVobdZQJ9Sx/VTQcWjrdLR5BEG7yXTEQ="
148
[mod."github.com/go-logr/logr"]
149
version = "v1.4.3"
150
hash = "sha256-Nnp/dEVNMxLp3RSPDHZzGbI8BkSNuZMX0I0cjWKXXLA="
···
298
[mod."github.com/lestrrat-go/option"]
299
version = "v1.0.1"
300
hash = "sha256-jVcIYYVsxElIS/l2akEw32vdEPR8+anR6oeT1FoYULI="
301
[mod."github.com/mattn/go-isatty"]
302
version = "v0.0.20"
303
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
304
[mod."github.com/mattn/go-sqlite3"]
305
version = "v1.14.24"
306
hash = "sha256-taGKFZFQlR5++5b2oZ1dYS3RERKv6yh1gniNWhb4egg="
···
328
[mod."github.com/mr-tron/base58"]
329
version = "v1.2.0"
330
hash = "sha256-8FzMu3kHUbBX10pUdtGf59Ag7BNupx8ZHeUaodR1/Vk="
331
[mod."github.com/multiformats/go-base32"]
332
version = "v0.1.0"
333
hash = "sha256-O2IM7FB+Y9MkDdZztyQL5F8oEnmON2Yew7XkotQziio="
···
394
[mod."github.com/resend/resend-go/v2"]
395
version = "v2.15.0"
396
hash = "sha256-1lMoxuMLQXaNWFKadS6rpztAKwvIl3/LWMXqw7f5WYg="
397
[mod."github.com/ryanuber/go-glob"]
398
version = "v1.0.0"
399
hash = "sha256-YkMl1utwUhi3E0sHK23ISpAsPyj4+KeXyXKoFYGXGVY="
···
440
[mod."github.com/wyatt915/treeblood"]
441
version = "v0.1.15"
442
hash = "sha256-hb99exdkoY2Qv8WdDxhwgPXGbEYimUr6wFtPXEvcO9g="
443
[mod."github.com/yuin/goldmark"]
444
version = "v1.7.13"
445
hash = "sha256-vBCxZrPYPc8x/nvAAv3Au59dCCyfS80Vw3/a9EXK7TE="
···
29
[mod."github.com/avast/retry-go/v4"]
30
version = "v4.6.1"
31
hash = "sha256-PeZc8k4rDV64+k8nZt/oy1YNVbLevltXP3ZD1jf6Z6k="
32
+
[mod."github.com/aymanbagabas/go-osc52/v2"]
33
+
version = "v2.0.1"
34
+
hash = "sha256-6Bp0jBZ6npvsYcKZGHHIUSVSTAMEyieweAX2YAKDjjg="
35
[mod."github.com/aymerick/douceur"]
36
version = "v0.2.0"
37
hash = "sha256-NiBX8EfOvLXNiK3pJaZX4N73YgfzdrzRXdiBFe3X3sE="
···
66
[mod."github.com/cespare/xxhash/v2"]
67
version = "v2.3.0"
68
hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY="
69
+
[mod."github.com/charmbracelet/colorprofile"]
70
+
version = "v0.2.3-0.20250311203215-f60798e515dc"
71
+
hash = "sha256-D9E/bMOyLXAUVOHA1/6o3i+vVmLfwIMOWib6sU7A6+Q="
72
+
[mod."github.com/charmbracelet/lipgloss"]
73
+
version = "v1.1.0"
74
+
hash = "sha256-RHsRT2EZ1nDOElxAK+6/DC9XAaGVjDTgPvRh3pyCfY4="
75
+
[mod."github.com/charmbracelet/log"]
76
+
version = "v0.4.2"
77
+
hash = "sha256-3w1PCM/c4JvVEh2d0sMfv4C77Xs1bPa1Ea84zdynC7I="
78
+
[mod."github.com/charmbracelet/x/ansi"]
79
+
version = "v0.8.0"
80
+
hash = "sha256-/YyDkGrULV2BtnNk3ojeSl0nUWQwIfIdW7WJuGbAZas="
81
+
[mod."github.com/charmbracelet/x/cellbuf"]
82
+
version = "v0.0.13-0.20250311204145-2c3ea96c31dd"
83
+
hash = "sha256-XAhCOt8qJ2vR77lH1ez0IVU1/2CaLTq9jSmrHVg5HHU="
84
+
[mod."github.com/charmbracelet/x/term"]
85
+
version = "v0.2.1"
86
+
hash = "sha256-VBkCZLI90PhMasftGw3403IqoV7d3E5WEGAIVrN5xQM="
87
[mod."github.com/cloudflare/circl"]
88
version = "v1.6.2-0.20250618153321-aa837fd1539d"
89
hash = "sha256-0s/i/XmMcuvPQ+qK9OIU5KxwYZyLVXRtdlYvIXRJT3Y="
···
166
[mod."github.com/go-jose/go-jose/v3"]
167
version = "v3.0.4"
168
hash = "sha256-RrLHCu9l6k0XVobdZQJ9Sx/VTQcWjrdLR5BEG7yXTEQ="
169
+
[mod."github.com/go-logfmt/logfmt"]
170
+
version = "v0.6.0"
171
+
hash = "sha256-RtIG2qARd5sT10WQ7F3LR8YJhS8exs+KiuUiVf75bWg="
172
[mod."github.com/go-logr/logr"]
173
version = "v1.4.3"
174
hash = "sha256-Nnp/dEVNMxLp3RSPDHZzGbI8BkSNuZMX0I0cjWKXXLA="
···
322
[mod."github.com/lestrrat-go/option"]
323
version = "v1.0.1"
324
hash = "sha256-jVcIYYVsxElIS/l2akEw32vdEPR8+anR6oeT1FoYULI="
325
+
[mod."github.com/lucasb-eyer/go-colorful"]
326
+
version = "v1.2.0"
327
+
hash = "sha256-Gg9dDJFCTaHrKHRR1SrJgZ8fWieJkybljybkI9x0gyE="
328
[mod."github.com/mattn/go-isatty"]
329
version = "v0.0.20"
330
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
331
+
[mod."github.com/mattn/go-runewidth"]
332
+
version = "v0.0.16"
333
+
hash = "sha256-NC+ntvwIpqDNmXb7aixcg09il80ygq6JAnW0Gb5b/DQ="
334
[mod."github.com/mattn/go-sqlite3"]
335
version = "v1.14.24"
336
hash = "sha256-taGKFZFQlR5++5b2oZ1dYS3RERKv6yh1gniNWhb4egg="
···
358
[mod."github.com/mr-tron/base58"]
359
version = "v1.2.0"
360
hash = "sha256-8FzMu3kHUbBX10pUdtGf59Ag7BNupx8ZHeUaodR1/Vk="
361
+
[mod."github.com/muesli/termenv"]
362
+
version = "v0.16.0"
363
+
hash = "sha256-hGo275DJlyLtcifSLpWnk8jardOksdeX9lH4lBeE3gI="
364
[mod."github.com/multiformats/go-base32"]
365
version = "v0.1.0"
366
hash = "sha256-O2IM7FB+Y9MkDdZztyQL5F8oEnmON2Yew7XkotQziio="
···
427
[mod."github.com/resend/resend-go/v2"]
428
version = "v2.15.0"
429
hash = "sha256-1lMoxuMLQXaNWFKadS6rpztAKwvIl3/LWMXqw7f5WYg="
430
+
[mod."github.com/rivo/uniseg"]
431
+
version = "v0.4.7"
432
+
hash = "sha256-rDcdNYH6ZD8KouyyiZCUEy8JrjOQoAkxHBhugrfHjFo="
433
[mod."github.com/ryanuber/go-glob"]
434
version = "v1.0.0"
435
hash = "sha256-YkMl1utwUhi3E0sHK23ISpAsPyj4+KeXyXKoFYGXGVY="
···
476
[mod."github.com/wyatt915/treeblood"]
477
version = "v0.1.15"
478
hash = "sha256-hb99exdkoY2Qv8WdDxhwgPXGbEYimUr6wFtPXEvcO9g="
479
+
[mod."github.com/xo/terminfo"]
480
+
version = "v0.0.0-20220910002029-abceb7e1c41e"
481
+
hash = "sha256-GyCDxxMQhXA3Pi/TsWXpA8cX5akEoZV7CFx4RO3rARU="
482
[mod."github.com/yuin/goldmark"]
483
version = "v1.7.13"
484
hash = "sha256-vBCxZrPYPc8x/nvAAv3Au59dCCyfS80Vw3/a9EXK7TE="
+5
-4
xrpc/serviceauth/service_auth.go
+5
-4
xrpc/serviceauth/service_auth.go
···
9
10
"github.com/bluesky-social/indigo/atproto/auth"
11
"tangled.org/core/idresolver"
12
xrpcerr "tangled.org/core/xrpc/errors"
13
)
14
···
22
23
func NewServiceAuth(logger *slog.Logger, resolver *idresolver.Resolver, audienceDid string) *ServiceAuth {
24
return &ServiceAuth{
25
-
logger: logger,
26
resolver: resolver,
27
audienceDid: audienceDid,
28
}
···
30
31
func (sa *ServiceAuth) VerifyServiceAuth(next http.Handler) http.Handler {
32
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
33
-
l := sa.logger.With("url", r.URL)
34
-
35
token := r.Header.Get("Authorization")
36
token = strings.TrimPrefix(token, "Bearer ")
37
···
42
43
did, err := s.Validate(r.Context(), token, nil)
44
if err != nil {
45
-
l.Error("signature verification failed", "err", err)
46
writeError(w, xrpcerr.AuthError(err), http.StatusForbidden)
47
return
48
}
49
50
r = r.WithContext(
51
context.WithValue(r.Context(), ActorDid, did),
···
9
10
"github.com/bluesky-social/indigo/atproto/auth"
11
"tangled.org/core/idresolver"
12
+
"tangled.org/core/log"
13
xrpcerr "tangled.org/core/xrpc/errors"
14
)
15
···
23
24
func NewServiceAuth(logger *slog.Logger, resolver *idresolver.Resolver, audienceDid string) *ServiceAuth {
25
return &ServiceAuth{
26
+
logger: log.SubLogger(logger, "serviceauth"),
27
resolver: resolver,
28
audienceDid: audienceDid,
29
}
···
31
32
func (sa *ServiceAuth) VerifyServiceAuth(next http.Handler) http.Handler {
33
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
34
token := r.Header.Get("Authorization")
35
token = strings.TrimPrefix(token, "Bearer ")
36
···
41
42
did, err := s.Validate(r.Context(), token, nil)
43
if err != nil {
44
+
sa.logger.Error("signature verification failed", "err", err)
45
writeError(w, xrpcerr.AuthError(err), http.StatusForbidden)
46
return
47
}
48
+
49
+
sa.logger.Debug("valid signature", ActorDid, did)
50
51
r = r.WithContext(
52
context.WithValue(r.Context(), ActorDid, did),