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

implement transactions

+14 -2
appview/db/db.go
···
package db
import (
+
"context"
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
type DB struct {
-
db *sql.DB
+
*sql.DB
+
}
+
+
type Execer interface {
+
Query(query string, args ...any) (*sql.Rows, error)
+
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
+
QueryRow(query string, args ...any) *sql.Row
+
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
+
Exec(query string, args ...any) (sql.Result, error)
+
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
+
Prepare(query string) (*sql.Stmt, error)
+
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}
func Make(dbPath string) (*DB, error) {
···
if err != nil {
return nil, err
}
-
return &DB{db: db}, nil
+
return &DB{db}, nil
}
+12 -12
appview/db/follow.go
···
RKey string
}
-
func (d *DB) AddFollow(userDid, subjectDid, rkey string) error {
+
func AddFollow(e Execer, userDid, subjectDid, rkey string) error {
query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)`
-
_, err := d.db.Exec(query, userDid, subjectDid, rkey)
+
_, err := e.Exec(query, userDid, subjectDid, rkey)
return err
}
// Get a follow record
-
func (d *DB) GetFollow(userDid, subjectDid string) (*Follow, error) {
+
func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) {
query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?`
-
row := d.db.QueryRow(query, userDid, subjectDid)
+
row := e.QueryRow(query, userDid, subjectDid)
var follow Follow
var followedAt string
···
}
// Get a follow record
-
func (d *DB) DeleteFollow(userDid, subjectDid string) error {
-
_, err := d.db.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid)
+
func DeleteFollow(e Execer, userDid, subjectDid string) error {
+
_, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid)
return err
}
-
func (d *DB) GetFollowerFollowing(did string) (int, int, error) {
+
func GetFollowerFollowing(e Execer, did string) (int, int, error) {
followers, following := 0, 0
-
err := d.db.QueryRow(
+
err := e.QueryRow(
`SELECT
COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers,
COUNT(CASE WHEN user_did = ? THEN 1 END) AS following
···
}
}
-
func (d *DB) GetFollowStatus(userDid, subjectDid string) FollowStatus {
+
func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus {
if userDid == subjectDid {
return IsSelf
-
} else if _, err := d.GetFollow(userDid, subjectDid); err != nil {
+
} else if _, err := GetFollow(e, userDid, subjectDid); err != nil {
return IsNotFollowing
} else {
return IsFollowing
}
}
-
func (d *DB) GetAllFollows() ([]Follow, error) {
+
func GetAllFollows(e Execer) ([]Follow, error) {
var follows []Follow
-
rows, err := d.db.Query(`select user_did, subject_did, followed_at, rkey from follows`)
+
rows, err := e.Query(`select user_did, subject_did, followed_at, rkey from follows`)
if err != nil {
return nil, err
}
+25 -29
appview/db/issues.go
···
Created *time.Time
}
-
func (d *DB) NewIssue(issue *Issue) error {
-
tx, err := d.db.Begin()
-
if err != nil {
-
return err
-
}
+
func NewIssue(tx *sql.Tx, issue *Issue) error {
defer tx.Rollback()
-
_, err = tx.Exec(`
+
_, err := tx.Exec(`
insert or ignore into repo_issue_seqs (repo_at, next_issue_id)
values (?, 1)
`, issue.RepoAt)
···
return nil
}
-
func (d *DB) SetIssueAt(repoAt string, issueId int, issueAt string) error {
-
_, err := d.db.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId)
+
func SetIssueAt(e Execer, repoAt string, issueId int, issueAt string) error {
+
_, err := e.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId)
return err
}
-
func (d *DB) GetIssueAt(repoAt string, issueId int) (string, error) {
+
func GetIssueAt(e Execer, repoAt string, issueId int) (string, error) {
var issueAt string
-
err := d.db.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt)
+
err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt)
return issueAt, err
}
-
func (d *DB) GetIssueId(repoAt string) (int, error) {
+
func GetIssueId(e Execer, repoAt string) (int, error) {
var issueId int
-
err := d.db.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId)
+
err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId)
return issueId - 1, err
}
-
func (d *DB) GetIssueOwnerDid(repoAt string, issueId int) (string, error) {
+
func GetIssueOwnerDid(e Execer, repoAt string, issueId int) (string, error) {
var ownerDid string
-
err := d.db.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid)
+
err := e.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid)
return ownerDid, err
}
-
func (d *DB) GetIssues(repoAt string) ([]Issue, error) {
+
func GetIssues(e Execer, repoAt string) ([]Issue, error) {
var issues []Issue
-
rows, err := d.db.Query(`select owner_did, issue_id, created, title, body, open from issues where repo_at = ? order by created desc`, repoAt)
+
rows, err := e.Query(`select owner_did, issue_id, created, title, body, open from issues where repo_at = ? order by created desc`, repoAt)
if err != nil {
return nil, err
}
···
return issues, nil
}
-
func (d *DB) GetIssue(repoAt string, issueId int) (*Issue, error) {
+
func GetIssue(e Execer, repoAt string, issueId int) (*Issue, error) {
query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?`
-
row := d.db.QueryRow(query, repoAt, issueId)
+
row := e.QueryRow(query, repoAt, issueId)
var issue Issue
var createdAt string
···
return &issue, nil
}
-
func (d *DB) GetIssueWithComments(repoAt string, issueId int) (*Issue, []Comment, error) {
+
func GetIssueWithComments(e Execer, repoAt string, issueId int) (*Issue, []Comment, error) {
query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?`
-
row := d.db.QueryRow(query, repoAt, issueId)
+
row := e.QueryRow(query, repoAt, issueId)
var issue Issue
var createdAt string
···
}
issue.Created = &createdTime
-
comments, err := d.GetComments(repoAt, issueId)
+
comments, err := GetComments(e, repoAt, issueId)
if err != nil {
return nil, nil, err
}
···
return &issue, comments, nil
}
-
func (d *DB) NewComment(comment *Comment) error {
+
func NewComment(e Execer, comment *Comment) error {
query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)`
-
_, err := d.db.Exec(
+
_, err := e.Exec(
query,
comment.OwnerDid,
comment.RepoAt,
···
return err
}
-
func (d *DB) GetComments(repoAt string, issueId int) ([]Comment, error) {
+
func GetComments(e Execer, repoAt string, issueId int) ([]Comment, error) {
var comments []Comment
-
rows, err := d.db.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId)
+
rows, err := e.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId)
if err == sql.ErrNoRows {
return []Comment{}, nil
}
···
return comments, nil
}
-
func (d *DB) CloseIssue(repoAt string, issueId int) error {
-
_, err := d.db.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId)
+
func CloseIssue(e Execer, repoAt string, issueId int) error {
+
_, err := e.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId)
return err
}
-
func (d *DB) ReopenIssue(repoAt string, issueId int) error {
-
_, err := d.db.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId)
+
func ReopenIssue(e Execer, repoAt string, issueId int) error {
+
_, err := e.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId)
return err
}
+10 -6
appview/db/jetstream.go
···
package db
-
func (d *DB) SaveLastTimeUs(lastTimeUs int64) error {
-
_, err := d.db.Exec(`insert into _jetstream (last_time_us) values (?)`, lastTimeUs)
+
type DbWrapper struct {
+
Execer
+
}
+
+
func (db DbWrapper) SaveLastTimeUs(lastTimeUs int64) error {
+
_, err := db.Exec(`insert into _jetstream (last_time_us) values (?)`, lastTimeUs)
return err
}
-
func (d *DB) UpdateLastTimeUs(lastTimeUs int64) error {
-
_, err := d.db.Exec(`update _jetstream set last_time_us = ? where rowid = 1`, lastTimeUs)
+
func (db DbWrapper) UpdateLastTimeUs(lastTimeUs int64) error {
+
_, err := db.Exec(`update _jetstream set last_time_us = ? where rowid = 1`, lastTimeUs)
if err != nil {
return err
}
return nil
}
-
func (d *DB) GetLastTimeUs() (int64, error) {
+
func (db DbWrapper) GetLastTimeUs() (int64, error) {
var lastTimeUs int64
-
row := d.db.QueryRow(`select last_time_us from _jetstream`)
+
row := db.QueryRow(`select last_time_us from _jetstream`)
err := row.Scan(&lastTimeUs)
return lastTimeUs, err
}
+8 -8
appview/db/pubkeys.go
···
"time"
)
-
func (d *DB) AddPublicKey(did, name, key string) error {
+
func AddPublicKey(e Execer, did, name, key string) error {
query := `insert or ignore into public_keys (did, name, key) values (?, ?, ?)`
-
_, err := d.db.Exec(query, did, name, key)
+
_, err := e.Exec(query, did, name, key)
return err
}
-
func (d *DB) RemovePublicKey(did string) error {
+
func RemovePublicKey(e Execer, did string) error {
query := `delete from public_keys where did = ?`
-
_, err := d.db.Exec(query, did)
+
_, err := e.Exec(query, did)
return err
}
···
})
}
-
func (d *DB) GetAllPublicKeys() ([]PublicKey, error) {
+
func GetAllPublicKeys(e Execer) ([]PublicKey, error) {
var keys []PublicKey
-
rows, err := d.db.Query(`select key, name, did, created from public_keys`)
+
rows, err := e.Query(`select key, name, did, created from public_keys`)
if err != nil {
return nil, err
}
···
return keys, nil
}
-
func (d *DB) GetPublicKeys(did string) ([]PublicKey, error) {
+
func GetPublicKeys(e Execer, did string) ([]PublicKey, error) {
var keys []PublicKey
-
rows, err := d.db.Query(`select did, key, name, created from public_keys where did = ?`, did)
+
rows, err := e.Query(`select did, key, name, created from public_keys where did = ?`, did)
if err != nil {
return nil, err
}
+11 -11
appview/db/registration.go
···
)
// returns registered status, did of owner, error
-
func (d *DB) RegistrationsByDid(did string) ([]Registration, error) {
+
func RegistrationsByDid(e Execer, did string) ([]Registration, error) {
var registrations []Registration
-
rows, err := d.db.Query(`
+
rows, err := e.Query(`
select domain, did, created, registered from registrations
where did = ?
`, did)
···
}
// returns registered status, did of owner, error
-
func (d *DB) RegistrationByDomain(domain string) (*Registration, error) {
+
func RegistrationByDomain(e Execer, domain string) (*Registration, error) {
var createdAt *string
var registeredAt *string
var registration Registration
-
err := d.db.QueryRow(`
+
err := e.QueryRow(`
select domain, did, created, registered from registrations
where domain = ?
`, domain).Scan(&registration.Domain, &registration.ByDid, &createdAt, &registeredAt)
···
return hex.EncodeToString(key)
}
-
func (d *DB) GenerateRegistrationKey(domain, did string) (string, error) {
+
func GenerateRegistrationKey(e Execer, domain, did string) (string, error) {
// sanity check: does this domain already have a registration?
-
reg, err := d.RegistrationByDomain(domain)
+
reg, err := RegistrationByDomain(e, domain)
if err != nil {
return "", err
}
···
secret := genSecret()
-
_, err = d.db.Exec(`
+
_, err = e.Exec(`
insert into registrations (domain, did, secret)
values (?, ?, ?)
on conflict(domain) do update set did = excluded.did, secret = excluded.secret
···
return secret, nil
}
-
func (d *DB) GetRegistrationKey(domain string) (string, error) {
-
res := d.db.QueryRow(`select secret from registrations where domain = ?`, domain)
+
func GetRegistrationKey(e Execer, domain string) (string, error) {
+
res := e.QueryRow(`select secret from registrations where domain = ?`, domain)
var secret string
err := res.Scan(&secret)
···
return secret, nil
}
-
func (d *DB) Register(domain string) error {
-
_, err := d.db.Exec(`
+
func Register(e Execer, domain string) error {
+
_, err := e.Exec(`
update registrations
set registered = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
where domain = ?;
+14 -14
appview/db/repos.go
···
AtUri string
}
-
func (d *DB) GetAllRepos() ([]Repo, error) {
+
func GetAllRepos(e Execer) ([]Repo, error) {
var repos []Repo
-
rows, err := d.db.Query(`select did, name, knot, rkey, created from repos`)
+
rows, err := e.Query(`select did, name, knot, rkey, created from repos`)
if err != nil {
return nil, err
}
···
return repos, nil
}
-
func (d *DB) GetAllReposByDid(did string) ([]Repo, error) {
+
func GetAllReposByDid(e Execer, did string) ([]Repo, error) {
var repos []Repo
-
rows, err := d.db.Query(`select did, name, knot, rkey, created from repos where did = ?`, did)
+
rows, err := e.Query(`select did, name, knot, rkey, created from repos where did = ?`, did)
if err != nil {
return nil, err
}
···
return repos, nil
}
-
func (d *DB) GetRepo(did, name string) (*Repo, error) {
+
func GetRepo(e Execer, did, name string) (*Repo, error) {
var repo Repo
-
row := d.db.QueryRow(`select did, name, knot, created, at_uri from repos where did = ? and name = ?`, did, name)
+
row := e.QueryRow(`select did, name, knot, created, at_uri from repos where did = ? and name = ?`, did, name)
var createdAt string
if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri); err != nil {
···
return &repo, nil
}
-
func (d *DB) AddRepo(repo *Repo) error {
-
_, err := d.db.Exec(`insert into repos (did, name, knot, rkey, at_uri) values (?, ?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri)
+
func AddRepo(e Execer, repo *Repo) error {
+
_, err := e.Exec(`insert into repos (did, name, knot, rkey, at_uri) values (?, ?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri)
return err
}
-
func (d *DB) RemoveRepo(did, name, rkey string) error {
-
_, err := d.db.Exec(`delete from repos where did = ? and name = ? and rkey = ?`, did, name, rkey)
+
func RemoveRepo(e Execer, did, name, rkey string) error {
+
_, err := e.Exec(`delete from repos where did = ? and name = ? and rkey = ?`, did, name, rkey)
return err
}
-
func (d *DB) AddCollaborator(collaborator, repoOwnerDid, repoName, repoKnot string) error {
-
_, err := d.db.Exec(
+
func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error {
+
_, err := e.Exec(
`insert into collaborators (did, repo)
values (?, (select id from repos where did = ? and name = ? and knot = ?));`,
collaborator, repoOwnerDid, repoName, repoKnot)
return err
}
-
func (d *DB) CollaboratingIn(collaborator string) ([]Repo, error) {
+
func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) {
var repos []Repo
-
rows, err := d.db.Query(`select r.did, r.name, r.knot, r.rkey, r.created from repos r join collaborators c on r.id = c.repo where c.did = ?;`, collaborator)
+
rows, err := e.Query(`select r.did, r.name, r.knot, r.rkey, r.created from repos r join collaborators c on r.id = c.repo where c.did = ?;`, collaborator)
if err != nil {
return nil, err
}
+3 -3
appview/db/timeline.go
···
EventAt time.Time
}
-
func (d *DB) MakeTimeline() ([]TimelineEvent, error) {
+
func MakeTimeline(e Execer) ([]TimelineEvent, error) {
var events []TimelineEvent
-
repos, err := d.GetAllRepos()
+
repos, err := GetAllRepos(e)
if err != nil {
return nil, err
}
-
follows, err := d.GetAllFollows()
+
follows, err := GetAllFollows(e)
if err != nil {
return nil, err
}
+1 -1
appview/pages/templates/repo/new.html
···
type="text"
id="branch"
name="branch"
+
value="main"
required
class="w-full max-w-md"
/>
-
<p class="text-sm text-gray-500">The default branch is <span class="font-bold">main</span></p>
</div>
<fieldset class="space-y-3">
+4 -3
appview/state/follow.go
···
comatproto "github.com/bluesky-social/indigo/api/atproto"
lexutil "github.com/bluesky-social/indigo/lex/util"
tangled "github.com/sotangled/tangled/api/tangled"
+
"github.com/sotangled/tangled/appview/db"
)
func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
···
return
}
-
err = s.db.AddFollow(currentUser.Did, subjectIdent.DID.String(), rkey)
+
err = db.AddFollow(s.db, currentUser.Did, subjectIdent.DID.String(), rkey)
if err != nil {
log.Println("failed to follow", err)
return
···
return
case http.MethodDelete:
// find the record in the db
-
follow, err := s.db.GetFollow(currentUser.Did, subjectIdent.DID.String())
+
follow, err := db.GetFollow(s.db, currentUser.Did, subjectIdent.DID.String())
if err != nil {
log.Println("failed to get follow relationship")
return
···
return
}
-
err = s.db.DeleteFollow(currentUser.Did, subjectIdent.DID.String())
+
err = db.DeleteFollow(s.db, currentUser.Did, subjectIdent.DID.String())
if err != nil {
log.Println("failed to delete follow from DB")
// this is not an issue, the firehose event might have already done this
+3 -3
appview/state/jetstream.go
···
type Ingester func(ctx context.Context, e *models.Event) error
-
func jetstreamIngester(db *db.DB) Ingester {
+
func jetstreamIngester(d db.DbWrapper) Ingester {
return func(ctx context.Context, e *models.Event) error {
var err error
defer func() {
eventTime := e.TimeUS
lastTimeUs := eventTime + 1
-
if err := db.UpdateLastTimeUs(lastTimeUs); err != nil {
+
if err := d.UpdateLastTimeUs(lastTimeUs); err != nil {
err = fmt.Errorf("(deferred) failed to save last time us: %w", err)
}
}()
···
log.Println("invalid record")
return err
}
-
err = db.AddFollow(did, record.Subject, e.Commit.RKey)
+
err = db.AddFollow(d, did, record.Subject, e.Commit.RKey)
if err != nil {
return fmt.Errorf("failed to add follow to db: %w", err)
}
+2 -1
appview/state/middleware.go
···
"github.com/go-chi/chi/v5"
"github.com/sotangled/tangled/appview"
"github.com/sotangled/tangled/appview/auth"
+
"github.com/sotangled/tangled/appview/db"
)
type Middleware func(http.Handler) http.Handler
···
return
}
-
repo, err := s.db.GetRepo(id.DID.String(), repoName)
+
repo, err := db.GetRepo(s.db, id.DID.String(), repoName)
if err != nil {
// invalid did or handle
log.Println("failed to resolve repo")
+46 -12
appview/state/repo.go
···
// TODO: create an atproto record for this
-
secret, err := s.db.GetRegistrationKey(f.Knot)
+
secret, err := db.GetRegistrationKey(s.db, f.Knot)
if err != nil {
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
return
···
return
}
+
tx, err := s.db.BeginTx(r.Context(), nil)
+
if err != nil {
+
log.Println("failed to start tx")
+
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
+
return
+
}
+
defer func() {
+
tx.Rollback()
+
err = s.enforcer.E.LoadPolicy()
+
if err != nil {
+
log.Println("failed to rollback policies")
+
}
+
}()
+
err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.OwnerSlashRepo())
if err != nil {
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
return
}
-
err = s.db.AddCollaborator(collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
+
err = db.AddCollaborator(s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
if err != nil {
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
return
}
+
err = tx.Commit()
+
if err != nil {
+
log.Println("failed to commit changes", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
+
err = s.enforcer.E.SavePolicy()
+
if err != nil {
+
log.Println("failed to update ACLs", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
w.Write([]byte(fmt.Sprint("added collaborator: ", collaboratorIdent.Handle.String())))
}
···
return
}
-
issue, comments, err := s.db.GetIssueWithComments(f.RepoAt, issueIdInt)
+
issue, comments, err := db.GetIssueWithComments(s.db, f.RepoAt, issueIdInt)
if err != nil {
log.Println("failed to get issue and comments", err)
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
···
return
}
-
issue, err := s.db.GetIssue(f.RepoAt, issueIdInt)
+
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
if err != nil {
log.Println("failed to get issue", err)
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
return
}
-
err := s.db.CloseIssue(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.")
···
}
if user.Did == f.OwnerDid() {
-
err := s.db.ReopenIssue(f.RepoAt, issueIdInt)
+
err := db.ReopenIssue(s.db, f.RepoAt, issueIdInt)
if err != nil {
log.Println("failed to reopen issue", err)
s.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
···
commentId := rand.IntN(1000000)
-
err := s.db.NewComment(&db.Comment{
+
err := db.NewComment(s.db, &db.Comment{
OwnerDid: user.Did,
RepoAt: f.RepoAt,
Issue: issueIdInt,
···
createdAt := time.Now().Format(time.RFC3339)
commentIdInt64 := int64(commentId)
ownerDid := user.Did
-
issueAt, err := s.db.GetIssueAt(f.RepoAt, issueIdInt)
+
issueAt, err := db.GetIssueAt(s.db, f.RepoAt, issueIdInt)
if err != nil {
log.Println("failed to get issue at", err)
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
···
return
}
-
issues, err := s.db.GetIssues(f.RepoAt)
+
issues, err := db.GetIssues(s.db, f.RepoAt)
if err != nil {
log.Println("failed to get issues", err)
s.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
return
}
-
err = s.db.NewIssue(&db.Issue{
+
tx, err := s.db.BeginTx(r.Context(), nil)
+
if err != nil {
+
s.pages.Notice(w, "issues", "Failed to create issue, try again later")
+
return
+
}
+
+
err = db.NewIssue(tx, &db.Issue{
RepoAt: f.RepoAt,
Title: title,
Body: body,
···
return
}
-
issueId, err := s.db.GetIssueId(f.RepoAt)
+
issueId, err := db.GetIssueId(s.db, f.RepoAt)
if err != nil {
log.Println("failed to get issue id", err)
s.pages.Notice(w, "issues", "Failed to create issue.")
···
return
}
-
err = s.db.SetIssueAt(f.RepoAt, issueId, resp.Uri)
+
err = db.SetIssueAt(s.db, f.RepoAt, issueId, resp.Uri)
if err != nil {
log.Println("failed to set issue at", err)
s.pages.Notice(w, "issues", "Failed to create issue.")
+3 -2
appview/state/settings.go
···
lexutil "github.com/bluesky-social/indigo/lex/util"
"github.com/gliderlabs/ssh"
"github.com/sotangled/tangled/api/tangled"
+
"github.com/sotangled/tangled/appview/db"
"github.com/sotangled/tangled/appview/pages"
)
func (s *State) Settings(w http.ResponseWriter, r *http.Request) {
// for now, this is just pubkeys
user := s.auth.GetUser(r)
-
pubKeys, err := s.db.GetPublicKeys(user.Did)
+
pubKeys, err := db.GetPublicKeys(s.db, user.Did)
if err != nil {
log.Println(err)
}
···
return
}
-
if err := s.db.AddPublicKey(did, name, key); err != nil {
+
if err := db.AddPublicKey(s.db, did, name, key); err != nil {
log.Printf("adding public key: %s", err)
s.pages.Notice(w, "settings-keys", "Failed to add public key.")
return
+99 -36
appview/state/state.go
···
}
func Make(config *appview.Config) (*State, error) {
-
db, err := db.Make(config.DbPath)
+
d, err := db.Make(config.DbPath)
if err != nil {
return nil, err
}
···
resolver := appview.NewResolver()
-
jc, err := jetstream.NewJetstreamClient("appview", []string{tangled.GraphFollowNSID}, nil, slog.Default(), db, false)
+
wrapper := db.DbWrapper{d}
+
jc, err := jetstream.NewJetstreamClient("appview", []string{tangled.GraphFollowNSID}, nil, slog.Default(), wrapper, false)
if err != nil {
return nil, fmt.Errorf("failed to create jetstream client: %w", err)
}
-
err = jc.StartJetstream(context.Background(), jetstreamIngester(db))
+
err = jc.StartJetstream(context.Background(), jetstreamIngester(wrapper))
if err != nil {
return nil, fmt.Errorf("failed to start jetstream watcher: %w", err)
}
state := &State{
-
db,
+
d,
auth,
enforcer,
clock,
···
func (s *State) Timeline(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
timeline, err := s.db.MakeTimeline()
+
timeline, err := db.MakeTimeline(s.db)
if err != nil {
log.Println(err)
s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.")
···
return
}
-
key, err := s.db.GenerateRegistrationKey(domain, did)
+
key, err := db.GenerateRegistrationKey(s.db, domain, did)
if err != nil {
log.Println(err)
···
return
}
-
pubKeys, err := s.db.GetPublicKeys(id.DID.String())
+
pubKeys, err := db.GetPublicKeys(s.db, id.DID.String())
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
···
}
log.Println("checking ", domain)
-
secret, err := s.db.GetRegistrationKey(domain)
+
secret, err := db.GetRegistrationKey(s.db, domain)
if err != nil {
log.Printf("no key found for domain %s: %s\n", domain, err)
return
···
return
}
+
tx, err := s.db.BeginTx(r.Context(), nil)
+
if err != nil {
+
log.Println("failed to start tx", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
defer func() {
+
tx.Rollback()
+
err = s.enforcer.E.LoadPolicy()
+
if err != nil {
+
log.Println("failed to rollback policies")
+
}
+
}()
+
// mark as registered
-
err = s.db.Register(domain)
+
err = db.Register(tx, domain)
if err != nil {
log.Println("failed to register domain", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
}
// set permissions for this did as owner
-
reg, err := s.db.RegistrationByDomain(domain)
+
reg, err := db.RegistrationByDomain(tx, domain)
if err != nil {
log.Println("failed to register domain", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
return
}
+
err = tx.Commit()
+
if err != nil {
+
log.Println("failed to commit changes", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
+
err = s.enforcer.E.SavePolicy()
+
if err != nil {
+
log.Println("failed to update ACLs", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
w.Write([]byte("check success"))
}
···
}
user := s.auth.GetUser(r)
-
reg, err := s.db.RegistrationByDomain(domain)
+
reg, err := db.RegistrationByDomain(s.db, domain)
if err != nil {
w.Write([]byte("failed to pull up registration info"))
return
···
func (s *State) Knots(w http.ResponseWriter, r *http.Request) {
// for now, this is just pubkeys
user := s.auth.GetUser(r)
-
registrations, err := s.db.RegistrationsByDid(user.Did)
+
registrations, err := db.RegistrationsByDid(s.db, user.Did)
if err != nil {
log.Println(err)
}
···
}
log.Println("created atproto record: ", resp.Uri)
-
secret, err := s.db.GetRegistrationKey(domain)
+
secret, err := db.GetRegistrationKey(s.db, domain)
if err != nil {
log.Printf("no key found for domain %s: %s\n", domain, err)
return
···
return
}
-
secret, err := s.db.GetRegistrationKey(domain)
+
existingRepo, err := db.GetRepo(s.db, user.Did, repoName)
+
if err == nil && existingRepo != nil {
+
s.pages.Notice(w, "repo", fmt.Sprintf("A repo by this name already exists on %s", existingRepo.Knot))
+
return
+
}
+
+
secret, err := db.GetRegistrationKey(s.db, domain)
if err != nil {
s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", domain))
return
···
return
}
-
resp, err := client.NewRepo(user.Did, repoName, defaultBranch)
-
if err != nil {
-
s.pages.Notice(w, "repo", "Failed to create repository on knot server.")
-
return
-
}
-
-
switch resp.StatusCode {
-
case http.StatusConflict:
-
s.pages.Notice(w, "repo", "A repository with that name already exists.")
-
return
-
case http.StatusInternalServerError:
-
s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
-
case http.StatusNoContent:
-
// continue
-
}
-
rkey := s.TID()
repo := &db.Repo{
Did: user.Did,
···
}
log.Println("created repo record: ", atresp.Uri)
-
repo.AtUri = atresp.Uri
+
tx, err := s.db.BeginTx(r.Context(), nil)
+
if err != nil {
+
log.Println(err)
+
s.pages.Notice(w, "repo", "Failed to save repository information.")
+
return
+
}
+
defer func() {
+
tx.Rollback()
+
err = s.enforcer.E.LoadPolicy()
+
if err != nil {
+
log.Println("failed to rollback policies")
+
}
+
}()
-
err = s.db.AddRepo(repo)
+
resp, err := client.NewRepo(user.Did, repoName, defaultBranch)
+
if err != nil {
+
s.pages.Notice(w, "repo", "Failed to create repository on knot server.")
+
return
+
}
+
+
switch resp.StatusCode {
+
case http.StatusConflict:
+
s.pages.Notice(w, "repo", "A repository with that name already exists.")
+
return
+
case http.StatusInternalServerError:
+
s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
+
case http.StatusNoContent:
+
// continue
+
}
+
+
repo.AtUri = atresp.Uri
+
err = db.AddRepo(tx, repo)
if err != nil {
log.Println(err)
s.pages.Notice(w, "repo", "Failed to save repository information.")
···
return
}
+
err = tx.Commit()
+
if err != nil {
+
log.Println("failed to commit changes", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
+
err = s.enforcer.E.SavePolicy()
+
if err != nil {
+
log.Println("failed to update ACLs", err)
+
http.Error(w, err.Error(), http.StatusInternalServerError)
+
return
+
}
+
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, repoName))
return
}
···
return
}
-
repos, err := s.db.GetAllReposByDid(ident.DID.String())
+
repos, err := db.GetAllReposByDid(s.db, ident.DID.String())
if err != nil {
log.Printf("getting repos for %s: %s", ident.DID.String(), err)
}
-
collaboratingRepos, err := s.db.CollaboratingIn(ident.DID.String())
+
collaboratingRepos, err := db.CollaboratingIn(s.db, ident.DID.String())
if err != nil {
log.Printf("getting collaborating repos for %s: %s", ident.DID.String(), err)
}
···
}
}
-
followers, following, err := s.db.GetFollowerFollowing(ident.DID.String())
+
followers, following, err := db.GetFollowerFollowing(s.db, ident.DID.String())
if err != nil {
log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err)
}
···
loggedInUser := s.auth.GetUser(r)
followStatus := db.IsNotFollowing
if loggedInUser != nil {
-
followStatus = s.db.GetFollowStatus(loggedInUser.Did, ident.DID.String())
+
followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String())
}
profileAvatarUri, err := GetAvatarUri(ident.DID.String())
···
r.Route("/repo", func(r chi.Router) {
r.Route("/new", func(r chi.Router) {
+
r.Use(AuthMiddleware(s))
r.Get("/", s.AddRepo)
r.Post("/", s.AddRepo)
})
+8 -7
rbac/rbac.go
···
"path"
"strings"
-
sqladapter "github.com/Blank-Xu/sql-adapter"
+
adapter "github.com/Blank-Xu/sql-adapter"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
)
···
)
type Enforcer struct {
-
E *casbin.SyncedEnforcer
+
E *casbin.Enforcer
}
func keyMatch2(key1 string, key2 string) bool {
···
return nil, err
}
-
a, err := sqladapter.NewAdapter(db, "sqlite3", "acl")
+
a, err := adapter.NewAdapter(db, "sqlite3", "acl")
if err != nil {
return nil, err
}
-
e, err := casbin.NewSyncedEnforcer(m, a)
+
e, err := casbin.NewEnforcer(m, a)
if err != nil {
return nil, err
}
-
e.EnableAutoSave(true)
+
e.EnableAutoSave(false)
+
e.AddFunction("keyMatch2", keyMatch2Func)
return &Enforcer{e}, nil
···
}
func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) {
-
return e.E.Enforcer.GetDomainsForUser(did)
+
return e.E.GetDomainsForUser(did)
}
func (e *Enforcer) AddOwner(domain, owner string) error {
···
// this includes roles too, casbin does not differentiate.
// the filtering criteria is to remove strings not starting with `did:`
-
members, err := e.E.Enforcer.GetImplicitUsersForRole(role, domain)
+
members, err := e.E.GetImplicitUsersForRole(role, domain)
for _, m := range members {
if strings.HasPrefix(m, "did:") {
membersWithoutRoles = append(membersWithoutRoles, m)