forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

appview: swap out old auth service for oauth

Also does some driveby config refactoring.

anirudh.fi 492f7060 ed9740a1

verified
+1 -1
.air/appview.toml
···
[build]
cmd = "tailwindcss -i input.css -o ./appview/pages/static/tw.css && go build -o .bin/app ./cmd/appview/main.go"
-
bin = ".bin/app"
+
bin = ";set -o allexport && source .env && set +o allexport; .bin/app"
root = "."
exclude_regex = [".*_templ.go"]
+37 -10
appview/config.go
···
"github.com/sethvargo/go-envconfig"
)
+
type CoreConfig struct {
+
CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"`
+
DbPath string `env:"DB_PATH, default=appview.db"`
+
ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"`
+
AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"`
+
Dev bool `env:"DEV, default=false"`
+
}
+
+
type OAuthConfig struct {
+
Jwks string `env:"JWKS"`
+
ServerMetadataUrl string `env:"SERVER_METADATA_URL"`
+
}
+
+
type JetstreamConfig struct {
+
Endpoint string `env:"ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"`
+
}
+
+
type ResendConfig struct {
+
ApiKey string `env:"API_KEY"`
+
}
+
+
type CamoConfig struct {
+
Host string `env:"HOST, default=https://camo.tangled.sh"`
+
SharedSecret string `env:"SHARED_SECRET"`
+
}
+
+
type AvatarConfig struct {
+
Host string `env:"HOST, default=https://avatar.tangled.sh"`
+
SharedSecret string `env:"SHARED_SECRET"`
+
}
+
type Config struct {
-
CookieSecret string `env:"TANGLED_COOKIE_SECRET, default=00000000000000000000000000000000"`
-
DbPath string `env:"TANGLED_DB_PATH, default=appview.db"`
-
ListenAddr string `env:"TANGLED_LISTEN_ADDR, default=0.0.0.0:3000"`
-
Dev bool `env:"TANGLED_DEV, default=false"`
-
JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"`
-
ResendApiKey string `env:"TANGLED_RESEND_API_KEY"`
-
CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"`
-
CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"`
-
AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"`
-
AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"`
+
Core CoreConfig `env:",prefix=TANGLED_"`
+
Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"`
+
Resend ResendConfig `env:",prefix=TANGLED_RESEND_"`
+
Camo CamoConfig `env:",prefix=TANGLED_CAMO_"`
+
Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"`
+
OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"`
}
func LoadConfig(ctx context.Context) (*Config, error) {
+3
appview/consts.go
···
SessionRefreshJwt = "refreshJwt"
SessionExpiry = "expiry"
SessionAuthenticated = "authenticated"
+
+
SessionDpopPrivateJwk = "dpopPrivateJwk"
+
SessionDpopAuthServerNonce = "dpopAuthServerNonce"
)
+26
appview/db/db.go
···
foreign key (at_uri) references repos(at_uri) on delete cascade
);
+
create table if not exists oauth_requests (
+
id integer primary key autoincrement,
+
auth_server_iss text not null,
+
state text not null,
+
did text not null,
+
handle text not null,
+
pds_url text not null,
+
pkce_verifier text not null,
+
dpop_auth_server_nonce text not null,
+
dpop_private_jwk text not null
+
);
+
+
create table if not exists oauth_sessions (
+
id integer primary key autoincrement,
+
did text not null,
+
handle text not null,
+
pds_url text not null,
+
auth_server_iss text not null,
+
access_jwt text not null,
+
refresh_jwt text not null,
+
dpop_pds_nonce text,
+
dpop_auth_server_nonce text not null,
+
dpop_private_jwk text not null,
+
expiry text not null
+
);
+
create table if not exists migrations (
id integer primary key autoincrement,
name text unique
+173
appview/db/oauth.go
···
+
package db
+
+
type OAuthRequest struct {
+
ID uint
+
AuthserverIss string
+
Handle string
+
State string
+
Did string
+
PdsUrl string
+
PkceVerifier string
+
DpopAuthserverNonce string
+
DpopPrivateJwk string
+
}
+
+
func SaveOAuthRequest(e Execer, oauthRequest OAuthRequest) error {
+
_, err := e.Exec(`
+
insert into oauth_requests (
+
auth_server_iss,
+
state,
+
handle,
+
did,
+
pds_url,
+
pkce_verifier,
+
dpop_auth_server_nonce,
+
dpop_private_jwk
+
) values (?, ?, ?, ?, ?, ?, ?, ?)`,
+
oauthRequest.AuthserverIss,
+
oauthRequest.State,
+
oauthRequest.Handle,
+
oauthRequest.Did,
+
oauthRequest.PdsUrl,
+
oauthRequest.PkceVerifier,
+
oauthRequest.DpopAuthserverNonce,
+
oauthRequest.DpopPrivateJwk,
+
)
+
return err
+
}
+
+
func GetOAuthRequestByState(e Execer, state string) (OAuthRequest, error) {
+
var req OAuthRequest
+
err := e.QueryRow(`
+
select
+
id,
+
auth_server_iss,
+
handle,
+
state,
+
did,
+
pds_url,
+
pkce_verifier,
+
dpop_auth_server_nonce,
+
dpop_private_jwk
+
from oauth_requests
+
where state = ?`, state).Scan(
+
&req.ID,
+
&req.AuthserverIss,
+
&req.Handle,
+
&req.State,
+
&req.Did,
+
&req.PdsUrl,
+
&req.PkceVerifier,
+
&req.DpopAuthserverNonce,
+
&req.DpopPrivateJwk,
+
)
+
return req, err
+
}
+
+
func DeleteOAuthRequestByState(e Execer, state string) error {
+
_, err := e.Exec(`
+
delete from oauth_requests
+
where state = ?`, state)
+
return err
+
}
+
+
type OAuthSession struct {
+
ID uint
+
Handle string
+
Did string
+
PdsUrl string
+
AccessJwt string
+
RefreshJwt string
+
AuthServerIss string
+
DpopPdsNonce string
+
DpopAuthserverNonce string
+
DpopPrivateJwk string
+
Expiry string
+
}
+
+
func SaveOAuthSession(e Execer, session OAuthSession) error {
+
_, err := e.Exec(`
+
insert into oauth_sessions (
+
did,
+
handle,
+
pds_url,
+
access_jwt,
+
refresh_jwt,
+
auth_server_iss,
+
dpop_auth_server_nonce,
+
dpop_private_jwk,
+
expiry
+
) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+
session.Did,
+
session.Handle,
+
session.PdsUrl,
+
session.AccessJwt,
+
session.RefreshJwt,
+
session.AuthServerIss,
+
session.DpopAuthserverNonce,
+
session.DpopPrivateJwk,
+
session.Expiry,
+
)
+
return err
+
}
+
+
func RefreshOAuthSession(e Execer, did string, accessJwt, refreshJwt, expiry string) error {
+
_, err := e.Exec(`
+
update oauth_sessions
+
set access_jwt = ?, refresh_jwt = ?, expiry = ?
+
where did = ?`,
+
accessJwt,
+
refreshJwt,
+
expiry,
+
did,
+
)
+
return err
+
}
+
+
func GetOAuthSessionByDid(e Execer, did string) (*OAuthSession, error) {
+
var session OAuthSession
+
err := e.QueryRow(`
+
select
+
id,
+
did,
+
handle,
+
pds_url,
+
access_jwt,
+
refresh_jwt,
+
auth_server_iss,
+
dpop_auth_server_nonce,
+
dpop_private_jwk,
+
expiry
+
from oauth_sessions
+
where did = ?`, did).Scan(
+
&session.ID,
+
&session.Did,
+
&session.Handle,
+
&session.PdsUrl,
+
&session.AccessJwt,
+
&session.RefreshJwt,
+
&session.AuthServerIss,
+
&session.DpopAuthserverNonce,
+
&session.DpopPrivateJwk,
+
&session.Expiry,
+
)
+
return &session, err
+
}
+
+
func DeleteOAuthSessionByDid(e Execer, did string) error {
+
_, err := e.Exec(`
+
delete from oauth_sessions
+
where did = ?`, did)
+
return err
+
}
+
+
func UpdateDpopPdsNonce(e Execer, did string, dpopPdsNonce string) error {
+
_, err := e.Exec(`
+
update oauth_sessions
+
set dpop_pds_nonce = ?
+
where did = ?`,
+
dpopPdsNonce,
+
did,
+
)
+
return err
+
}
+5 -58
appview/middleware/middleware.go
···
"log"
"net/http"
"strconv"
-
"time"
-
comatproto "github.com/bluesky-social/indigo/api/atproto"
-
"github.com/bluesky-social/indigo/xrpc"
-
"tangled.sh/tangled.sh/core/appview"
-
"tangled.sh/tangled.sh/core/appview/auth"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pagination"
)
type Middleware func(http.Handler) http.Handler
-
func AuthMiddleware(a *auth.Auth) Middleware {
+
func AuthMiddleware(a *oauth.OAuth) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
redirectFunc := func(w http.ResponseWriter, r *http.Request) {
···
}
}
-
session, err := a.GetSession(r)
-
if session.IsNew || err != nil {
+
_, auth, err := a.GetSession(r)
+
if err != nil {
log.Printf("not logged in, redirecting")
redirectFunc(w, r)
return
}
-
authorized, ok := session.Values[appview.SessionAuthenticated].(bool)
-
if !ok || !authorized {
+
if !auth {
log.Printf("not logged in, redirecting")
redirectFunc(w, r)
return
-
}
-
-
// refresh if nearing expiry
-
// TODO: dedup with /login
-
expiryStr := session.Values[appview.SessionExpiry].(string)
-
expiry, err := time.Parse(time.RFC3339, expiryStr)
-
if err != nil {
-
log.Println("invalid expiry time", err)
-
redirectFunc(w, r)
-
return
-
}
-
pdsUrl, ok1 := session.Values[appview.SessionPds].(string)
-
did, ok2 := session.Values[appview.SessionDid].(string)
-
refreshJwt, ok3 := session.Values[appview.SessionRefreshJwt].(string)
-
-
if !ok1 || !ok2 || !ok3 {
-
log.Println("invalid expiry time", err)
-
redirectFunc(w, r)
-
return
-
}
-
-
if time.Now().After(expiry) {
-
log.Println("token expired, refreshing ...")
-
-
client := xrpc.Client{
-
Host: pdsUrl,
-
Auth: &xrpc.AuthInfo{
-
Did: did,
-
AccessJwt: refreshJwt,
-
RefreshJwt: refreshJwt,
-
},
-
}
-
atSession, err := comatproto.ServerRefreshSession(r.Context(), &client)
-
if err != nil {
-
log.Println("failed to refresh session", err)
-
redirectFunc(w, r)
-
return
-
}
-
-
sessionish := auth.RefreshSessionWrapper{atSession}
-
-
err = a.StoreSession(r, w, &sessionish, pdsUrl)
-
if err != nil {
-
log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err)
-
return
-
}
-
-
log.Println("successfully refreshed token")
}
next.ServeHTTP(w, r)
+2
appview/oauth/oauth.go
···
}
userSession.Values[appview.SessionDid] = oreq.Did
+
userSession.Values[appview.SessionHandle] = oreq.Handle
+
userSession.Values[appview.SessionPds] = oreq.PdsUrl
userSession.Values[appview.SessionAuthenticated] = true
err = userSession.Save(r, w)
if err != nil {
+41 -37
appview/pages/pages.go
···
"strings"
"tangled.sh/tangled.sh/core/appview"
-
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages/markup"
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
"tangled.sh/tangled.sh/core/appview/pagination"
···
func NewPages(config *appview.Config) *Pages {
// initialized with safe defaults, can be overriden per use
rctx := &markup.RenderContext{
-
IsDev: config.Dev,
-
CamoUrl: config.CamoHost,
-
CamoSecret: config.CamoSharedSecret,
+
IsDev: config.Core.Dev,
+
CamoUrl: config.Camo.Host,
+
CamoSecret: config.Camo.SharedSecret,
}
p := &Pages{
t: make(map[string]*template.Template),
-
dev: config.Dev,
+
dev: config.Core.Dev,
embedFS: Files,
rctx: rctx,
templateDir: "appview/pages",
···
return p.executePlain("user/login", w, params)
}
+
func (p *Pages) OAuthLogin(w io.Writer, params LoginParams) error {
+
return p.executePlain("user/oauthlogin", w, params)
+
}
+
type TimelineParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Timeline []db.TimelineEvent
DidHandleMap map[string]string
}
···
}
type SettingsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
PubKeys []db.PublicKey
Emails []db.Email
}
···
}
type KnotsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Registrations []db.Registration
}
···
}
type KnotParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
DidHandleMap map[string]string
Registration *db.Registration
Members []string
···
}
type NewRepoParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Knots []string
}
···
}
type ForkRepoParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Knots []string
RepoInfo repoinfo.RepoInfo
}
···
}
type ProfilePageParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Repos []db.Repo
CollaboratingRepos []db.Repo
ProfileTimeline *db.ProfileTimeline
···
}
type ReposPageParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Repos []db.Repo
Card ProfileCard
···
}
type EditBioParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Profile *db.Profile
}
···
}
type EditPinsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
Profile *db.Profile
AllRepos []PinnedRepo
DidHandleMap map[string]string
···
}
type RepoIndexParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
TagMap map[string][]string
···
}
type RepoLogParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
TagMap map[string][]string
types.RepoLogResponse
···
}
type RepoCommitParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
EmailToDidOrHandle map[string]string
···
}
type RepoTreeParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
BreadCrumbs [][]string
···
}
type RepoBranchesParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
types.RepoBranchesResponse
···
}
type RepoTagsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
types.RepoTagsResponse
···
}
type RepoArtifactParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Artifact db.Artifact
}
···
}
type RepoBlobParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
BreadCrumbs [][]string
···
}
type RepoSettingsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Collaborators []Collaborator
Active string
···
}
type RepoIssuesParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
Issues []db.Issue
···
}
type RepoSingleIssueParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
Issue db.Issue
···
}
type RepoNewIssueParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
}
···
}
type EditIssueCommentParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Issue *db.Issue
Comment *db.Comment
···
}
type SingleIssueCommentParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
DidHandleMap map[string]string
RepoInfo repoinfo.RepoInfo
Issue *db.Issue
···
}
type RepoNewPullParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Branches []types.Branch
Active string
···
}
type RepoPullsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Pulls []*db.Pull
Active string
···
}
type RepoSinglePullParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
DidHandleMap map[string]string
···
}
type RepoPullPatchParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
DidHandleMap map[string]string
RepoInfo repoinfo.RepoInfo
Pull *db.Pull
···
}
type RepoPullInterdiffParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
DidHandleMap map[string]string
RepoInfo repoinfo.RepoInfo
Pull *db.Pull
···
}
type PullResubmitParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Pull *db.Pull
SubmissionId int
···
}
type PullActionsParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Pull *db.Pull
RoundNumber int
···
}
type PullNewCommentParams struct {
-
LoggedInUser *auth.User
+
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Pull *db.Pull
RoundNumber int
+71
appview/pages/templates/user/oauthlogin.html
···
+
{{ define "user/oauthlogin" }}
+
<!doctype html>
+
<html lang="en" class="dark:bg-gray-900">
+
<head>
+
<meta charset="UTF-8" />
+
<meta
+
name="viewport"
+
content="width=device-width, initial-scale=1.0"
+
/>
+
<script src="/static/htmx.min.js"></script>
+
<link
+
rel="stylesheet"
+
href="/static/tw.css?{{ cssContentHash }}"
+
type="text/css"
+
/>
+
<title>login</title>
+
</head>
+
<body class="flex items-center justify-center min-h-screen">
+
<main class="max-w-7xl px-6 -mt-4">
+
<h1
+
class="text-center text-2xl font-semibold italic dark:text-white"
+
>
+
tangled
+
</h1>
+
<h2 class="text-center text-xl italic dark:text-white">
+
tightly-knit social coding.
+
</h2>
+
<form
+
class="w-full mt-4"
+
hx-post="/oauth/login"
+
hx-swap="none"
+
hx-disabled-elt="this"
+
>
+
<div class="flex flex-col">
+
<label for="handle">handle</label>
+
<input
+
type="text"
+
id="handle"
+
name="handle"
+
tabindex="1"
+
required
+
/>
+
<span class="text-xs text-gray-500 mt-1">
+
Use your
+
<a href="https://bsky.app">Bluesky</a> handle to log
+
in. You will then be redirected to your PDS to
+
complete authentication.
+
</span>
+
</div>
+
+
<button
+
class="btn w-full my-2 mt-6"
+
type="submit"
+
id="login-button"
+
tabindex="3"
+
>
+
<span>login</span>
+
</button>
+
</form>
+
<p class="text-sm text-gray-500">
+
Join our <a href="https://chat.tangled.sh">Discord</a> or
+
IRC channel:
+
<a href="https://web.libera.chat/#tangled"
+
><code>#tangled</code> on Libera Chat</a
+
>.
+
</p>
+
<p id="login-msg" class="error w-full"></p>
+
</main>
+
</body>
+
</html>
+
{{ end }}
+27 -18
appview/settings/settings.go
···
"github.com/go-chi/chi/v5"
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
-
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/email"
"tangled.sh/tangled.sh/core/appview/middleware"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
type Settings struct {
Db *db.DB
-
Auth *auth.Auth
+
OAuth *oauth.OAuth
Pages *pages.Pages
Config *appview.Config
}
···
func (s *Settings) Router() http.Handler {
r := chi.NewRouter()
-
r.Use(middleware.AuthMiddleware(s.Auth))
+
r.Use(middleware.AuthMiddleware(s.OAuth))
r.Get("/", s.settings)
···
}
func (s *Settings) settings(w http.ResponseWriter, r *http.Request) {
-
user := s.Auth.GetUser(r)
+
user := s.OAuth.GetUser(r)
pubKeys, err := db.GetPublicKeys(s.Db, user.Did)
if err != nil {
log.Println(err)
···
verifyURL := s.verifyUrl(did, emailAddr, code)
return email.Email{
-
APIKey: s.Config.ResendApiKey,
+
APIKey: s.Config.Resend.ApiKey,
From: "noreply@notifs.tangled.sh",
To: emailAddr,
Subject: "Verify your Tangled email",
···
log.Println("unimplemented")
return
case http.MethodPut:
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
emAddr := r.FormValue("email")
emAddr = strings.TrimSpace(emAddr)
···
s.Pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.")
return
case http.MethodDelete:
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
emailAddr := r.FormValue("email")
emailAddr = strings.TrimSpace(emailAddr)
···
func (s *Settings) verifyUrl(did string, email string, code string) string {
var appUrl string
-
if s.Config.Dev {
-
appUrl = "http://" + s.Config.ListenAddr
+
if s.Config.Core.Dev {
+
appUrl = "http://" + s.Config.Core.ListenAddr
} else {
appUrl = "https://tangled.sh"
}
···
return
}
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
emAddr := r.FormValue("email")
emAddr = strings.TrimSpace(emAddr)
···
}
func (s *Settings) emailsPrimary(w http.ResponseWriter, r *http.Request) {
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
emailAddr := r.FormValue("email")
emailAddr = strings.TrimSpace(emailAddr)
···
log.Println("unimplemented")
return
case http.MethodPut:
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
key := r.FormValue("key")
key = strings.TrimSpace(key)
name := r.FormValue("name")
-
client, _ := s.Auth.AuthorizedClient(r)
+
client, err := s.OAuth.AuthorizedClient(r)
+
if err != nil {
+
s.Pages.Notice(w, "settings-keys", "Failed to authorize. Try again later.")
+
return
+
}
-
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
+
_, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key))
if err != nil {
log.Printf("parsing public key: %s", err)
s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a <strong>public</strong> key.")
···
}
// store in pds too
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.PublicKeyNSID,
Repo: did,
Rkey: rkey,
···
return
case http.MethodDelete:
-
did := s.Auth.GetDid(r)
+
did := s.OAuth.GetDid(r)
q := r.URL.Query()
name := q.Get("name")
···
log.Println(rkey)
log.Println(key)
-
client, _ := s.Auth.AuthorizedClient(r)
+
client, err := s.OAuth.AuthorizedClient(r)
+
if err != nil {
+
log.Printf("failed to authorize client: %s", err)
+
s.Pages.Notice(w, "settings-keys", "Failed to authorize client.")
+
return
+
}
if err := db.DeletePublicKey(s.Db, did, name, key); err != nil {
log.Printf("removing public key: %s", err)
···
if rkey != "" {
// remove from pds too
-
_, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
+
_, err := client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.PublicKeyNSID,
Repo: did,
Rkey: rkey,
+19 -10
appview/state/artifact.go
···
// TODO: proper statuses here on early exit
func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
tagParam := chi.URLParam(r, "tag")
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
}
defer file.Close()
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "upload", "failed to get authorized client")
+
return
+
}
-
uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file)
+
uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file)
if err != nil {
log.Println("failed to upload blob", err)
s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.")
···
rkey := appview.TID()
createdAt := time.Now()
-
putRecordResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoArtifactNSID,
Repo: user.Did,
Rkey: rkey,
···
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
return
+
}
artifacts, err := db.GetArtifact(
s.db,
···
artifact := artifacts[0]
-
getBlobResp, err := comatproto.SyncGetBlob(r.Context(), client, artifact.BlobCid.String(), artifact.Did)
+
getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did)
if err != nil {
log.Println("failed to get blob from pds", err)
return
···
// TODO: proper statuses here on early exit
func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
tagParam := chi.URLParam(r, "tag")
filename := chi.URLParam(r, "file")
f, err := s.fullyResolvedRepo(r)
···
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, _ := s.oauth.AuthorizedClient(r)
tag := plumbing.NewHash(tagParam)
···
return
}
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.RepoArtifactNSID,
Repo: user.Did,
Rkey: artifact.Rkey,
···
return nil, err
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
return nil, err
}
+8 -4
appview/state/follow.go
···
)
func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
-
currentUser := s.auth.GetUser(r)
+
currentUser := s.oauth.GetUser(r)
subject := r.URL.Query().Get("subject")
if subject == "" {
···
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to authorize client")
+
return
+
}
switch r.Method {
case http.MethodPost:
createdAt := time.Now().Format(time.RFC3339)
rkey := appview.TID()
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.GraphFollowNSID,
Repo: currentUser.Did,
Rkey: rkey,
···
return
}
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.GraphFollowNSID,
Repo: currentUser.Did,
Rkey: follow.Rkey,
+2 -2
appview/state/git_http.go
···
repo := chi.URLParam(r, "repo")
scheme := "https"
-
if s.config.Dev {
+
if s.config.Core.Dev {
scheme = "http"
}
targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, knot, user.DID, repo, r.URL.RawQuery)
···
repo := chi.URLParam(r, "repo")
scheme := "https"
-
if s.config.Dev {
+
if s.config.Core.Dev {
scheme = "http"
}
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
···
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// requires auth also
-
actor := s.auth.GetUser(r)
+
actor := s.oauth.GetUser(r)
if actor == nil {
// we need a logged in user
log.Printf("not logged in, redirecting")
···
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// requires auth also
-
actor := s.auth.GetUser(r)
+
actor := s.oauth.GetUser(r)
if actor == nil {
// we need a logged in user
log.Printf("not logged in, redirecting")
+17 -12
appview/state/profile.go
···
log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err)
}
-
loggedInUser := s.auth.GetUser(r)
+
loggedInUser := s.oauth.GetUser(r)
followStatus := db.IsNotFollowing
if loggedInUser != nil {
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
···
log.Printf("getting repos for %s: %s", ident.DID.String(), err)
}
-
loggedInUser := s.auth.GetUser(r)
+
loggedInUser := s.oauth.GetUser(r)
followStatus := db.IsNotFollowing
if loggedInUser != nil {
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
···
}
func (s *State) GetAvatarUri(handle string) string {
-
secret := s.config.AvatarSharedSecret
+
secret := s.config.Avatar.SharedSecret
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(handle))
signature := hex.EncodeToString(h.Sum(nil))
-
return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle)
+
return fmt.Sprintf("%s/%s/%s", s.config.Avatar.Host, signature, handle)
}
func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
err := r.ParseForm()
if err != nil {
···
}
func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
err := r.ParseForm()
if err != nil {
···
}
func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
tx, err := s.db.BeginTx(r.Context(), nil)
if err != nil {
log.Println("failed to start transaction", err)
···
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.")
+
return
+
}
// yeah... lexgen dose not support syntax.ATURI in the record for some reason,
// nor does it support exact size arrays
···
vanityStats = append(vanityStats, string(v.Kind))
}
-
ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self")
+
ex, _ := client.RepoGetRecord(r.Context(), "", tangled.ActorProfileNSID, user.Did, "self")
var cid *string
if ex != nil {
cid = ex.Cid
}
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.ActorProfileNSID,
Repo: user.Did,
Rkey: "self",
···
}
func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
profile, err := db.GetProfile(s.db, user.Did)
if err != nil {
···
}
func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
profile, err := db.GetProfile(s.db, user.Did)
if err != nil {
+76 -51
appview/state/pull.go
···
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
-
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/patchutil"
"tangled.sh/tangled.sh/core/types"
···
func (s *State) PullActions(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
}
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
}
}
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err)
return types.MergeCheckResponse{
···
repoName = f.RepoName
}
-
us, err := NewUnsignedClient(knot, s.config.Dev)
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to setup client for %s; ignoring: %v", knot, err)
return pages.Unknown
···
}
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
}
func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
interdiff := patchutil.Interdiff(previousPatch, currentPatch)
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
-
LoggedInUser: s.auth.GetUser(r),
+
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(s, user),
Pull: pull,
Round: roundIdInt,
···
}
func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
params := r.URL.Query()
state := db.PullOpen
···
}
s.pages.RepoPulls(w, pages.RepoPullsParams{
-
LoggedInUser: s.auth.GetUser(r),
+
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(s, user),
Pulls: pulls,
DidHandleMap: didHandleMap,
···
}
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
}
atUri := f.RepoAt.String()
-
client, _ := s.auth.AuthorizedClient(r)
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
+
return
+
}
+
atResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullCommentNSID,
Repo: user.Did,
Rkey: appview.TID(),
···
}
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
switch r.Method {
case http.MethodGet:
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for %s", f.Knot)
s.pages.Error503(w)
···
return
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client to %s: %v", f.Knot, err)
s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.")
···
}
}
-
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {
+
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) {
pullSource := &db.PullSource{
Branch: sourceBranch,
}
···
}
// Generate a patch using /compare
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
}
-
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {
+
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) {
if !patchutil.IsPatchValid(patch) {
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
return
···
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
}
-
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
+
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
if errors.Is(err, sql.ErrNoRows) {
s.pages.Notice(w, "pull", "No such fork.")
···
return
}
-
sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev)
+
sc, err := NewSignedClient(fork.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create signed client:", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
return
}
-
us, err := NewUnsignedClient(fork.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(fork.Knot, s.config.Core.Dev)
if err != nil {
log.Println("failed to create unsigned client:", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
w http.ResponseWriter,
r *http.Request,
f *FullyResolvedRepo,
-
user *auth.User,
+
user *oauth.User,
title, body, targetBranch string,
patch string,
sourceRev string,
···
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
return
+
}
pullId, err := db.NextPullId(s.db, f.RepoAt)
if err != nil {
log.Println("failed to get pull id", err)
···
return
}
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
Repo: user.Did,
Rkey: rkey,
···
}
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
}
func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for %s", f.Knot)
s.pages.Error503(w)
···
}
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
return
-
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev)
+
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for %s", repo.Knot)
s.pages.Error503(w)
···
return
-
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for target knot %s", f.Knot)
s.pages.Error503(w)
···
func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
···
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.")
return
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
+
return
+
}
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
if err != nil {
// failed to get record
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
return
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
Repo: user.Did,
Rkey: pull.Rkey,
···
func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
···
return
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create client for %s: %s", f.Knot, err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
return
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to authorize client")
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
+
return
+
}
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
if err != nil {
// failed to get record
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
recordPullSource := &tangled.RepoPull_Source{
Branch: pull.PullSource.Branch,
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
Repo: user.Did,
Rkey: pull.Rkey,
···
func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
···
// extract patch by performing compare
-
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev)
+
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create client for %s: %s", forkRepo.Knot, err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
// update the hidden tracking branch to latest
-
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev)
+
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
return
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get client")
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
+
return
+
}
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey)
if err != nil {
// failed to get record
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
Branch: pull.PullSource.Branch,
Repo: &repoAt,
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
Repo: user.Did,
Rkey: pull.Rkey,
···
log.Printf("failed to get primary email: %s", err)
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
+98 -60
appview/state/repo.go
···
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
-
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/appview/pages/markup"
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
···
return
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for %s", f.Knot)
s.pages.Error503(w)
···
emails := uniqueEmails(commitsTrunc)
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
···
ref := chi.URLParam(r, "ref")
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Println("failed to create unsigned client", err)
return
···
tagMap[hash] = append(tagMap[hash], tag.Name)
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoLog(w, pages.RepoLogParams{
LoggedInUser: user,
TagMap: tagMap,
···
return
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
RepoInfo: f.RepoInfo(s, user),
})
···
return
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
switch r.Method {
case http.MethodGet:
···
})
return
case http.MethodPut:
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
newDescription := r.FormValue("description")
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get client")
+
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
+
return
+
}
// optimistic update
err = db.UpdateDescription(s.db, string(repoAt), newDescription)
···
// this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field
//
// SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey)
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey)
if err != nil {
// failed to get record
s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
return
}
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoNSID,
Repo: user.Did,
Rkey: rkey,
···
}
ref := chi.URLParam(r, "ref")
protocol := "http"
-
if !s.config.Dev {
+
if !s.config.Core.Dev {
protocol = "https"
}
···
return
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoCommit(w, pages.RepoCommitParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
···
ref := chi.URLParam(r, "ref")
treePath := chi.URLParam(r, "*")
protocol := "http"
-
if !s.config.Dev {
+
if !s.config.Core.Dev {
protocol = "https"
}
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath))
···
return
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
var breadcrumbs [][]string
breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
return
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Println("failed to create unsigned client", err)
return
···
}
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoTags(w, pages.RepoTagsParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
···
return
}
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Println("failed to create unsigned client", err)
return
···
return strings.Compare(a.Name, b.Name) * -1
})
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoBranches(w, pages.RepoBranchesParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
···
ref := chi.URLParam(r, "ref")
filePath := chi.URLParam(r, "*")
protocol := "http"
-
if !s.config.Dev {
+
if !s.config.Core.Dev {
protocol = "https"
}
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
showRendered = r.URL.Query().Get("code") != "true"
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
s.pages.RepoBlob(w, pages.RepoBlobParams{
LoggedInUser: user,
RepoInfo: f.RepoInfo(s, user),
···
filePath := chi.URLParam(r, "*")
protocol := "http"
-
if !s.config.Dev {
+
if !s.config.Core.Dev {
protocol = "https"
}
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
return
}
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create client to ", f.Knot)
return
···
}
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
}
// remove record from pds
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
return
+
}
repoRkey := f.RepoAt.RecordKey().String()
-
_, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{
+
_, err = xrpcClient.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.RepoNSID,
Repo: user.Did,
Rkey: repoRkey,
···
return
}
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create client to ", f.Knot)
return
···
return
}
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create client to ", f.Knot)
return
···
switch r.Method {
case http.MethodGet:
// for now, this is just pubkeys
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
repoCollaborators, err := f.Collaborators(r.Context(), s)
if err != nil {
log.Println("failed to get collaborators", err)
···
var branchNames []string
var defaultBranch string
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev)
if err != nil {
log.Println("failed to create unsigned client", err)
} else {
···
return collaborators, nil
-
func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo {
+
func (f *FullyResolvedRepo) RepoInfo(s *State, u *oauth.User) repoinfo.RepoInfo {
isStarred := false
if u != nil {
isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt))
···
knot := f.Knot
var disableFork bool
-
us, err := NewUnsignedClient(knot, s.config.Dev)
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
if err != nil {
log.Printf("failed to create unsigned client for %s: %v", knot, err)
} else {
···
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
closed := tangled.RepoIssueStateClosed
-
client, _ := s.auth.AuthorizedClient(r)
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
return
+
}
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoIssueStateNSID,
Repo: user.Did,
Rkey: appview.TID(),
···
return
-
err := db.CloseIssue(s.db, f.RepoAt, issueIdInt)
+
err = db.CloseIssue(s.db, f.RepoAt, issueIdInt)
if err != nil {
log.Println("failed to close issue", err)
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
atUri := f.RepoAt.String()
-
client, _ := s.auth.AuthorizedClient(r)
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
+
return
+
}
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoIssueCommentNSID,
Repo: user.Did,
Rkey: rkey,
···
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
case http.MethodPost:
// extract form value
newBody := r.FormValue("body")
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
+
return
+
}
rkey := comment.Rkey
// optimistic update
···
// rkey is optional, it was introduced later
if comment.Rkey != "" {
// update the record on pds
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey)
+
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoIssueCommentNSID, user.Did, rkey)
if err != nil {
// failed to get record
log.Println(err, rkey)
···
createdAt := record["createdAt"].(string)
commentIdInt64 := int64(commentIdInt)
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoIssueCommentNSID,
Repo: user.Did,
Rkey: rkey,
···
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
// delete from pds
if comment.Rkey != "" {
-
client, _ := s.auth.AuthorizedClient(r)
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "issue-comment", "Failed to delete comment.")
+
return
+
}
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.GraphFollowNSID,
Repo: user.Did,
Rkey: comment.Rkey,
···
page = pagination.FirstPage()
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
···
s.pages.RepoIssues(w, pages.RepoIssuesParams{
-
LoggedInUser: s.auth.GetUser(r),
+
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(s, user),
Issues: issues,
DidHandleMap: didHandleMap,
···
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
···
return
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "issues", "Failed to create issue.")
+
return
+
}
atUri := f.RepoAt.String()
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoIssueNSID,
Repo: user.Did,
Rkey: appview.TID(),
···
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Printf("failed to resolve source repo: %v", err)
···
switch r.Method {
case http.MethodGet:
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
knots, err := s.enforcer.GetDomainsForUser(user.Did)
if err != nil {
s.pages.Notice(w, "repo", "Invalid user account.")
···
return
-
client, err := NewSignedClient(knot, secret, s.config.Dev)
+
client, err := NewSignedClient(knot, secret, s.config.Core.Dev)
if err != nil {
s.pages.Notice(w, "repo", "Failed to reach knot server.")
return
var uri string
-
if s.config.Dev {
+
if s.config.Core.Dev {
uri = "http"
} else {
uri = "https"
···
// continue
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to get authorized client", err)
+
s.pages.Notice(w, "repo", "Failed to create repository.")
+
return
+
}
createdAt := time.Now().Format(time.RFC3339)
-
atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{
+
atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoNSID,
Repo: user.Did,
Rkey: rkey,
+3 -3
appview/state/repo_util.go
···
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/go-chi/chi/v5"
"github.com/go-git/go-git/v5/plumbing/object"
-
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages/repoinfo"
)
···
ref := chi.URLParam(r, "ref")
if ref == "" {
-
us, err := NewUnsignedClient(knot, s.config.Dev)
+
us, err := NewUnsignedClient(knot, s.config.Core.Dev)
if err != nil {
return nil, err
}
···
}, nil
}
-
func RolesInRepo(s *State, u *auth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo {
+
func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo {
if u != nil {
r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo())
return repoinfo.RolesInRepo{r}
+34 -19
appview/state/router.go
···
"strings"
"github.com/go-chi/chi/v5"
+
"github.com/gorilla/sessions"
"tangled.sh/tangled.sh/core/appview/middleware"
+
oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler"
"tangled.sh/tangled.sh/core/appview/settings"
"tangled.sh/tangled.sh/core/appview/state/userutil"
)
···
r.Route("/tags", func(r chi.Router) {
r.Get("/", s.RepoTags)
r.Route("/{tag}", func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
// require auth to download for now
r.Get("/download/{file}", s.DownloadArtifact)
···
r.Get("/{issue}", s.RepoSingleIssue)
r.Group(func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Get("/new", s.NewIssue)
r.Post("/new", s.NewIssue)
r.Post("/{issue}/comment", s.NewIssueComment)
···
})
r.Route("/fork", func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Get("/", s.ForkRepo)
r.Post("/", s.ForkRepo)
})
r.Route("/pulls", func(r chi.Router) {
r.Get("/", s.RepoPulls)
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) {
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) {
r.Get("/", s.NewPull)
r.Get("/patch-upload", s.PatchUploadFragment)
r.Post("/validate-patch", s.ValidatePatch)
···
r.Get("/", s.RepoPullPatch)
r.Get("/interdiff", s.RepoPullInterdiff)
r.Get("/actions", s.PullActions)
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) {
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) {
r.Get("/", s.PullComment)
r.Post("/", s.PullComment)
})
···
})
r.Group(func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Route("/resubmit", func(r chi.Router) {
r.Get("/", s.ResubmitPull)
r.Post("/", s.ResubmitPull)
···
// settings routes, needs auth
r.Group(func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
// repo description can only be edited by owner
r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) {
r.Put("/", s.RepoDescription)
···
r.Get("/", s.Timeline)
-
r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout)
+
r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout)
-
r.Route("/login", func(r chi.Router) {
-
r.Get("/", s.Login)
-
r.Post("/", s.Login)
-
})
+
// r.Route("/login", func(r chi.Router) {
+
// r.Get("/", s.Login)
+
// r.Post("/", s.Login)
+
// })
r.Route("/knots", func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Get("/", s.Knots)
r.Post("/key", s.RegistrationKey)
···
r.Route("/repo", func(r chi.Router) {
r.Route("/new", func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Get("/", s.NewRepo)
r.Post("/", s.NewRepo)
})
// r.Post("/import", s.ImportRepo)
})
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) {
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) {
r.Post("/", s.Follow)
r.Delete("/", s.Follow)
})
-
r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) {
+
r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) {
r.Post("/", s.Star)
r.Delete("/", s.Star)
})
r.Route("/profile", func(r chi.Router) {
-
r.Use(middleware.AuthMiddleware(s.auth))
+
r.Use(middleware.AuthMiddleware(s.oauth))
r.Get("/edit-bio", s.EditBioFragment)
r.Get("/edit-pins", s.EditPinsFragment)
r.Post("/bio", s.UpdateProfileBio)
···
})
r.Mount("/settings", s.SettingsRouter())
-
+
r.Mount("/oauth", s.OAuthRouter())
r.Get("/keys/{user}", s.Keys)
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
···
return r
}
+
func (s *State) OAuthRouter() http.Handler {
+
oauth := &oauthhandler.OAuthHandler{
+
Config: s.config,
+
Pages: s.pages,
+
Resolver: s.resolver,
+
Db: s.db,
+
Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)),
+
OAuth: s.oauth,
+
}
+
+
return oauth.Router()
+
}
+
func (s *State) SettingsRouter() http.Handler {
settings := &settings.Settings{
Db: s.db,
-
Auth: s.auth,
+
OAuth: s.oauth,
Pages: s.pages,
Config: s.config,
}
+8 -4
appview/state/star.go
···
)
func (s *State) Star(w http.ResponseWriter, r *http.Request) {
-
currentUser := s.auth.GetUser(r)
+
currentUser := s.oauth.GetUser(r)
subject := r.URL.Query().Get("subject")
if subject == "" {
···
return
}
-
client, _ := s.auth.AuthorizedClient(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
log.Println("failed to authorize client", err)
+
return
+
}
switch r.Method {
case http.MethodPost:
createdAt := time.Now().Format(time.RFC3339)
rkey := appview.TID()
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.FeedStarNSID,
Repo: currentUser.Did,
Rkey: rkey,
···
return
}
-
_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
+
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
Collection: tangled.FeedStarNSID,
Repo: currentUser.Did,
Rkey: star.Rkey,
+98 -85
appview/state/state.go
···
"tangled.sh/tangled.sh/core/appview"
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/jetstream"
"tangled.sh/tangled.sh/core/rbac"
···
type State struct {
db *db.DB
auth *auth.Auth
+
oauth *oauth.OAuth
enforcer *rbac.Enforcer
-
tidClock *syntax.TIDClock
+
tidClock syntax.TIDClock
pages *pages.Pages
resolver *appview.Resolver
jc *jetstream.JetstreamClient
···
}
func Make(config *appview.Config) (*State, error) {
-
d, err := db.Make(config.DbPath)
+
d, err := db.Make(config.Core.DbPath)
if err != nil {
return nil, err
}
-
auth, err := auth.Make(config.CookieSecret)
+
auth, err := auth.Make(config.Core.CookieSecret)
if err != nil {
return nil, err
}
-
enforcer, err := rbac.NewEnforcer(config.DbPath)
+
enforcer, err := rbac.NewEnforcer(config.Core.DbPath)
if err != nil {
return nil, err
}
···
pgs := pages.NewPages(config)
resolver := appview.NewResolver()
+
+
oauth := oauth.NewOAuth(d, config)
wrapper := db.DbWrapper{d}
jc, err := jetstream.NewJetstreamClient(
-
config.JetstreamEndpoint,
+
config.Jetstream.Endpoint,
"appview",
[]string{
tangled.GraphFollowNSID,
···
state := &State{
d,
auth,
+
oauth,
enforcer,
clock,
pgs,
···
return c.Next().String()
}
-
func (s *State) Login(w http.ResponseWriter, r *http.Request) {
-
ctx := r.Context()
+
// func (s *State) Login(w http.ResponseWriter, r *http.Request) {
+
// ctx := r.Context()
-
switch r.Method {
-
case http.MethodGet:
-
err := s.pages.Login(w, pages.LoginParams{})
-
if err != nil {
-
log.Printf("rendering login page: %s", err)
-
}
+
// switch r.Method {
+
// case http.MethodGet:
+
// err := s.pages.Login(w, pages.LoginParams{})
+
// if err != nil {
+
// log.Printf("rendering login page: %s", err)
+
// }
-
return
-
case http.MethodPost:
-
handle := strings.TrimPrefix(r.FormValue("handle"), "@")
-
appPassword := r.FormValue("app_password")
+
// return
+
// case http.MethodPost:
+
// handle := strings.TrimPrefix(r.FormValue("handle"), "@")
+
// appPassword := r.FormValue("app_password")
-
resolved, err := s.resolver.ResolveIdent(ctx, handle)
-
if err != nil {
-
log.Println("failed to resolve handle:", err)
-
s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
-
return
-
}
+
// resolved, err := s.resolver.ResolveIdent(ctx, handle)
+
// if err != nil {
+
// log.Println("failed to resolve handle:", err)
+
// s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
+
// return
+
// }
-
atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword)
-
if err != nil {
-
s.pages.Notice(w, "login-msg", "Invalid handle or password.")
-
return
-
}
-
sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession}
+
// atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword)
+
// if err != nil {
+
// s.pages.Notice(w, "login-msg", "Invalid handle or password.")
+
// return
+
// }
+
// sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession}
-
err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint())
-
if err != nil {
-
s.pages.Notice(w, "login-msg", "Failed to login, try again later.")
-
return
-
}
+
// err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint())
+
// if err != nil {
+
// s.pages.Notice(w, "login-msg", "Failed to login, try again later.")
+
// return
+
// }
-
log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did)
+
// log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did)
-
did := resolved.DID.String()
-
defaultKnot := "knot1.tangled.sh"
+
// did := resolved.DID.String()
+
// defaultKnot := "knot1.tangled.sh"
-
go func() {
-
log.Printf("adding %s to default knot", did)
-
err = s.enforcer.AddMember(defaultKnot, did)
-
if err != nil {
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
-
return
-
}
-
err = s.enforcer.E.SavePolicy()
-
if err != nil {
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
-
return
-
}
+
// go func() {
+
// log.Printf("adding %s to default knot", did)
+
// err = s.enforcer.AddMember(defaultKnot, did)
+
// if err != nil {
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
+
// return
+
// }
+
// err = s.enforcer.E.SavePolicy()
+
// if err != nil {
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
+
// return
+
// }
-
secret, err := db.GetRegistrationKey(s.db, defaultKnot)
-
if err != nil {
-
log.Println("failed to get registration key for knot1.tangled.sh")
-
return
-
}
-
signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Dev)
-
resp, err := signedClient.AddMember(did)
-
if err != nil {
-
log.Println("failed to add user to knot1.tangled.sh: ", err)
-
return
-
}
+
// secret, err := db.GetRegistrationKey(s.db, defaultKnot)
+
// if err != nil {
+
// log.Println("failed to get registration key for knot1.tangled.sh")
+
// return
+
// }
+
// signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev)
+
// resp, err := signedClient.AddMember(did)
+
// if err != nil {
+
// log.Println("failed to add user to knot1.tangled.sh: ", err)
+
// return
+
// }
-
if resp.StatusCode != http.StatusNoContent {
-
log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode)
-
return
-
}
-
}()
+
// if resp.StatusCode != http.StatusNoContent {
+
// log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode)
+
// return
+
// }
+
// }()
-
s.pages.HxRedirect(w, "/")
-
return
-
}
-
}
+
// s.pages.HxRedirect(w, "/")
+
// return
+
// }
+
// }
func (s *State) Logout(w http.ResponseWriter, r *http.Request) {
-
s.auth.ClearSession(r, w)
+
s.oauth.ClearSession(r, w)
w.Header().Set("HX-Redirect", "/login")
w.WriteHeader(http.StatusSeeOther)
}
func (s *State) Timeline(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
timeline, err := db.MakeTimeline(s.db)
if err != nil {
···
return
case http.MethodPost:
-
session, err := s.auth.Store.Get(r, appview.SessionName)
+
session, err := s.oauth.Store.Get(r, appview.SessionName)
if err != nil || session.IsNew {
log.Println("unauthorized attempt to generate registration key")
http.Error(w, "Forbidden", http.StatusUnauthorized)
···
// create a signed request and check if a node responds to that
func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) {
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
domain := chi.URLParam(r, "domain")
if domain == "" {
···
return
}
-
client, err := NewSignedClient(domain, secret, s.config.Dev)
+
client, err := NewSignedClient(domain, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create client to ", domain)
}
···
return
}
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
reg, err := db.RegistrationByDomain(s.db, domain)
if err != nil {
w.Write([]byte("failed to pull up registration info"))
···
// get knots registered by this user
func (s *State) Knots(w http.ResponseWriter, r *http.Request) {
// for now, this is just pubkeys
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
registrations, err := db.RegistrationsByDid(s.db, user.Did)
if err != nil {
log.Println(err)
···
log.Printf("adding %s to %s\n", subjectIdentity.Handle.String(), domain)
// announce this relation into the firehose, store into owners' pds
-
client, _ := s.auth.AuthorizedClient(r)
-
currentUser := s.auth.GetUser(r)
+
client, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
http.Error(w, "failed to authorize client", http.StatusInternalServerError)
+
return
+
}
+
currentUser := s.oauth.GetUser(r)
createdAt := time.Now().Format(time.RFC3339)
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.KnotMemberNSID,
Repo: currentUser.Did,
Rkey: appview.TID(),
···
return
}
-
ksClient, err := NewSignedClient(domain, secret, s.config.Dev)
+
ksClient, err := NewSignedClient(domain, secret, s.config.Core.Dev)
if err != nil {
log.Println("failed to create client to ", domain)
return
···
func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
knots, err := s.enforcer.GetDomainsForUser(user.Did)
if err != nil {
s.pages.Notice(w, "repo", "Invalid user account.")
···
})
case http.MethodPost:
-
user := s.auth.GetUser(r)
+
user := s.oauth.GetUser(r)
domain := r.FormValue("domain")
if domain == "" {
···
return
}
-
client, err := NewSignedClient(domain, secret, s.config.Dev)
+
client, err := NewSignedClient(domain, secret, s.config.Core.Dev)
if err != nil {
s.pages.Notice(w, "repo", "Failed to connect to knot server.")
return
···
Description: description,
}
-
xrpcClient, _ := s.auth.AuthorizedClient(r)
+
xrpcClient, err := s.oauth.AuthorizedClient(r)
+
if err != nil {
+
s.pages.Notice(w, "repo", "Failed to write record to PDS.")
+
return
+
}
createdAt := time.Now().Format(time.RFC3339)
-
atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{
+
atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoNSID,
Repo: user.Did,
Rkey: rkey,
+1 -1
appview/tid.go
···
"github.com/bluesky-social/indigo/atproto/syntax"
)
-
var c *syntax.TIDClock = syntax.NewTIDClock(0)
+
var c syntax.TIDClock = syntax.NewTIDClock(0)
func TID() string {
return c.Next().String()
+2 -2
cmd/appview/main.go
···
log.Fatal(err)
}
-
log.Println("starting server on", c.ListenAddr)
-
log.Println(http.ListenAndServe(c.ListenAddr, state.Router()))
+
log.Println("starting server on", c.Core.ListenAddr)
+
log.Println(http.ListenAndServe(c.Core.ListenAddr, state.Router()))
}