+1
-1
.air/appview.toml
+1
-1
.air/appview.toml
+37
-10
appview/config.go
+37
-10
appview/config.go
···
6
6
"github.com/sethvargo/go-envconfig"
7
7
)
8
8
9
+
type CoreConfig struct {
10
+
CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"`
11
+
DbPath string `env:"DB_PATH, default=appview.db"`
12
+
ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"`
13
+
AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"`
14
+
Dev bool `env:"DEV, default=false"`
15
+
}
16
+
17
+
type OAuthConfig struct {
18
+
Jwks string `env:"JWKS"`
19
+
ServerMetadataUrl string `env:"SERVER_METADATA_URL"`
20
+
}
21
+
22
+
type JetstreamConfig struct {
23
+
Endpoint string `env:"ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"`
24
+
}
25
+
26
+
type ResendConfig struct {
27
+
ApiKey string `env:"API_KEY"`
28
+
}
29
+
30
+
type CamoConfig struct {
31
+
Host string `env:"HOST, default=https://camo.tangled.sh"`
32
+
SharedSecret string `env:"SHARED_SECRET"`
33
+
}
34
+
35
+
type AvatarConfig struct {
36
+
Host string `env:"HOST, default=https://avatar.tangled.sh"`
37
+
SharedSecret string `env:"SHARED_SECRET"`
38
+
}
39
+
9
40
type Config struct {
10
-
CookieSecret string `env:"TANGLED_COOKIE_SECRET, default=00000000000000000000000000000000"`
11
-
DbPath string `env:"TANGLED_DB_PATH, default=appview.db"`
12
-
ListenAddr string `env:"TANGLED_LISTEN_ADDR, default=0.0.0.0:3000"`
13
-
Dev bool `env:"TANGLED_DEV, default=false"`
14
-
JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"`
15
-
ResendApiKey string `env:"TANGLED_RESEND_API_KEY"`
16
-
CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"`
17
-
CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"`
18
-
AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"`
19
-
AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"`
41
+
Core CoreConfig `env:",prefix=TANGLED_"`
42
+
Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"`
43
+
Resend ResendConfig `env:",prefix=TANGLED_RESEND_"`
44
+
Camo CamoConfig `env:",prefix=TANGLED_CAMO_"`
45
+
Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"`
46
+
OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"`
20
47
}
21
48
22
49
func LoadConfig(ctx context.Context) (*Config, error) {
+3
appview/consts.go
+3
appview/consts.go
+26
appview/db/db.go
+26
appview/db/db.go
···
288
288
foreign key (at_uri) references repos(at_uri) on delete cascade
289
289
);
290
290
291
+
create table if not exists oauth_requests (
292
+
id integer primary key autoincrement,
293
+
auth_server_iss text not null,
294
+
state text not null,
295
+
did text not null,
296
+
handle text not null,
297
+
pds_url text not null,
298
+
pkce_verifier text not null,
299
+
dpop_auth_server_nonce text not null,
300
+
dpop_private_jwk text not null
301
+
);
302
+
303
+
create table if not exists oauth_sessions (
304
+
id integer primary key autoincrement,
305
+
did text not null,
306
+
handle text not null,
307
+
pds_url text not null,
308
+
auth_server_iss text not null,
309
+
access_jwt text not null,
310
+
refresh_jwt text not null,
311
+
dpop_pds_nonce text,
312
+
dpop_auth_server_nonce text not null,
313
+
dpop_private_jwk text not null,
314
+
expiry text not null
315
+
);
316
+
291
317
create table if not exists migrations (
292
318
id integer primary key autoincrement,
293
319
name text unique
+173
appview/db/oauth.go
+173
appview/db/oauth.go
···
1
+
package db
2
+
3
+
type OAuthRequest struct {
4
+
ID uint
5
+
AuthserverIss string
6
+
Handle string
7
+
State string
8
+
Did string
9
+
PdsUrl string
10
+
PkceVerifier string
11
+
DpopAuthserverNonce string
12
+
DpopPrivateJwk string
13
+
}
14
+
15
+
func SaveOAuthRequest(e Execer, oauthRequest OAuthRequest) error {
16
+
_, err := e.Exec(`
17
+
insert into oauth_requests (
18
+
auth_server_iss,
19
+
state,
20
+
handle,
21
+
did,
22
+
pds_url,
23
+
pkce_verifier,
24
+
dpop_auth_server_nonce,
25
+
dpop_private_jwk
26
+
) values (?, ?, ?, ?, ?, ?, ?, ?)`,
27
+
oauthRequest.AuthserverIss,
28
+
oauthRequest.State,
29
+
oauthRequest.Handle,
30
+
oauthRequest.Did,
31
+
oauthRequest.PdsUrl,
32
+
oauthRequest.PkceVerifier,
33
+
oauthRequest.DpopAuthserverNonce,
34
+
oauthRequest.DpopPrivateJwk,
35
+
)
36
+
return err
37
+
}
38
+
39
+
func GetOAuthRequestByState(e Execer, state string) (OAuthRequest, error) {
40
+
var req OAuthRequest
41
+
err := e.QueryRow(`
42
+
select
43
+
id,
44
+
auth_server_iss,
45
+
handle,
46
+
state,
47
+
did,
48
+
pds_url,
49
+
pkce_verifier,
50
+
dpop_auth_server_nonce,
51
+
dpop_private_jwk
52
+
from oauth_requests
53
+
where state = ?`, state).Scan(
54
+
&req.ID,
55
+
&req.AuthserverIss,
56
+
&req.Handle,
57
+
&req.State,
58
+
&req.Did,
59
+
&req.PdsUrl,
60
+
&req.PkceVerifier,
61
+
&req.DpopAuthserverNonce,
62
+
&req.DpopPrivateJwk,
63
+
)
64
+
return req, err
65
+
}
66
+
67
+
func DeleteOAuthRequestByState(e Execer, state string) error {
68
+
_, err := e.Exec(`
69
+
delete from oauth_requests
70
+
where state = ?`, state)
71
+
return err
72
+
}
73
+
74
+
type OAuthSession struct {
75
+
ID uint
76
+
Handle string
77
+
Did string
78
+
PdsUrl string
79
+
AccessJwt string
80
+
RefreshJwt string
81
+
AuthServerIss string
82
+
DpopPdsNonce string
83
+
DpopAuthserverNonce string
84
+
DpopPrivateJwk string
85
+
Expiry string
86
+
}
87
+
88
+
func SaveOAuthSession(e Execer, session OAuthSession) error {
89
+
_, err := e.Exec(`
90
+
insert into oauth_sessions (
91
+
did,
92
+
handle,
93
+
pds_url,
94
+
access_jwt,
95
+
refresh_jwt,
96
+
auth_server_iss,
97
+
dpop_auth_server_nonce,
98
+
dpop_private_jwk,
99
+
expiry
100
+
) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
101
+
session.Did,
102
+
session.Handle,
103
+
session.PdsUrl,
104
+
session.AccessJwt,
105
+
session.RefreshJwt,
106
+
session.AuthServerIss,
107
+
session.DpopAuthserverNonce,
108
+
session.DpopPrivateJwk,
109
+
session.Expiry,
110
+
)
111
+
return err
112
+
}
113
+
114
+
func RefreshOAuthSession(e Execer, did string, accessJwt, refreshJwt, expiry string) error {
115
+
_, err := e.Exec(`
116
+
update oauth_sessions
117
+
set access_jwt = ?, refresh_jwt = ?, expiry = ?
118
+
where did = ?`,
119
+
accessJwt,
120
+
refreshJwt,
121
+
expiry,
122
+
did,
123
+
)
124
+
return err
125
+
}
126
+
127
+
func GetOAuthSessionByDid(e Execer, did string) (*OAuthSession, error) {
128
+
var session OAuthSession
129
+
err := e.QueryRow(`
130
+
select
131
+
id,
132
+
did,
133
+
handle,
134
+
pds_url,
135
+
access_jwt,
136
+
refresh_jwt,
137
+
auth_server_iss,
138
+
dpop_auth_server_nonce,
139
+
dpop_private_jwk,
140
+
expiry
141
+
from oauth_sessions
142
+
where did = ?`, did).Scan(
143
+
&session.ID,
144
+
&session.Did,
145
+
&session.Handle,
146
+
&session.PdsUrl,
147
+
&session.AccessJwt,
148
+
&session.RefreshJwt,
149
+
&session.AuthServerIss,
150
+
&session.DpopAuthserverNonce,
151
+
&session.DpopPrivateJwk,
152
+
&session.Expiry,
153
+
)
154
+
return &session, err
155
+
}
156
+
157
+
func DeleteOAuthSessionByDid(e Execer, did string) error {
158
+
_, err := e.Exec(`
159
+
delete from oauth_sessions
160
+
where did = ?`, did)
161
+
return err
162
+
}
163
+
164
+
func UpdateDpopPdsNonce(e Execer, did string, dpopPdsNonce string) error {
165
+
_, err := e.Exec(`
166
+
update oauth_sessions
167
+
set dpop_pds_nonce = ?
168
+
where did = ?`,
169
+
dpopPdsNonce,
170
+
did,
171
+
)
172
+
return err
173
+
}
+5
-58
appview/middleware/middleware.go
+5
-58
appview/middleware/middleware.go
···
5
5
"log"
6
6
"net/http"
7
7
"strconv"
8
-
"time"
9
8
10
-
comatproto "github.com/bluesky-social/indigo/api/atproto"
11
-
"github.com/bluesky-social/indigo/xrpc"
12
-
"tangled.sh/tangled.sh/core/appview"
13
-
"tangled.sh/tangled.sh/core/appview/auth"
9
+
"tangled.sh/tangled.sh/core/appview/oauth"
14
10
"tangled.sh/tangled.sh/core/appview/pagination"
15
11
)
16
12
17
13
type Middleware func(http.Handler) http.Handler
18
14
19
-
func AuthMiddleware(a *auth.Auth) Middleware {
15
+
func AuthMiddleware(a *oauth.OAuth) Middleware {
20
16
return func(next http.Handler) http.Handler {
21
17
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22
18
redirectFunc := func(w http.ResponseWriter, r *http.Request) {
···
29
25
}
30
26
}
31
27
32
-
session, err := a.GetSession(r)
33
-
if session.IsNew || err != nil {
28
+
_, auth, err := a.GetSession(r)
29
+
if err != nil {
34
30
log.Printf("not logged in, redirecting")
35
31
redirectFunc(w, r)
36
32
return
37
33
}
38
34
39
-
authorized, ok := session.Values[appview.SessionAuthenticated].(bool)
40
-
if !ok || !authorized {
35
+
if !auth {
41
36
log.Printf("not logged in, redirecting")
42
37
redirectFunc(w, r)
43
38
return
44
-
}
45
-
46
-
// refresh if nearing expiry
47
-
// TODO: dedup with /login
48
-
expiryStr := session.Values[appview.SessionExpiry].(string)
49
-
expiry, err := time.Parse(time.RFC3339, expiryStr)
50
-
if err != nil {
51
-
log.Println("invalid expiry time", err)
52
-
redirectFunc(w, r)
53
-
return
54
-
}
55
-
pdsUrl, ok1 := session.Values[appview.SessionPds].(string)
56
-
did, ok2 := session.Values[appview.SessionDid].(string)
57
-
refreshJwt, ok3 := session.Values[appview.SessionRefreshJwt].(string)
58
-
59
-
if !ok1 || !ok2 || !ok3 {
60
-
log.Println("invalid expiry time", err)
61
-
redirectFunc(w, r)
62
-
return
63
-
}
64
-
65
-
if time.Now().After(expiry) {
66
-
log.Println("token expired, refreshing ...")
67
-
68
-
client := xrpc.Client{
69
-
Host: pdsUrl,
70
-
Auth: &xrpc.AuthInfo{
71
-
Did: did,
72
-
AccessJwt: refreshJwt,
73
-
RefreshJwt: refreshJwt,
74
-
},
75
-
}
76
-
atSession, err := comatproto.ServerRefreshSession(r.Context(), &client)
77
-
if err != nil {
78
-
log.Println("failed to refresh session", err)
79
-
redirectFunc(w, r)
80
-
return
81
-
}
82
-
83
-
sessionish := auth.RefreshSessionWrapper{atSession}
84
-
85
-
err = a.StoreSession(r, w, &sessionish, pdsUrl)
86
-
if err != nil {
87
-
log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err)
88
-
return
89
-
}
90
-
91
-
log.Println("successfully refreshed token")
92
39
}
93
40
94
41
next.ServeHTTP(w, r)
+2
appview/oauth/oauth.go
+2
appview/oauth/oauth.go
···
48
48
}
49
49
50
50
userSession.Values[appview.SessionDid] = oreq.Did
51
+
userSession.Values[appview.SessionHandle] = oreq.Handle
52
+
userSession.Values[appview.SessionPds] = oreq.PdsUrl
51
53
userSession.Values[appview.SessionAuthenticated] = true
52
54
err = userSession.Save(r, w)
53
55
if err != nil {
+41
-37
appview/pages/pages.go
+41
-37
appview/pages/pages.go
···
16
16
"strings"
17
17
18
18
"tangled.sh/tangled.sh/core/appview"
19
-
"tangled.sh/tangled.sh/core/appview/auth"
20
19
"tangled.sh/tangled.sh/core/appview/db"
20
+
"tangled.sh/tangled.sh/core/appview/oauth"
21
21
"tangled.sh/tangled.sh/core/appview/pages/markup"
22
22
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
23
23
"tangled.sh/tangled.sh/core/appview/pagination"
···
48
48
func NewPages(config *appview.Config) *Pages {
49
49
// initialized with safe defaults, can be overriden per use
50
50
rctx := &markup.RenderContext{
51
-
IsDev: config.Dev,
52
-
CamoUrl: config.CamoHost,
53
-
CamoSecret: config.CamoSharedSecret,
51
+
IsDev: config.Core.Dev,
52
+
CamoUrl: config.Camo.Host,
53
+
CamoSecret: config.Camo.SharedSecret,
54
54
}
55
55
56
56
p := &Pages{
57
57
t: make(map[string]*template.Template),
58
-
dev: config.Dev,
58
+
dev: config.Core.Dev,
59
59
embedFS: Files,
60
60
rctx: rctx,
61
61
templateDir: "appview/pages",
···
249
249
return p.executePlain("user/login", w, params)
250
250
}
251
251
252
+
func (p *Pages) OAuthLogin(w io.Writer, params LoginParams) error {
253
+
return p.executePlain("user/oauthlogin", w, params)
254
+
}
255
+
252
256
type TimelineParams struct {
253
-
LoggedInUser *auth.User
257
+
LoggedInUser *oauth.User
254
258
Timeline []db.TimelineEvent
255
259
DidHandleMap map[string]string
256
260
}
···
260
264
}
261
265
262
266
type SettingsParams struct {
263
-
LoggedInUser *auth.User
267
+
LoggedInUser *oauth.User
264
268
PubKeys []db.PublicKey
265
269
Emails []db.Email
266
270
}
···
270
274
}
271
275
272
276
type KnotsParams struct {
273
-
LoggedInUser *auth.User
277
+
LoggedInUser *oauth.User
274
278
Registrations []db.Registration
275
279
}
276
280
···
279
283
}
280
284
281
285
type KnotParams struct {
282
-
LoggedInUser *auth.User
286
+
LoggedInUser *oauth.User
283
287
DidHandleMap map[string]string
284
288
Registration *db.Registration
285
289
Members []string
···
291
295
}
292
296
293
297
type NewRepoParams struct {
294
-
LoggedInUser *auth.User
298
+
LoggedInUser *oauth.User
295
299
Knots []string
296
300
}
297
301
···
300
304
}
301
305
302
306
type ForkRepoParams struct {
303
-
LoggedInUser *auth.User
307
+
LoggedInUser *oauth.User
304
308
Knots []string
305
309
RepoInfo repoinfo.RepoInfo
306
310
}
···
310
314
}
311
315
312
316
type ProfilePageParams struct {
313
-
LoggedInUser *auth.User
317
+
LoggedInUser *oauth.User
314
318
Repos []db.Repo
315
319
CollaboratingRepos []db.Repo
316
320
ProfileTimeline *db.ProfileTimeline
···
335
339
}
336
340
337
341
type ReposPageParams struct {
338
-
LoggedInUser *auth.User
342
+
LoggedInUser *oauth.User
339
343
Repos []db.Repo
340
344
Card ProfileCard
341
345
···
356
360
}
357
361
358
362
type EditBioParams struct {
359
-
LoggedInUser *auth.User
363
+
LoggedInUser *oauth.User
360
364
Profile *db.Profile
361
365
}
362
366
···
365
369
}
366
370
367
371
type EditPinsParams struct {
368
-
LoggedInUser *auth.User
372
+
LoggedInUser *oauth.User
369
373
Profile *db.Profile
370
374
AllRepos []PinnedRepo
371
375
DidHandleMap map[string]string
···
403
407
}
404
408
405
409
type RepoIndexParams struct {
406
-
LoggedInUser *auth.User
410
+
LoggedInUser *oauth.User
407
411
RepoInfo repoinfo.RepoInfo
408
412
Active string
409
413
TagMap map[string][]string
···
444
448
}
445
449
446
450
type RepoLogParams struct {
447
-
LoggedInUser *auth.User
451
+
LoggedInUser *oauth.User
448
452
RepoInfo repoinfo.RepoInfo
449
453
TagMap map[string][]string
450
454
types.RepoLogResponse
···
458
462
}
459
463
460
464
type RepoCommitParams struct {
461
-
LoggedInUser *auth.User
465
+
LoggedInUser *oauth.User
462
466
RepoInfo repoinfo.RepoInfo
463
467
Active string
464
468
EmailToDidOrHandle map[string]string
···
472
476
}
473
477
474
478
type RepoTreeParams struct {
475
-
LoggedInUser *auth.User
479
+
LoggedInUser *oauth.User
476
480
RepoInfo repoinfo.RepoInfo
477
481
Active string
478
482
BreadCrumbs [][]string
···
508
512
}
509
513
510
514
type RepoBranchesParams struct {
511
-
LoggedInUser *auth.User
515
+
LoggedInUser *oauth.User
512
516
RepoInfo repoinfo.RepoInfo
513
517
Active string
514
518
types.RepoBranchesResponse
···
520
524
}
521
525
522
526
type RepoTagsParams struct {
523
-
LoggedInUser *auth.User
527
+
LoggedInUser *oauth.User
524
528
RepoInfo repoinfo.RepoInfo
525
529
Active string
526
530
types.RepoTagsResponse
···
534
538
}
535
539
536
540
type RepoArtifactParams struct {
537
-
LoggedInUser *auth.User
541
+
LoggedInUser *oauth.User
538
542
RepoInfo repoinfo.RepoInfo
539
543
Artifact db.Artifact
540
544
}
···
544
548
}
545
549
546
550
type RepoBlobParams struct {
547
-
LoggedInUser *auth.User
551
+
LoggedInUser *oauth.User
548
552
RepoInfo repoinfo.RepoInfo
549
553
Active string
550
554
BreadCrumbs [][]string
···
606
610
}
607
611
608
612
type RepoSettingsParams struct {
609
-
LoggedInUser *auth.User
613
+
LoggedInUser *oauth.User
610
614
RepoInfo repoinfo.RepoInfo
611
615
Collaborators []Collaborator
612
616
Active string
···
622
626
}
623
627
624
628
type RepoIssuesParams struct {
625
-
LoggedInUser *auth.User
629
+
LoggedInUser *oauth.User
626
630
RepoInfo repoinfo.RepoInfo
627
631
Active string
628
632
Issues []db.Issue
···
637
641
}
638
642
639
643
type RepoSingleIssueParams struct {
640
-
LoggedInUser *auth.User
644
+
LoggedInUser *oauth.User
641
645
RepoInfo repoinfo.RepoInfo
642
646
Active string
643
647
Issue db.Issue
···
659
663
}
660
664
661
665
type RepoNewIssueParams struct {
662
-
LoggedInUser *auth.User
666
+
LoggedInUser *oauth.User
663
667
RepoInfo repoinfo.RepoInfo
664
668
Active string
665
669
}
···
670
674
}
671
675
672
676
type EditIssueCommentParams struct {
673
-
LoggedInUser *auth.User
677
+
LoggedInUser *oauth.User
674
678
RepoInfo repoinfo.RepoInfo
675
679
Issue *db.Issue
676
680
Comment *db.Comment
···
681
685
}
682
686
683
687
type SingleIssueCommentParams struct {
684
-
LoggedInUser *auth.User
688
+
LoggedInUser *oauth.User
685
689
DidHandleMap map[string]string
686
690
RepoInfo repoinfo.RepoInfo
687
691
Issue *db.Issue
···
693
697
}
694
698
695
699
type RepoNewPullParams struct {
696
-
LoggedInUser *auth.User
700
+
LoggedInUser *oauth.User
697
701
RepoInfo repoinfo.RepoInfo
698
702
Branches []types.Branch
699
703
Active string
···
705
709
}
706
710
707
711
type RepoPullsParams struct {
708
-
LoggedInUser *auth.User
712
+
LoggedInUser *oauth.User
709
713
RepoInfo repoinfo.RepoInfo
710
714
Pulls []*db.Pull
711
715
Active string
···
737
741
}
738
742
739
743
type RepoSinglePullParams struct {
740
-
LoggedInUser *auth.User
744
+
LoggedInUser *oauth.User
741
745
RepoInfo repoinfo.RepoInfo
742
746
Active string
743
747
DidHandleMap map[string]string
···
752
756
}
753
757
754
758
type RepoPullPatchParams struct {
755
-
LoggedInUser *auth.User
759
+
LoggedInUser *oauth.User
756
760
DidHandleMap map[string]string
757
761
RepoInfo repoinfo.RepoInfo
758
762
Pull *db.Pull
···
767
771
}
768
772
769
773
type RepoPullInterdiffParams struct {
770
-
LoggedInUser *auth.User
774
+
LoggedInUser *oauth.User
771
775
DidHandleMap map[string]string
772
776
RepoInfo repoinfo.RepoInfo
773
777
Pull *db.Pull
···
817
821
}
818
822
819
823
type PullResubmitParams struct {
820
-
LoggedInUser *auth.User
824
+
LoggedInUser *oauth.User
821
825
RepoInfo repoinfo.RepoInfo
822
826
Pull *db.Pull
823
827
SubmissionId int
···
828
832
}
829
833
830
834
type PullActionsParams struct {
831
-
LoggedInUser *auth.User
835
+
LoggedInUser *oauth.User
832
836
RepoInfo repoinfo.RepoInfo
833
837
Pull *db.Pull
834
838
RoundNumber int
···
841
845
}
842
846
843
847
type PullNewCommentParams struct {
844
-
LoggedInUser *auth.User
848
+
LoggedInUser *oauth.User
845
849
RepoInfo repoinfo.RepoInfo
846
850
Pull *db.Pull
847
851
RoundNumber int
+71
appview/pages/templates/user/oauthlogin.html
+71
appview/pages/templates/user/oauthlogin.html
···
1
+
{{ define "user/oauthlogin" }}
2
+
<!doctype html>
3
+
<html lang="en" class="dark:bg-gray-900">
4
+
<head>
5
+
<meta charset="UTF-8" />
6
+
<meta
7
+
name="viewport"
8
+
content="width=device-width, initial-scale=1.0"
9
+
/>
10
+
<script src="/static/htmx.min.js"></script>
11
+
<link
12
+
rel="stylesheet"
13
+
href="/static/tw.css?{{ cssContentHash }}"
14
+
type="text/css"
15
+
/>
16
+
<title>login</title>
17
+
</head>
18
+
<body class="flex items-center justify-center min-h-screen">
19
+
<main class="max-w-7xl px-6 -mt-4">
20
+
<h1
21
+
class="text-center text-2xl font-semibold italic dark:text-white"
22
+
>
23
+
tangled
24
+
</h1>
25
+
<h2 class="text-center text-xl italic dark:text-white">
26
+
tightly-knit social coding.
27
+
</h2>
28
+
<form
29
+
class="w-full mt-4"
30
+
hx-post="/oauth/login"
31
+
hx-swap="none"
32
+
hx-disabled-elt="this"
33
+
>
34
+
<div class="flex flex-col">
35
+
<label for="handle">handle</label>
36
+
<input
37
+
type="text"
38
+
id="handle"
39
+
name="handle"
40
+
tabindex="1"
41
+
required
42
+
/>
43
+
<span class="text-xs text-gray-500 mt-1">
44
+
Use your
45
+
<a href="https://bsky.app">Bluesky</a> handle to log
46
+
in. You will then be redirected to your PDS to
47
+
complete authentication.
48
+
</span>
49
+
</div>
50
+
51
+
<button
52
+
class="btn w-full my-2 mt-6"
53
+
type="submit"
54
+
id="login-button"
55
+
tabindex="3"
56
+
>
57
+
<span>login</span>
58
+
</button>
59
+
</form>
60
+
<p class="text-sm text-gray-500">
61
+
Join our <a href="https://chat.tangled.sh">Discord</a> or
62
+
IRC channel:
63
+
<a href="https://web.libera.chat/#tangled"
64
+
><code>#tangled</code> on Libera Chat</a
65
+
>.
66
+
</p>
67
+
<p id="login-msg" class="error w-full"></p>
68
+
</main>
69
+
</body>
70
+
</html>
71
+
{{ end }}
+27
-18
appview/settings/settings.go
+27
-18
appview/settings/settings.go
···
13
13
"github.com/go-chi/chi/v5"
14
14
"tangled.sh/tangled.sh/core/api/tangled"
15
15
"tangled.sh/tangled.sh/core/appview"
16
-
"tangled.sh/tangled.sh/core/appview/auth"
17
16
"tangled.sh/tangled.sh/core/appview/db"
18
17
"tangled.sh/tangled.sh/core/appview/email"
19
18
"tangled.sh/tangled.sh/core/appview/middleware"
19
+
"tangled.sh/tangled.sh/core/appview/oauth"
20
20
"tangled.sh/tangled.sh/core/appview/pages"
21
21
22
22
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
27
27
28
28
type Settings struct {
29
29
Db *db.DB
30
-
Auth *auth.Auth
30
+
OAuth *oauth.OAuth
31
31
Pages *pages.Pages
32
32
Config *appview.Config
33
33
}
···
35
35
func (s *Settings) Router() http.Handler {
36
36
r := chi.NewRouter()
37
37
38
-
r.Use(middleware.AuthMiddleware(s.Auth))
38
+
r.Use(middleware.AuthMiddleware(s.OAuth))
39
39
40
40
r.Get("/", s.settings)
41
41
···
56
56
}
57
57
58
58
func (s *Settings) settings(w http.ResponseWriter, r *http.Request) {
59
-
user := s.Auth.GetUser(r)
59
+
user := s.OAuth.GetUser(r)
60
60
pubKeys, err := db.GetPublicKeys(s.Db, user.Did)
61
61
if err != nil {
62
62
log.Println(err)
···
79
79
verifyURL := s.verifyUrl(did, emailAddr, code)
80
80
81
81
return email.Email{
82
-
APIKey: s.Config.ResendApiKey,
82
+
APIKey: s.Config.Resend.ApiKey,
83
83
From: "noreply@notifs.tangled.sh",
84
84
To: emailAddr,
85
85
Subject: "Verify your Tangled email",
···
111
111
log.Println("unimplemented")
112
112
return
113
113
case http.MethodPut:
114
-
did := s.Auth.GetDid(r)
114
+
did := s.OAuth.GetDid(r)
115
115
emAddr := r.FormValue("email")
116
116
emAddr = strings.TrimSpace(emAddr)
117
117
···
174
174
s.Pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.")
175
175
return
176
176
case http.MethodDelete:
177
-
did := s.Auth.GetDid(r)
177
+
did := s.OAuth.GetDid(r)
178
178
emailAddr := r.FormValue("email")
179
179
emailAddr = strings.TrimSpace(emailAddr)
180
180
···
207
207
208
208
func (s *Settings) verifyUrl(did string, email string, code string) string {
209
209
var appUrl string
210
-
if s.Config.Dev {
211
-
appUrl = "http://" + s.Config.ListenAddr
210
+
if s.Config.Core.Dev {
211
+
appUrl = "http://" + s.Config.Core.ListenAddr
212
212
} else {
213
213
appUrl = "https://tangled.sh"
214
214
}
···
252
252
return
253
253
}
254
254
255
-
did := s.Auth.GetDid(r)
255
+
did := s.OAuth.GetDid(r)
256
256
emAddr := r.FormValue("email")
257
257
emAddr = strings.TrimSpace(emAddr)
258
258
···
323
323
}
324
324
325
325
func (s *Settings) emailsPrimary(w http.ResponseWriter, r *http.Request) {
326
-
did := s.Auth.GetDid(r)
326
+
did := s.OAuth.GetDid(r)
327
327
emailAddr := r.FormValue("email")
328
328
emailAddr = strings.TrimSpace(emailAddr)
329
329
···
348
348
log.Println("unimplemented")
349
349
return
350
350
case http.MethodPut:
351
-
did := s.Auth.GetDid(r)
351
+
did := s.OAuth.GetDid(r)
352
352
key := r.FormValue("key")
353
353
key = strings.TrimSpace(key)
354
354
name := r.FormValue("name")
355
-
client, _ := s.Auth.AuthorizedClient(r)
355
+
client, err := s.OAuth.AuthorizedClient(r)
356
+
if err != nil {
357
+
s.Pages.Notice(w, "settings-keys", "Failed to authorize. Try again later.")
358
+
return
359
+
}
356
360
357
-
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
361
+
_, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key))
358
362
if err != nil {
359
363
log.Printf("parsing public key: %s", err)
360
364
s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a <strong>public</strong> key.")
···
378
382
}
379
383
380
384
// store in pds too
381
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
385
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
382
386
Collection: tangled.PublicKeyNSID,
383
387
Repo: did,
384
388
Rkey: rkey,
···
409
413
return
410
414
411
415
case http.MethodDelete:
412
-
did := s.Auth.GetDid(r)
416
+
did := s.OAuth.GetDid(r)
413
417
q := r.URL.Query()
414
418
415
419
name := q.Get("name")
···
420
424
log.Println(rkey)
421
425
log.Println(key)
422
426
423
-
client, _ := s.Auth.AuthorizedClient(r)
427
+
client, err := s.OAuth.AuthorizedClient(r)
428
+
if err != nil {
429
+
log.Printf("failed to authorize client: %s", err)
430
+
s.Pages.Notice(w, "settings-keys", "Failed to authorize client.")
431
+
return
432
+
}
424
433
425
434
if err := db.DeletePublicKey(s.Db, did, name, key); err != nil {
426
435
log.Printf("removing public key: %s", err)
···
430
439
431
440
if rkey != "" {
432
441
// remove from pds too
433
-
_, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
442
+
_, err := client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
434
443
Collection: tangled.PublicKeyNSID,
435
444
Repo: did,
436
445
Rkey: rkey,
+19
-10
appview/state/artifact.go
+19
-10
appview/state/artifact.go
···
22
22
23
23
// TODO: proper statuses here on early exit
24
24
func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) {
25
-
user := s.auth.GetUser(r)
25
+
user := s.oauth.GetUser(r)
26
26
tagParam := chi.URLParam(r, "tag")
27
27
f, err := s.fullyResolvedRepo(r)
28
28
if err != nil {
···
46
46
}
47
47
defer file.Close()
48
48
49
-
client, _ := s.auth.AuthorizedClient(r)
49
+
client, err := s.oauth.AuthorizedClient(r)
50
+
if err != nil {
51
+
log.Println("failed to get authorized client", err)
52
+
s.pages.Notice(w, "upload", "failed to get authorized client")
53
+
return
54
+
}
50
55
51
-
uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file)
56
+
uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file)
52
57
if err != nil {
53
58
log.Println("failed to upload blob", err)
54
59
s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.")
···
60
65
rkey := appview.TID()
61
66
createdAt := time.Now()
62
67
63
-
putRecordResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
68
+
putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
64
69
Collection: tangled.RepoArtifactNSID,
65
70
Repo: user.Did,
66
71
Rkey: rkey,
···
140
145
return
141
146
}
142
147
143
-
client, _ := s.auth.AuthorizedClient(r)
148
+
client, err := s.oauth.AuthorizedClient(r)
149
+
if err != nil {
150
+
log.Println("failed to get authorized client", err)
151
+
return
152
+
}
144
153
145
154
artifacts, err := db.GetArtifact(
146
155
s.db,
···
159
168
160
169
artifact := artifacts[0]
161
170
162
-
getBlobResp, err := comatproto.SyncGetBlob(r.Context(), client, artifact.BlobCid.String(), artifact.Did)
171
+
getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did)
163
172
if err != nil {
164
173
log.Println("failed to get blob from pds", err)
165
174
return
···
171
180
172
181
// TODO: proper statuses here on early exit
173
182
func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) {
174
-
user := s.auth.GetUser(r)
183
+
user := s.oauth.GetUser(r)
175
184
tagParam := chi.URLParam(r, "tag")
176
185
filename := chi.URLParam(r, "file")
177
186
f, err := s.fullyResolvedRepo(r)
···
180
189
return
181
190
}
182
191
183
-
client, _ := s.auth.AuthorizedClient(r)
192
+
client, _ := s.oauth.AuthorizedClient(r)
184
193
185
194
tag := plumbing.NewHash(tagParam)
186
195
···
208
217
return
209
218
}
210
219
211
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
220
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
212
221
Collection: tangled.RepoArtifactNSID,
213
222
Repo: user.Did,
214
223
Rkey: artifact.Rkey,
···
254
263
return nil, err
255
264
}
256
265
257
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
266
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
258
267
if err != nil {
259
268
return nil, err
260
269
}
+8
-4
appview/state/follow.go
+8
-4
appview/state/follow.go
···
14
14
)
15
15
16
16
func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
17
-
currentUser := s.auth.GetUser(r)
17
+
currentUser := s.oauth.GetUser(r)
18
18
19
19
subject := r.URL.Query().Get("subject")
20
20
if subject == "" {
···
32
32
return
33
33
}
34
34
35
-
client, _ := s.auth.AuthorizedClient(r)
35
+
client, err := s.oauth.AuthorizedClient(r)
36
+
if err != nil {
37
+
log.Println("failed to authorize client")
38
+
return
39
+
}
36
40
37
41
switch r.Method {
38
42
case http.MethodPost:
39
43
createdAt := time.Now().Format(time.RFC3339)
40
44
rkey := appview.TID()
41
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
45
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
42
46
Collection: tangled.GraphFollowNSID,
43
47
Repo: currentUser.Did,
44
48
Rkey: rkey,
···
75
79
return
76
80
}
77
81
78
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
82
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
79
83
Collection: tangled.GraphFollowNSID,
80
84
Repo: currentUser.Did,
81
85
Rkey: follow.Rkey,
+2
-2
appview/state/git_http.go
+2
-2
appview/state/git_http.go
···
15
15
repo := chi.URLParam(r, "repo")
16
16
17
17
scheme := "https"
18
-
if s.config.Dev {
18
+
if s.config.Core.Dev {
19
19
scheme = "http"
20
20
}
21
21
targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, knot, user.DID, repo, r.URL.RawQuery)
···
52
52
repo := chi.URLParam(r, "repo")
53
53
54
54
scheme := "https"
55
-
if s.config.Dev {
55
+
if s.config.Core.Dev {
56
56
scheme = "http"
57
57
}
58
58
targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, knot, user.DID, repo, r.URL.RawQuery)
+2
-2
appview/state/middleware.go
+2
-2
appview/state/middleware.go
···
20
20
return func(next http.Handler) http.Handler {
21
21
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22
22
// requires auth also
23
-
actor := s.auth.GetUser(r)
23
+
actor := s.oauth.GetUser(r)
24
24
if actor == nil {
25
25
// we need a logged in user
26
26
log.Printf("not logged in, redirecting")
···
54
54
return func(next http.Handler) http.Handler {
55
55
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
56
56
// requires auth also
57
-
actor := s.auth.GetUser(r)
57
+
actor := s.oauth.GetUser(r)
58
58
if actor == nil {
59
59
// we need a logged in user
60
60
log.Printf("not logged in, redirecting")
+17
-12
appview/state/profile.go
+17
-12
appview/state/profile.go
···
119
119
log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err)
120
120
}
121
121
122
-
loggedInUser := s.auth.GetUser(r)
122
+
loggedInUser := s.oauth.GetUser(r)
123
123
followStatus := db.IsNotFollowing
124
124
if loggedInUser != nil {
125
125
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
···
161
161
log.Printf("getting repos for %s: %s", ident.DID.String(), err)
162
162
}
163
163
164
-
loggedInUser := s.auth.GetUser(r)
164
+
loggedInUser := s.oauth.GetUser(r)
165
165
followStatus := db.IsNotFollowing
166
166
if loggedInUser != nil {
167
167
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
···
190
190
}
191
191
192
192
func (s *State) GetAvatarUri(handle string) string {
193
-
secret := s.config.AvatarSharedSecret
193
+
secret := s.config.Avatar.SharedSecret
194
194
h := hmac.New(sha256.New, []byte(secret))
195
195
h.Write([]byte(handle))
196
196
signature := hex.EncodeToString(h.Sum(nil))
197
-
return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle)
197
+
return fmt.Sprintf("%s/%s/%s", s.config.Avatar.Host, signature, handle)
198
198
}
199
199
200
200
func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) {
201
-
user := s.auth.GetUser(r)
201
+
user := s.oauth.GetUser(r)
202
202
203
203
err := r.ParseForm()
204
204
if err != nil {
···
246
246
}
247
247
248
248
func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) {
249
-
user := s.auth.GetUser(r)
249
+
user := s.oauth.GetUser(r)
250
250
251
251
err := r.ParseForm()
252
252
if err != nil {
···
286
286
}
287
287
288
288
func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) {
289
-
user := s.auth.GetUser(r)
289
+
user := s.oauth.GetUser(r)
290
290
tx, err := s.db.BeginTx(r.Context(), nil)
291
291
if err != nil {
292
292
log.Println("failed to start transaction", err)
···
294
294
return
295
295
}
296
296
297
-
client, _ := s.auth.AuthorizedClient(r)
297
+
client, err := s.oauth.AuthorizedClient(r)
298
+
if err != nil {
299
+
log.Println("failed to get authorized client", err)
300
+
s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.")
301
+
return
302
+
}
298
303
299
304
// yeah... lexgen dose not support syntax.ATURI in the record for some reason,
300
305
// nor does it support exact size arrays
···
308
313
vanityStats = append(vanityStats, string(v.Kind))
309
314
}
310
315
311
-
ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self")
316
+
ex, _ := client.RepoGetRecord(r.Context(), "", tangled.ActorProfileNSID, user.Did, "self")
312
317
var cid *string
313
318
if ex != nil {
314
319
cid = ex.Cid
315
320
}
316
321
317
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
322
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
318
323
Collection: tangled.ActorProfileNSID,
319
324
Repo: user.Did,
320
325
Rkey: "self",
···
347
352
}
348
353
349
354
func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) {
350
-
user := s.auth.GetUser(r)
355
+
user := s.oauth.GetUser(r)
351
356
352
357
profile, err := db.GetProfile(s.db, user.Did)
353
358
if err != nil {
···
361
366
}
362
367
363
368
func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) {
364
-
user := s.auth.GetUser(r)
369
+
user := s.oauth.GetUser(r)
365
370
366
371
profile, err := db.GetProfile(s.db, user.Did)
367
372
if err != nil {
+76
-51
appview/state/pull.go
+76
-51
appview/state/pull.go
···
13
13
14
14
"tangled.sh/tangled.sh/core/api/tangled"
15
15
"tangled.sh/tangled.sh/core/appview"
16
-
"tangled.sh/tangled.sh/core/appview/auth"
17
16
"tangled.sh/tangled.sh/core/appview/db"
17
+
"tangled.sh/tangled.sh/core/appview/oauth"
18
18
"tangled.sh/tangled.sh/core/appview/pages"
19
19
"tangled.sh/tangled.sh/core/patchutil"
20
20
"tangled.sh/tangled.sh/core/types"
···
29
29
func (s *State) PullActions(w http.ResponseWriter, r *http.Request) {
30
30
switch r.Method {
31
31
case http.MethodGet:
32
-
user := s.auth.GetUser(r)
32
+
user := s.oauth.GetUser(r)
33
33
f, err := s.fullyResolvedRepo(r)
34
34
if err != nil {
35
35
log.Println("failed to get repo and knot", err)
···
73
73
}
74
74
75
75
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
76
-
user := s.auth.GetUser(r)
76
+
user := s.oauth.GetUser(r)
77
77
f, err := s.fullyResolvedRepo(r)
78
78
if err != nil {
79
79
log.Println("failed to get repo and knot", err)
···
143
143
}
144
144
}
145
145
146
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
146
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
147
147
if err != nil {
148
148
log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err)
149
149
return types.MergeCheckResponse{
···
215
215
repoName = f.RepoName
216
216
}
217
217
218
-
us, err := NewUnsignedClient(knot, s.config.Dev)
218
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
219
219
if err != nil {
220
220
log.Printf("failed to setup client for %s; ignoring: %v", knot, err)
221
221
return pages.Unknown
···
250
250
}
251
251
252
252
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
253
-
user := s.auth.GetUser(r)
253
+
user := s.oauth.GetUser(r)
254
254
f, err := s.fullyResolvedRepo(r)
255
255
if err != nil {
256
256
log.Println("failed to get repo and knot", err)
···
298
298
}
299
299
300
300
func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) {
301
-
user := s.auth.GetUser(r)
301
+
user := s.oauth.GetUser(r)
302
302
303
303
f, err := s.fullyResolvedRepo(r)
304
304
if err != nil {
···
355
355
interdiff := patchutil.Interdiff(previousPatch, currentPatch)
356
356
357
357
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
358
-
LoggedInUser: s.auth.GetUser(r),
358
+
LoggedInUser: s.oauth.GetUser(r),
359
359
RepoInfo: f.RepoInfo(s, user),
360
360
Pull: pull,
361
361
Round: roundIdInt,
···
397
397
}
398
398
399
399
func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) {
400
-
user := s.auth.GetUser(r)
400
+
user := s.oauth.GetUser(r)
401
401
params := r.URL.Query()
402
402
403
403
state := db.PullOpen
···
451
451
}
452
452
453
453
s.pages.RepoPulls(w, pages.RepoPullsParams{
454
-
LoggedInUser: s.auth.GetUser(r),
454
+
LoggedInUser: s.oauth.GetUser(r),
455
455
RepoInfo: f.RepoInfo(s, user),
456
456
Pulls: pulls,
457
457
DidHandleMap: didHandleMap,
···
461
461
}
462
462
463
463
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
464
-
user := s.auth.GetUser(r)
464
+
user := s.oauth.GetUser(r)
465
465
f, err := s.fullyResolvedRepo(r)
466
466
if err != nil {
467
467
log.Println("failed to get repo and knot", err)
···
519
519
}
520
520
521
521
atUri := f.RepoAt.String()
522
-
client, _ := s.auth.AuthorizedClient(r)
523
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
522
+
client, err := s.oauth.AuthorizedClient(r)
523
+
if err != nil {
524
+
log.Println("failed to get authorized client", err)
525
+
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
526
+
return
527
+
}
528
+
atResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
524
529
Collection: tangled.RepoPullCommentNSID,
525
530
Repo: user.Did,
526
531
Rkey: appview.TID(),
···
568
573
}
569
574
570
575
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
571
-
user := s.auth.GetUser(r)
576
+
user := s.oauth.GetUser(r)
572
577
f, err := s.fullyResolvedRepo(r)
573
578
if err != nil {
574
579
log.Println("failed to get repo and knot", err)
···
577
582
578
583
switch r.Method {
579
584
case http.MethodGet:
580
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
585
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
581
586
if err != nil {
582
587
log.Printf("failed to create unsigned client for %s", f.Knot)
583
588
s.pages.Error503(w)
···
646
651
return
647
652
}
648
653
649
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
654
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
650
655
if err != nil {
651
656
log.Printf("failed to create unsigned client to %s: %v", f.Knot, err)
652
657
s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.")
···
689
694
}
690
695
}
691
696
692
-
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {
697
+
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) {
693
698
pullSource := &db.PullSource{
694
699
Branch: sourceBranch,
695
700
}
···
698
703
}
699
704
700
705
// Generate a patch using /compare
701
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
706
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
702
707
if err != nil {
703
708
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
704
709
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
723
728
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
724
729
}
725
730
726
-
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {
731
+
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) {
727
732
if !patchutil.IsPatchValid(patch) {
728
733
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
729
734
return
···
732
737
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
733
738
}
734
739
735
-
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
740
+
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
736
741
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
737
742
if errors.Is(err, sql.ErrNoRows) {
738
743
s.pages.Notice(w, "pull", "No such fork.")
···
750
755
return
751
756
}
752
757
753
-
sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev)
758
+
sc, err := NewSignedClient(fork.Knot, secret, s.config.Core.Dev)
754
759
if err != nil {
755
760
log.Println("failed to create signed client:", err)
756
761
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
757
762
return
758
763
}
759
764
760
-
us, err := NewUnsignedClient(fork.Knot, s.config.Dev)
765
+
us, err := NewUnsignedClient(fork.Knot, s.config.Core.Dev)
761
766
if err != nil {
762
767
log.Println("failed to create unsigned client:", err)
763
768
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
816
821
w http.ResponseWriter,
817
822
r *http.Request,
818
823
f *FullyResolvedRepo,
819
-
user *auth.User,
824
+
user *oauth.User,
820
825
title, body, targetBranch string,
821
826
patch string,
822
827
sourceRev string,
···
870
875
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
871
876
return
872
877
}
873
-
client, _ := s.auth.AuthorizedClient(r)
878
+
client, err := s.oauth.AuthorizedClient(r)
879
+
if err != nil {
880
+
log.Println("failed to get authorized client", err)
881
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
882
+
return
883
+
}
874
884
pullId, err := db.NextPullId(s.db, f.RepoAt)
875
885
if err != nil {
876
886
log.Println("failed to get pull id", err)
···
878
888
return
879
889
}
880
890
881
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
891
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
882
892
Collection: tangled.RepoPullNSID,
883
893
Repo: user.Did,
884
894
Rkey: rkey,
···
929
939
}
930
940
931
941
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
932
-
user := s.auth.GetUser(r)
942
+
user := s.oauth.GetUser(r)
933
943
f, err := s.fullyResolvedRepo(r)
934
944
if err != nil {
935
945
log.Println("failed to get repo and knot", err)
···
942
952
}
943
953
944
954
func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) {
945
-
user := s.auth.GetUser(r)
955
+
user := s.oauth.GetUser(r)
946
956
f, err := s.fullyResolvedRepo(r)
947
957
if err != nil {
948
958
log.Println("failed to get repo and knot", err)
949
959
return
950
960
}
951
961
952
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
962
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
953
963
if err != nil {
954
964
log.Printf("failed to create unsigned client for %s", f.Knot)
955
965
s.pages.Error503(w)
···
982
992
}
983
993
984
994
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
985
-
user := s.auth.GetUser(r)
995
+
user := s.oauth.GetUser(r)
986
996
f, err := s.fullyResolvedRepo(r)
987
997
if err != nil {
988
998
log.Println("failed to get repo and knot", err)
···
1002
1012
}
1003
1013
1004
1014
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
1005
-
user := s.auth.GetUser(r)
1015
+
user := s.oauth.GetUser(r)
1006
1016
1007
1017
f, err := s.fullyResolvedRepo(r)
1008
1018
if err != nil {
···
1019
1029
return
1020
1030
}
1021
1031
1022
-
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev)
1032
+
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Core.Dev)
1023
1033
if err != nil {
1024
1034
log.Printf("failed to create unsigned client for %s", repo.Knot)
1025
1035
s.pages.Error503(w)
···
1046
1056
return
1047
1057
}
1048
1058
1049
-
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
1059
+
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
1050
1060
if err != nil {
1051
1061
log.Printf("failed to create unsigned client for target knot %s", f.Knot)
1052
1062
s.pages.Error503(w)
···
1081
1091
}
1082
1092
1083
1093
func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
1084
-
user := s.auth.GetUser(r)
1094
+
user := s.oauth.GetUser(r)
1085
1095
f, err := s.fullyResolvedRepo(r)
1086
1096
if err != nil {
1087
1097
log.Println("failed to get repo and knot", err)
···
1117
1127
}
1118
1128
1119
1129
func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) {
1120
-
user := s.auth.GetUser(r)
1130
+
user := s.oauth.GetUser(r)
1121
1131
1122
1132
pull, ok := r.Context().Value("pull").(*db.Pull)
1123
1133
if !ok {
···
1159
1169
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.")
1160
1170
return
1161
1171
}
1162
-
client, _ := s.auth.AuthorizedClient(r)
1172
+
client, err := s.oauth.AuthorizedClient(r)
1173
+
if err != nil {
1174
+
log.Println("failed to get authorized client", err)
1175
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1176
+
return
1177
+
}
1163
1178
1164
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1179
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1165
1180
if err != nil {
1166
1181
// failed to get record
1167
1182
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1168
1183
return
1169
1184
}
1170
1185
1171
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1186
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1172
1187
Collection: tangled.RepoPullNSID,
1173
1188
Repo: user.Did,
1174
1189
Rkey: pull.Rkey,
···
1200
1215
}
1201
1216
1202
1217
func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) {
1203
-
user := s.auth.GetUser(r)
1218
+
user := s.oauth.GetUser(r)
1204
1219
1205
1220
pull, ok := r.Context().Value("pull").(*db.Pull)
1206
1221
if !ok {
···
1227
1242
return
1228
1243
}
1229
1244
1230
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
1245
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
1231
1246
if err != nil {
1232
1247
log.Printf("failed to create client for %s: %s", f.Knot, err)
1233
1248
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
1268
1283
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1269
1284
return
1270
1285
}
1271
-
client, _ := s.auth.AuthorizedClient(r)
1286
+
client, err := s.oauth.AuthorizedClient(r)
1287
+
if err != nil {
1288
+
log.Println("failed to authorize client")
1289
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1290
+
return
1291
+
}
1272
1292
1273
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1293
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1274
1294
if err != nil {
1275
1295
// failed to get record
1276
1296
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
1280
1300
recordPullSource := &tangled.RepoPull_Source{
1281
1301
Branch: pull.PullSource.Branch,
1282
1302
}
1283
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1303
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1284
1304
Collection: tangled.RepoPullNSID,
1285
1305
Repo: user.Did,
1286
1306
Rkey: pull.Rkey,
···
1313
1333
}
1314
1334
1315
1335
func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) {
1316
-
user := s.auth.GetUser(r)
1336
+
user := s.oauth.GetUser(r)
1317
1337
1318
1338
pull, ok := r.Context().Value("pull").(*db.Pull)
1319
1339
if !ok {
···
1342
1362
}
1343
1363
1344
1364
// extract patch by performing compare
1345
-
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev)
1365
+
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev)
1346
1366
if err != nil {
1347
1367
log.Printf("failed to create client for %s: %s", forkRepo.Knot, err)
1348
1368
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
1357
1377
}
1358
1378
1359
1379
// update the hidden tracking branch to latest
1360
-
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev)
1380
+
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev)
1361
1381
if err != nil {
1362
1382
log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err)
1363
1383
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
1406
1426
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1407
1427
return
1408
1428
}
1409
-
client, _ := s.auth.AuthorizedClient(r)
1429
+
client, err := s.oauth.AuthorizedClient(r)
1430
+
if err != nil {
1431
+
log.Println("failed to get client")
1432
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1433
+
return
1434
+
}
1410
1435
1411
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1436
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1412
1437
if err != nil {
1413
1438
// failed to get record
1414
1439
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
1420
1445
Branch: pull.PullSource.Branch,
1421
1446
Repo: &repoAt,
1422
1447
}
1423
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1448
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1424
1449
Collection: tangled.RepoPullNSID,
1425
1450
Repo: user.Did,
1426
1451
Rkey: pull.Rkey,
···
1503
1528
log.Printf("failed to get primary email: %s", err)
1504
1529
}
1505
1530
1506
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
1531
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
1507
1532
if err != nil {
1508
1533
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
1509
1534
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
1533
1558
}
1534
1559
1535
1560
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
1536
-
user := s.auth.GetUser(r)
1561
+
user := s.oauth.GetUser(r)
1537
1562
1538
1563
f, err := s.fullyResolvedRepo(r)
1539
1564
if err != nil {
···
1587
1612
}
1588
1613
1589
1614
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
1590
-
user := s.auth.GetUser(r)
1615
+
user := s.oauth.GetUser(r)
1591
1616
1592
1617
f, err := s.fullyResolvedRepo(r)
1593
1618
if err != nil {
+98
-60
appview/state/repo.go
+98
-60
appview/state/repo.go
···
18
18
19
19
"tangled.sh/tangled.sh/core/api/tangled"
20
20
"tangled.sh/tangled.sh/core/appview"
21
-
"tangled.sh/tangled.sh/core/appview/auth"
22
21
"tangled.sh/tangled.sh/core/appview/db"
22
+
"tangled.sh/tangled.sh/core/appview/oauth"
23
23
"tangled.sh/tangled.sh/core/appview/pages"
24
24
"tangled.sh/tangled.sh/core/appview/pages/markup"
25
25
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
···
45
45
return
46
46
}
47
47
48
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
48
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
49
49
if err != nil {
50
50
log.Printf("failed to create unsigned client for %s", f.Knot)
51
51
s.pages.Error503(w)
···
119
119
120
120
emails := uniqueEmails(commitsTrunc)
121
121
122
-
user := s.auth.GetUser(r)
122
+
user := s.oauth.GetUser(r)
123
123
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
124
124
LoggedInUser: user,
125
125
RepoInfo: f.RepoInfo(s, user),
···
150
150
151
151
ref := chi.URLParam(r, "ref")
152
152
153
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
153
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
154
154
if err != nil {
155
155
log.Println("failed to create unsigned client", err)
156
156
return
···
190
190
tagMap[hash] = append(tagMap[hash], tag.Name)
191
191
}
192
192
193
-
user := s.auth.GetUser(r)
193
+
user := s.oauth.GetUser(r)
194
194
s.pages.RepoLog(w, pages.RepoLogParams{
195
195
LoggedInUser: user,
196
196
TagMap: tagMap,
···
209
209
return
210
210
}
211
211
212
-
user := s.auth.GetUser(r)
212
+
user := s.oauth.GetUser(r)
213
213
s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
214
214
RepoInfo: f.RepoInfo(s, user),
215
215
})
···
232
232
return
233
233
}
234
234
235
-
user := s.auth.GetUser(r)
235
+
user := s.oauth.GetUser(r)
236
236
237
237
switch r.Method {
238
238
case http.MethodGet:
···
241
241
})
242
242
return
243
243
case http.MethodPut:
244
-
user := s.auth.GetUser(r)
244
+
user := s.oauth.GetUser(r)
245
245
newDescription := r.FormValue("description")
246
-
client, _ := s.auth.AuthorizedClient(r)
246
+
client, err := s.oauth.AuthorizedClient(r)
247
+
if err != nil {
248
+
log.Println("failed to get client")
249
+
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
250
+
return
251
+
}
247
252
248
253
// optimistic update
249
254
err = db.UpdateDescription(s.db, string(repoAt), newDescription)
···
256
261
// this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field
257
262
//
258
263
// SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests
259
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey)
264
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey)
260
265
if err != nil {
261
266
// failed to get record
262
267
s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
263
268
return
264
269
}
265
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
270
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
266
271
Collection: tangled.RepoNSID,
267
272
Repo: user.Did,
268
273
Rkey: rkey,
···
303
308
}
304
309
ref := chi.URLParam(r, "ref")
305
310
protocol := "http"
306
-
if !s.config.Dev {
311
+
if !s.config.Core.Dev {
307
312
protocol = "https"
308
313
}
309
314
···
331
336
return
332
337
}
333
338
334
-
user := s.auth.GetUser(r)
339
+
user := s.oauth.GetUser(r)
335
340
s.pages.RepoCommit(w, pages.RepoCommitParams{
336
341
LoggedInUser: user,
337
342
RepoInfo: f.RepoInfo(s, user),
···
351
356
ref := chi.URLParam(r, "ref")
352
357
treePath := chi.URLParam(r, "*")
353
358
protocol := "http"
354
-
if !s.config.Dev {
359
+
if !s.config.Core.Dev {
355
360
protocol = "https"
356
361
}
357
362
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath))
···
380
385
return
381
386
}
382
387
383
-
user := s.auth.GetUser(r)
388
+
user := s.oauth.GetUser(r)
384
389
385
390
var breadcrumbs [][]string
386
391
breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
411
416
return
412
417
}
413
418
414
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
419
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
415
420
if err != nil {
416
421
log.Println("failed to create unsigned client", err)
417
422
return
···
451
456
}
452
457
}
453
458
454
-
user := s.auth.GetUser(r)
459
+
user := s.oauth.GetUser(r)
455
460
s.pages.RepoTags(w, pages.RepoTagsParams{
456
461
LoggedInUser: user,
457
462
RepoInfo: f.RepoInfo(s, user),
···
469
474
return
470
475
}
471
476
472
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
477
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
473
478
if err != nil {
474
479
log.Println("failed to create unsigned client", err)
475
480
return
···
511
516
return strings.Compare(a.Name, b.Name) * -1
512
517
})
513
518
514
-
user := s.auth.GetUser(r)
519
+
user := s.oauth.GetUser(r)
515
520
s.pages.RepoBranches(w, pages.RepoBranchesParams{
516
521
LoggedInUser: user,
517
522
RepoInfo: f.RepoInfo(s, user),
···
530
535
ref := chi.URLParam(r, "ref")
531
536
filePath := chi.URLParam(r, "*")
532
537
protocol := "http"
533
-
if !s.config.Dev {
538
+
if !s.config.Core.Dev {
534
539
protocol = "https"
535
540
}
536
541
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
568
573
showRendered = r.URL.Query().Get("code") != "true"
569
574
}
570
575
571
-
user := s.auth.GetUser(r)
576
+
user := s.oauth.GetUser(r)
572
577
s.pages.RepoBlob(w, pages.RepoBlobParams{
573
578
LoggedInUser: user,
574
579
RepoInfo: f.RepoInfo(s, user),
···
591
596
filePath := chi.URLParam(r, "*")
592
597
593
598
protocol := "http"
594
-
if !s.config.Dev {
599
+
if !s.config.Core.Dev {
595
600
protocol = "https"
596
601
}
597
602
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
652
657
return
653
658
}
654
659
655
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
660
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
656
661
if err != nil {
657
662
log.Println("failed to create client to ", f.Knot)
658
663
return
···
714
719
}
715
720
716
721
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
717
-
user := s.auth.GetUser(r)
722
+
user := s.oauth.GetUser(r)
718
723
719
724
f, err := s.fullyResolvedRepo(r)
720
725
if err != nil {
···
723
728
}
724
729
725
730
// remove record from pds
726
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
731
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
732
+
if err != nil {
733
+
log.Println("failed to get authorized client", err)
734
+
return
735
+
}
727
736
repoRkey := f.RepoAt.RecordKey().String()
728
-
_, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{
737
+
_, err = xrpcClient.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
729
738
Collection: tangled.RepoNSID,
730
739
Repo: user.Did,
731
740
Rkey: repoRkey,
···
743
752
return
744
753
}
745
754
746
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
755
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
747
756
if err != nil {
748
757
log.Println("failed to create client to ", f.Knot)
749
758
return
···
838
847
return
839
848
}
840
849
841
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
850
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
842
851
if err != nil {
843
852
log.Println("failed to create client to ", f.Knot)
844
853
return
···
868
877
switch r.Method {
869
878
case http.MethodGet:
870
879
// for now, this is just pubkeys
871
-
user := s.auth.GetUser(r)
880
+
user := s.oauth.GetUser(r)
872
881
repoCollaborators, err := f.Collaborators(r.Context(), s)
873
882
if err != nil {
874
883
log.Println("failed to get collaborators", err)
···
884
893
885
894
var branchNames []string
886
895
var defaultBranch string
887
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
896
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
888
897
if err != nil {
889
898
log.Println("failed to create unsigned client", err)
890
899
} else {
···
1008
1017
return collaborators, nil
1009
1018
}
1010
1019
1011
-
func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo {
1020
+
func (f *FullyResolvedRepo) RepoInfo(s *State, u *oauth.User) repoinfo.RepoInfo {
1012
1021
isStarred := false
1013
1022
if u != nil {
1014
1023
isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt))
···
1051
1060
1052
1061
knot := f.Knot
1053
1062
var disableFork bool
1054
-
us, err := NewUnsignedClient(knot, s.config.Dev)
1063
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
1055
1064
if err != nil {
1056
1065
log.Printf("failed to create unsigned client for %s: %v", knot, err)
1057
1066
} else {
···
1105
1114
}
1106
1115
1107
1116
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
1108
-
user := s.auth.GetUser(r)
1117
+
user := s.oauth.GetUser(r)
1109
1118
f, err := s.fullyResolvedRepo(r)
1110
1119
if err != nil {
1111
1120
log.Println("failed to get repo and knot", err)
···
1159
1168
}
1160
1169
1161
1170
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
1162
-
user := s.auth.GetUser(r)
1171
+
user := s.oauth.GetUser(r)
1163
1172
f, err := s.fullyResolvedRepo(r)
1164
1173
if err != nil {
1165
1174
log.Println("failed to get repo and knot", err)
···
1195
1204
1196
1205
closed := tangled.RepoIssueStateClosed
1197
1206
1198
-
client, _ := s.auth.AuthorizedClient(r)
1199
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1207
+
client, err := s.oauth.AuthorizedClient(r)
1208
+
if err != nil {
1209
+
log.Println("failed to get authorized client", err)
1210
+
return
1211
+
}
1212
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1200
1213
Collection: tangled.RepoIssueStateNSID,
1201
1214
Repo: user.Did,
1202
1215
Rkey: appview.TID(),
···
1214
1227
return
1215
1228
}
1216
1229
1217
-
err := db.CloseIssue(s.db, f.RepoAt, issueIdInt)
1230
+
err = db.CloseIssue(s.db, f.RepoAt, issueIdInt)
1218
1231
if err != nil {
1219
1232
log.Println("failed to close issue", err)
1220
1233
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
1231
1244
}
1232
1245
1233
1246
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
1234
-
user := s.auth.GetUser(r)
1247
+
user := s.oauth.GetUser(r)
1235
1248
f, err := s.fullyResolvedRepo(r)
1236
1249
if err != nil {
1237
1250
log.Println("failed to get repo and knot", err)
···
1279
1292
}
1280
1293
1281
1294
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
1282
-
user := s.auth.GetUser(r)
1295
+
user := s.oauth.GetUser(r)
1283
1296
f, err := s.fullyResolvedRepo(r)
1284
1297
if err != nil {
1285
1298
log.Println("failed to get repo and knot", err)
···
1330
1343
}
1331
1344
1332
1345
atUri := f.RepoAt.String()
1333
-
client, _ := s.auth.AuthorizedClient(r)
1334
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1346
+
client, err := s.oauth.AuthorizedClient(r)
1347
+
if err != nil {
1348
+
log.Println("failed to get authorized client", err)
1349
+
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1350
+
return
1351
+
}
1352
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1335
1353
Collection: tangled.RepoIssueCommentNSID,
1336
1354
Repo: user.Did,
1337
1355
Rkey: rkey,
···
1358
1376
}
1359
1377
1360
1378
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
1361
-
user := s.auth.GetUser(r)
1379
+
user := s.oauth.GetUser(r)
1362
1380
f, err := s.fullyResolvedRepo(r)
1363
1381
if err != nil {
1364
1382
log.Println("failed to get repo and knot", err)
···
1417
1435
}
1418
1436
1419
1437
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
1420
-
user := s.auth.GetUser(r)
1438
+
user := s.oauth.GetUser(r)
1421
1439
f, err := s.fullyResolvedRepo(r)
1422
1440
if err != nil {
1423
1441
log.Println("failed to get repo and knot", err)
···
1469
1487
case http.MethodPost:
1470
1488
// extract form value
1471
1489
newBody := r.FormValue("body")
1472
-
client, _ := s.auth.AuthorizedClient(r)
1490
+
client, err := s.oauth.AuthorizedClient(r)
1491
+
if err != nil {
1492
+
log.Println("failed to get authorized client", err)
1493
+
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1494
+
return
1495
+
}
1473
1496
rkey := comment.Rkey
1474
1497
1475
1498
// optimistic update
···
1484
1507
// rkey is optional, it was introduced later
1485
1508
if comment.Rkey != "" {
1486
1509
// update the record on pds
1487
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey)
1510
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoIssueCommentNSID, user.Did, rkey)
1488
1511
if err != nil {
1489
1512
// failed to get record
1490
1513
log.Println(err, rkey)
···
1499
1522
createdAt := record["createdAt"].(string)
1500
1523
commentIdInt64 := int64(commentIdInt)
1501
1524
1502
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1525
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1503
1526
Collection: tangled.RepoIssueCommentNSID,
1504
1527
Repo: user.Did,
1505
1528
Rkey: rkey,
···
1542
1565
}
1543
1566
1544
1567
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
1545
-
user := s.auth.GetUser(r)
1568
+
user := s.oauth.GetUser(r)
1546
1569
f, err := s.fullyResolvedRepo(r)
1547
1570
if err != nil {
1548
1571
log.Println("failed to get repo and knot", err)
···
1599
1622
1600
1623
// delete from pds
1601
1624
if comment.Rkey != "" {
1602
-
client, _ := s.auth.AuthorizedClient(r)
1603
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
1625
+
client, err := s.oauth.AuthorizedClient(r)
1626
+
if err != nil {
1627
+
log.Println("failed to get authorized client", err)
1628
+
s.pages.Notice(w, "issue-comment", "Failed to delete comment.")
1629
+
return
1630
+
}
1631
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
1604
1632
Collection: tangled.GraphFollowNSID,
1605
1633
Repo: user.Did,
1606
1634
Rkey: comment.Rkey,
···
1647
1675
page = pagination.FirstPage()
1648
1676
}
1649
1677
1650
-
user := s.auth.GetUser(r)
1678
+
user := s.oauth.GetUser(r)
1651
1679
f, err := s.fullyResolvedRepo(r)
1652
1680
if err != nil {
1653
1681
log.Println("failed to get repo and knot", err)
···
1676
1704
}
1677
1705
1678
1706
s.pages.RepoIssues(w, pages.RepoIssuesParams{
1679
-
LoggedInUser: s.auth.GetUser(r),
1707
+
LoggedInUser: s.oauth.GetUser(r),
1680
1708
RepoInfo: f.RepoInfo(s, user),
1681
1709
Issues: issues,
1682
1710
DidHandleMap: didHandleMap,
···
1687
1715
}
1688
1716
1689
1717
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
1690
-
user := s.auth.GetUser(r)
1718
+
user := s.oauth.GetUser(r)
1691
1719
1692
1720
f, err := s.fullyResolvedRepo(r)
1693
1721
if err != nil {
···
1735
1763
return
1736
1764
}
1737
1765
1738
-
client, _ := s.auth.AuthorizedClient(r)
1766
+
client, err := s.oauth.AuthorizedClient(r)
1767
+
if err != nil {
1768
+
log.Println("failed to get authorized client", err)
1769
+
s.pages.Notice(w, "issues", "Failed to create issue.")
1770
+
return
1771
+
}
1739
1772
atUri := f.RepoAt.String()
1740
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1773
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1741
1774
Collection: tangled.RepoIssueNSID,
1742
1775
Repo: user.Did,
1743
1776
Rkey: appview.TID(),
···
1770
1803
}
1771
1804
1772
1805
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
1773
-
user := s.auth.GetUser(r)
1806
+
user := s.oauth.GetUser(r)
1774
1807
f, err := s.fullyResolvedRepo(r)
1775
1808
if err != nil {
1776
1809
log.Printf("failed to resolve source repo: %v", err)
···
1779
1812
1780
1813
switch r.Method {
1781
1814
case http.MethodGet:
1782
-
user := s.auth.GetUser(r)
1815
+
user := s.oauth.GetUser(r)
1783
1816
knots, err := s.enforcer.GetDomainsForUser(user.Did)
1784
1817
if err != nil {
1785
1818
s.pages.Notice(w, "repo", "Invalid user account.")
···
1829
1862
return
1830
1863
}
1831
1864
1832
-
client, err := NewSignedClient(knot, secret, s.config.Dev)
1865
+
client, err := NewSignedClient(knot, secret, s.config.Core.Dev)
1833
1866
if err != nil {
1834
1867
s.pages.Notice(w, "repo", "Failed to reach knot server.")
1835
1868
return
1836
1869
}
1837
1870
1838
1871
var uri string
1839
-
if s.config.Dev {
1872
+
if s.config.Core.Dev {
1840
1873
uri = "http"
1841
1874
} else {
1842
1875
uri = "https"
···
1883
1916
// continue
1884
1917
}
1885
1918
1886
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
1919
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
1920
+
if err != nil {
1921
+
log.Println("failed to get authorized client", err)
1922
+
s.pages.Notice(w, "repo", "Failed to create repository.")
1923
+
return
1924
+
}
1887
1925
1888
1926
createdAt := time.Now().Format(time.RFC3339)
1889
-
atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{
1927
+
atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
1890
1928
Collection: tangled.RepoNSID,
1891
1929
Repo: user.Did,
1892
1930
Rkey: rkey,
+3
-3
appview/state/repo_util.go
+3
-3
appview/state/repo_util.go
···
12
12
"github.com/bluesky-social/indigo/atproto/syntax"
13
13
"github.com/go-chi/chi/v5"
14
14
"github.com/go-git/go-git/v5/plumbing/object"
15
-
"tangled.sh/tangled.sh/core/appview/auth"
16
15
"tangled.sh/tangled.sh/core/appview/db"
16
+
"tangled.sh/tangled.sh/core/appview/oauth"
17
17
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
18
18
)
19
19
···
45
45
ref := chi.URLParam(r, "ref")
46
46
47
47
if ref == "" {
48
-
us, err := NewUnsignedClient(knot, s.config.Dev)
48
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
49
49
if err != nil {
50
50
return nil, err
51
51
}
···
73
73
}, nil
74
74
}
75
75
76
-
func RolesInRepo(s *State, u *auth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo {
76
+
func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo {
77
77
if u != nil {
78
78
r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo())
79
79
return repoinfo.RolesInRepo{r}
+34
-19
appview/state/router.go
+34
-19
appview/state/router.go
···
5
5
"strings"
6
6
7
7
"github.com/go-chi/chi/v5"
8
+
"github.com/gorilla/sessions"
8
9
"tangled.sh/tangled.sh/core/appview/middleware"
10
+
oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler"
9
11
"tangled.sh/tangled.sh/core/appview/settings"
10
12
"tangled.sh/tangled.sh/core/appview/state/userutil"
11
13
)
···
67
69
r.Route("/tags", func(r chi.Router) {
68
70
r.Get("/", s.RepoTags)
69
71
r.Route("/{tag}", func(r chi.Router) {
70
-
r.Use(middleware.AuthMiddleware(s.auth))
72
+
r.Use(middleware.AuthMiddleware(s.oauth))
71
73
// require auth to download for now
72
74
r.Get("/download/{file}", s.DownloadArtifact)
73
75
···
90
92
r.Get("/{issue}", s.RepoSingleIssue)
91
93
92
94
r.Group(func(r chi.Router) {
93
-
r.Use(middleware.AuthMiddleware(s.auth))
95
+
r.Use(middleware.AuthMiddleware(s.oauth))
94
96
r.Get("/new", s.NewIssue)
95
97
r.Post("/new", s.NewIssue)
96
98
r.Post("/{issue}/comment", s.NewIssueComment)
···
106
108
})
107
109
108
110
r.Route("/fork", func(r chi.Router) {
109
-
r.Use(middleware.AuthMiddleware(s.auth))
111
+
r.Use(middleware.AuthMiddleware(s.oauth))
110
112
r.Get("/", s.ForkRepo)
111
113
r.Post("/", s.ForkRepo)
112
114
})
113
115
114
116
r.Route("/pulls", func(r chi.Router) {
115
117
r.Get("/", s.RepoPulls)
116
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) {
118
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) {
117
119
r.Get("/", s.NewPull)
118
120
r.Get("/patch-upload", s.PatchUploadFragment)
119
121
r.Post("/validate-patch", s.ValidatePatch)
···
131
133
r.Get("/", s.RepoPullPatch)
132
134
r.Get("/interdiff", s.RepoPullInterdiff)
133
135
r.Get("/actions", s.PullActions)
134
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) {
136
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) {
135
137
r.Get("/", s.PullComment)
136
138
r.Post("/", s.PullComment)
137
139
})
···
142
144
})
143
145
144
146
r.Group(func(r chi.Router) {
145
-
r.Use(middleware.AuthMiddleware(s.auth))
147
+
r.Use(middleware.AuthMiddleware(s.oauth))
146
148
r.Route("/resubmit", func(r chi.Router) {
147
149
r.Get("/", s.ResubmitPull)
148
150
r.Post("/", s.ResubmitPull)
···
165
167
166
168
// settings routes, needs auth
167
169
r.Group(func(r chi.Router) {
168
-
r.Use(middleware.AuthMiddleware(s.auth))
170
+
r.Use(middleware.AuthMiddleware(s.oauth))
169
171
// repo description can only be edited by owner
170
172
r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) {
171
173
r.Put("/", s.RepoDescription)
···
196
198
197
199
r.Get("/", s.Timeline)
198
200
199
-
r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout)
201
+
r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout)
200
202
201
-
r.Route("/login", func(r chi.Router) {
202
-
r.Get("/", s.Login)
203
-
r.Post("/", s.Login)
204
-
})
203
+
// r.Route("/login", func(r chi.Router) {
204
+
// r.Get("/", s.Login)
205
+
// r.Post("/", s.Login)
206
+
// })
205
207
206
208
r.Route("/knots", func(r chi.Router) {
207
-
r.Use(middleware.AuthMiddleware(s.auth))
209
+
r.Use(middleware.AuthMiddleware(s.oauth))
208
210
r.Get("/", s.Knots)
209
211
r.Post("/key", s.RegistrationKey)
210
212
···
222
224
223
225
r.Route("/repo", func(r chi.Router) {
224
226
r.Route("/new", func(r chi.Router) {
225
-
r.Use(middleware.AuthMiddleware(s.auth))
227
+
r.Use(middleware.AuthMiddleware(s.oauth))
226
228
r.Get("/", s.NewRepo)
227
229
r.Post("/", s.NewRepo)
228
230
})
229
231
// r.Post("/import", s.ImportRepo)
230
232
})
231
233
232
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) {
234
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) {
233
235
r.Post("/", s.Follow)
234
236
r.Delete("/", s.Follow)
235
237
})
236
238
237
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) {
239
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) {
238
240
r.Post("/", s.Star)
239
241
r.Delete("/", s.Star)
240
242
})
241
243
242
244
r.Route("/profile", func(r chi.Router) {
243
-
r.Use(middleware.AuthMiddleware(s.auth))
245
+
r.Use(middleware.AuthMiddleware(s.oauth))
244
246
r.Get("/edit-bio", s.EditBioFragment)
245
247
r.Get("/edit-pins", s.EditPinsFragment)
246
248
r.Post("/bio", s.UpdateProfileBio)
···
248
250
})
249
251
250
252
r.Mount("/settings", s.SettingsRouter())
251
-
253
+
r.Mount("/oauth", s.OAuthRouter())
252
254
r.Get("/keys/{user}", s.Keys)
253
255
254
256
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
···
257
259
return r
258
260
}
259
261
262
+
func (s *State) OAuthRouter() http.Handler {
263
+
oauth := &oauthhandler.OAuthHandler{
264
+
Config: s.config,
265
+
Pages: s.pages,
266
+
Resolver: s.resolver,
267
+
Db: s.db,
268
+
Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)),
269
+
OAuth: s.oauth,
270
+
}
271
+
272
+
return oauth.Router()
273
+
}
274
+
260
275
func (s *State) SettingsRouter() http.Handler {
261
276
settings := &settings.Settings{
262
277
Db: s.db,
263
-
Auth: s.auth,
278
+
OAuth: s.oauth,
264
279
Pages: s.pages,
265
280
Config: s.config,
266
281
}
+8
-4
appview/state/star.go
+8
-4
appview/state/star.go
···
15
15
)
16
16
17
17
func (s *State) Star(w http.ResponseWriter, r *http.Request) {
18
-
currentUser := s.auth.GetUser(r)
18
+
currentUser := s.oauth.GetUser(r)
19
19
20
20
subject := r.URL.Query().Get("subject")
21
21
if subject == "" {
···
29
29
return
30
30
}
31
31
32
-
client, _ := s.auth.AuthorizedClient(r)
32
+
client, err := s.oauth.AuthorizedClient(r)
33
+
if err != nil {
34
+
log.Println("failed to authorize client", err)
35
+
return
36
+
}
33
37
34
38
switch r.Method {
35
39
case http.MethodPost:
36
40
createdAt := time.Now().Format(time.RFC3339)
37
41
rkey := appview.TID()
38
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
42
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
39
43
Collection: tangled.FeedStarNSID,
40
44
Repo: currentUser.Did,
41
45
Rkey: rkey,
···
80
84
return
81
85
}
82
86
83
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
87
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
84
88
Collection: tangled.FeedStarNSID,
85
89
Repo: currentUser.Did,
86
90
Rkey: star.Rkey,
+98
-85
appview/state/state.go
+98
-85
appview/state/state.go
···
21
21
"tangled.sh/tangled.sh/core/appview"
22
22
"tangled.sh/tangled.sh/core/appview/auth"
23
23
"tangled.sh/tangled.sh/core/appview/db"
24
+
"tangled.sh/tangled.sh/core/appview/oauth"
24
25
"tangled.sh/tangled.sh/core/appview/pages"
25
26
"tangled.sh/tangled.sh/core/jetstream"
26
27
"tangled.sh/tangled.sh/core/rbac"
···
29
30
type State struct {
30
31
db *db.DB
31
32
auth *auth.Auth
33
+
oauth *oauth.OAuth
32
34
enforcer *rbac.Enforcer
33
-
tidClock *syntax.TIDClock
35
+
tidClock syntax.TIDClock
34
36
pages *pages.Pages
35
37
resolver *appview.Resolver
36
38
jc *jetstream.JetstreamClient
···
38
40
}
39
41
40
42
func Make(config *appview.Config) (*State, error) {
41
-
d, err := db.Make(config.DbPath)
43
+
d, err := db.Make(config.Core.DbPath)
42
44
if err != nil {
43
45
return nil, err
44
46
}
45
47
46
-
auth, err := auth.Make(config.CookieSecret)
48
+
auth, err := auth.Make(config.Core.CookieSecret)
47
49
if err != nil {
48
50
return nil, err
49
51
}
50
52
51
-
enforcer, err := rbac.NewEnforcer(config.DbPath)
53
+
enforcer, err := rbac.NewEnforcer(config.Core.DbPath)
52
54
if err != nil {
53
55
return nil, err
54
56
}
···
58
60
pgs := pages.NewPages(config)
59
61
60
62
resolver := appview.NewResolver()
63
+
64
+
oauth := oauth.NewOAuth(d, config)
61
65
62
66
wrapper := db.DbWrapper{d}
63
67
jc, err := jetstream.NewJetstreamClient(
64
-
config.JetstreamEndpoint,
68
+
config.Jetstream.Endpoint,
65
69
"appview",
66
70
[]string{
67
71
tangled.GraphFollowNSID,
···
86
90
state := &State{
87
91
d,
88
92
auth,
93
+
oauth,
89
94
enforcer,
90
95
clock,
91
96
pgs,
···
101
106
return c.Next().String()
102
107
}
103
108
104
-
func (s *State) Login(w http.ResponseWriter, r *http.Request) {
105
-
ctx := r.Context()
109
+
// func (s *State) Login(w http.ResponseWriter, r *http.Request) {
110
+
// ctx := r.Context()
106
111
107
-
switch r.Method {
108
-
case http.MethodGet:
109
-
err := s.pages.Login(w, pages.LoginParams{})
110
-
if err != nil {
111
-
log.Printf("rendering login page: %s", err)
112
-
}
112
+
// switch r.Method {
113
+
// case http.MethodGet:
114
+
// err := s.pages.Login(w, pages.LoginParams{})
115
+
// if err != nil {
116
+
// log.Printf("rendering login page: %s", err)
117
+
// }
113
118
114
-
return
115
-
case http.MethodPost:
116
-
handle := strings.TrimPrefix(r.FormValue("handle"), "@")
117
-
appPassword := r.FormValue("app_password")
119
+
// return
120
+
// case http.MethodPost:
121
+
// handle := strings.TrimPrefix(r.FormValue("handle"), "@")
122
+
// appPassword := r.FormValue("app_password")
118
123
119
-
resolved, err := s.resolver.ResolveIdent(ctx, handle)
120
-
if err != nil {
121
-
log.Println("failed to resolve handle:", err)
122
-
s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
123
-
return
124
-
}
124
+
// resolved, err := s.resolver.ResolveIdent(ctx, handle)
125
+
// if err != nil {
126
+
// log.Println("failed to resolve handle:", err)
127
+
// s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
128
+
// return
129
+
// }
125
130
126
-
atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword)
127
-
if err != nil {
128
-
s.pages.Notice(w, "login-msg", "Invalid handle or password.")
129
-
return
130
-
}
131
-
sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession}
131
+
// atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword)
132
+
// if err != nil {
133
+
// s.pages.Notice(w, "login-msg", "Invalid handle or password.")
134
+
// return
135
+
// }
136
+
// sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession}
132
137
133
-
err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint())
134
-
if err != nil {
135
-
s.pages.Notice(w, "login-msg", "Failed to login, try again later.")
136
-
return
137
-
}
138
+
// err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint())
139
+
// if err != nil {
140
+
// s.pages.Notice(w, "login-msg", "Failed to login, try again later.")
141
+
// return
142
+
// }
138
143
139
-
log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did)
144
+
// log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did)
140
145
141
-
did := resolved.DID.String()
142
-
defaultKnot := "knot1.tangled.sh"
146
+
// did := resolved.DID.String()
147
+
// defaultKnot := "knot1.tangled.sh"
143
148
144
-
go func() {
145
-
log.Printf("adding %s to default knot", did)
146
-
err = s.enforcer.AddMember(defaultKnot, did)
147
-
if err != nil {
148
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
149
-
return
150
-
}
151
-
err = s.enforcer.E.SavePolicy()
152
-
if err != nil {
153
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
154
-
return
155
-
}
149
+
// go func() {
150
+
// log.Printf("adding %s to default knot", did)
151
+
// err = s.enforcer.AddMember(defaultKnot, did)
152
+
// if err != nil {
153
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
154
+
// return
155
+
// }
156
+
// err = s.enforcer.E.SavePolicy()
157
+
// if err != nil {
158
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
159
+
// return
160
+
// }
156
161
157
-
secret, err := db.GetRegistrationKey(s.db, defaultKnot)
158
-
if err != nil {
159
-
log.Println("failed to get registration key for knot1.tangled.sh")
160
-
return
161
-
}
162
-
signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Dev)
163
-
resp, err := signedClient.AddMember(did)
164
-
if err != nil {
165
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
166
-
return
167
-
}
162
+
// secret, err := db.GetRegistrationKey(s.db, defaultKnot)
163
+
// if err != nil {
164
+
// log.Println("failed to get registration key for knot1.tangled.sh")
165
+
// return
166
+
// }
167
+
// signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev)
168
+
// resp, err := signedClient.AddMember(did)
169
+
// if err != nil {
170
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
171
+
// return
172
+
// }
168
173
169
-
if resp.StatusCode != http.StatusNoContent {
170
-
log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode)
171
-
return
172
-
}
173
-
}()
174
+
// if resp.StatusCode != http.StatusNoContent {
175
+
// log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode)
176
+
// return
177
+
// }
178
+
// }()
174
179
175
-
s.pages.HxRedirect(w, "/")
176
-
return
177
-
}
178
-
}
180
+
// s.pages.HxRedirect(w, "/")
181
+
// return
182
+
// }
183
+
// }
179
184
180
185
func (s *State) Logout(w http.ResponseWriter, r *http.Request) {
181
-
s.auth.ClearSession(r, w)
186
+
s.oauth.ClearSession(r, w)
182
187
w.Header().Set("HX-Redirect", "/login")
183
188
w.WriteHeader(http.StatusSeeOther)
184
189
}
185
190
186
191
func (s *State) Timeline(w http.ResponseWriter, r *http.Request) {
187
-
user := s.auth.GetUser(r)
192
+
user := s.oauth.GetUser(r)
188
193
189
194
timeline, err := db.MakeTimeline(s.db)
190
195
if err != nil {
···
235
240
236
241
return
237
242
case http.MethodPost:
238
-
session, err := s.auth.Store.Get(r, appview.SessionName)
243
+
session, err := s.oauth.Store.Get(r, appview.SessionName)
239
244
if err != nil || session.IsNew {
240
245
log.Println("unauthorized attempt to generate registration key")
241
246
http.Error(w, "Forbidden", http.StatusUnauthorized)
···
297
302
298
303
// create a signed request and check if a node responds to that
299
304
func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) {
300
-
user := s.auth.GetUser(r)
305
+
user := s.oauth.GetUser(r)
301
306
302
307
domain := chi.URLParam(r, "domain")
303
308
if domain == "" {
···
312
317
return
313
318
}
314
319
315
-
client, err := NewSignedClient(domain, secret, s.config.Dev)
320
+
client, err := NewSignedClient(domain, secret, s.config.Core.Dev)
316
321
if err != nil {
317
322
log.Println("failed to create client to ", domain)
318
323
}
···
421
426
return
422
427
}
423
428
424
-
user := s.auth.GetUser(r)
429
+
user := s.oauth.GetUser(r)
425
430
reg, err := db.RegistrationByDomain(s.db, domain)
426
431
if err != nil {
427
432
w.Write([]byte("failed to pull up registration info"))
···
469
474
// get knots registered by this user
470
475
func (s *State) Knots(w http.ResponseWriter, r *http.Request) {
471
476
// for now, this is just pubkeys
472
-
user := s.auth.GetUser(r)
477
+
user := s.oauth.GetUser(r)
473
478
registrations, err := db.RegistrationsByDid(s.db, user.Did)
474
479
if err != nil {
475
480
log.Println(err)
···
522
527
log.Printf("adding %s to %s\n", subjectIdentity.Handle.String(), domain)
523
528
524
529
// announce this relation into the firehose, store into owners' pds
525
-
client, _ := s.auth.AuthorizedClient(r)
526
-
currentUser := s.auth.GetUser(r)
530
+
client, err := s.oauth.AuthorizedClient(r)
531
+
if err != nil {
532
+
http.Error(w, "failed to authorize client", http.StatusInternalServerError)
533
+
return
534
+
}
535
+
currentUser := s.oauth.GetUser(r)
527
536
createdAt := time.Now().Format(time.RFC3339)
528
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
537
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
529
538
Collection: tangled.KnotMemberNSID,
530
539
Repo: currentUser.Did,
531
540
Rkey: appview.TID(),
···
550
559
return
551
560
}
552
561
553
-
ksClient, err := NewSignedClient(domain, secret, s.config.Dev)
562
+
ksClient, err := NewSignedClient(domain, secret, s.config.Core.Dev)
554
563
if err != nil {
555
564
log.Println("failed to create client to ", domain)
556
565
return
···
614
623
func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) {
615
624
switch r.Method {
616
625
case http.MethodGet:
617
-
user := s.auth.GetUser(r)
626
+
user := s.oauth.GetUser(r)
618
627
knots, err := s.enforcer.GetDomainsForUser(user.Did)
619
628
if err != nil {
620
629
s.pages.Notice(w, "repo", "Invalid user account.")
···
627
636
})
628
637
629
638
case http.MethodPost:
630
-
user := s.auth.GetUser(r)
639
+
user := s.oauth.GetUser(r)
631
640
632
641
domain := r.FormValue("domain")
633
642
if domain == "" {
···
671
680
return
672
681
}
673
682
674
-
client, err := NewSignedClient(domain, secret, s.config.Dev)
683
+
client, err := NewSignedClient(domain, secret, s.config.Core.Dev)
675
684
if err != nil {
676
685
s.pages.Notice(w, "repo", "Failed to connect to knot server.")
677
686
return
···
686
695
Description: description,
687
696
}
688
697
689
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
698
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
699
+
if err != nil {
700
+
s.pages.Notice(w, "repo", "Failed to write record to PDS.")
701
+
return
702
+
}
690
703
691
704
createdAt := time.Now().Format(time.RFC3339)
692
-
atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{
705
+
atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
693
706
Collection: tangled.RepoNSID,
694
707
Repo: user.Did,
695
708
Rkey: rkey,
+1
-1
appview/tid.go
+1
-1
appview/tid.go
+2
-2
cmd/appview/main.go
+2
-2
cmd/appview/main.go