Doing some work
This commit is contained in:
182
data/models/pds.go
Normal file
182
data/models/pds.go
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright © Brian Buller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"git.bullercodeworks.com/brian/expds/helpers"
|
||||
comatproto "github.com/bluesky-social/indigo/api/atproto"
|
||||
"github.com/bluesky-social/indigo/atproto/atdata"
|
||||
"github.com/bluesky-social/indigo/atproto/identity"
|
||||
"github.com/bluesky-social/indigo/atproto/repo"
|
||||
"github.com/bluesky-social/indigo/atproto/syntax"
|
||||
"github.com/bluesky-social/indigo/xrpc"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type EntryType int
|
||||
|
||||
const (
|
||||
TypeNSID = EntryType(iota)
|
||||
TypeRecord
|
||||
)
|
||||
|
||||
type Pds struct {
|
||||
AtId syntax.AtIdentifier
|
||||
Did syntax.DID
|
||||
localPath string
|
||||
|
||||
Commit string
|
||||
NSIDs []syntax.NSID
|
||||
RecordIds []string
|
||||
nsidToRecordIds map[syntax.NSID][]string
|
||||
Records map[string]map[string]any
|
||||
|
||||
RefreshTime time.Time
|
||||
}
|
||||
|
||||
func NewPdsFromDid(id string) (*Pds, error) {
|
||||
ctx := context.Background()
|
||||
atid, err := syntax.ParseAtIdentifier(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// first look up the DID and PDS for this repo
|
||||
dir := identity.DefaultDirectory()
|
||||
ident, err := dir.Lookup(ctx, atid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create a new API client to connect to the account's PDS
|
||||
xrpcc := xrpc.Client{
|
||||
Host: ident.PDSEndpoint(),
|
||||
}
|
||||
if xrpcc.Host == "" {
|
||||
return nil, fmt.Errorf("no PDS endpoint for identity")
|
||||
}
|
||||
|
||||
carPath := helpers.Path(viper.GetString("data"), ident.DID.String()+".car")
|
||||
repoBytes, err := comatproto.SyncGetRepo(ctx, &xrpcc, ident.DID.String(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.WriteFile(carPath, repoBytes, 0666); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &Pds{
|
||||
AtId: atid,
|
||||
Did: ident.DID,
|
||||
localPath: carPath,
|
||||
nsidToRecordIds: make(map[syntax.NSID][]string),
|
||||
Records: make(map[string]map[string]any),
|
||||
RefreshTime: time.Now(),
|
||||
}
|
||||
return ret, ret.unpack()
|
||||
}
|
||||
|
||||
func (p *Pds) unpack() error {
|
||||
ctx := context.Background()
|
||||
fi, err := os.Open(p.localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening local car: %w", err)
|
||||
}
|
||||
|
||||
c, r, err := repo.LoadRepoFromCAR(ctx, fi)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error loading repo from car: %w", err)
|
||||
}
|
||||
|
||||
comBytes, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building commit meta json: %w")
|
||||
}
|
||||
p.Commit = string(comBytes)
|
||||
|
||||
err = r.MST.Walk(func(k []byte, v cid.Cid) error {
|
||||
col, rkey, err := syntax.ParseRepoPath(string(k))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing repo path (%s): %w", string(k), err)
|
||||
}
|
||||
|
||||
sCol, sRKey := string(col), string(rkey)
|
||||
recBytes, _, err := r.GetRecordBytes(ctx, col, rkey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting record bytes (%s/%s): %w", sCol, sRKey, err)
|
||||
}
|
||||
|
||||
rec, err := atdata.UnmarshalCBOR(recBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unmarshalling cbor (%s/%s): %w", sCol, sRKey, err)
|
||||
}
|
||||
p.Records[string(k)] = rec
|
||||
|
||||
if !slices.Contains(p.NSIDs, col) {
|
||||
p.NSIDs = append(p.NSIDs, col)
|
||||
}
|
||||
p.nsidToRecordIds[col] = append(p.nsidToRecordIds[col], sRKey)
|
||||
return nil
|
||||
})
|
||||
//slices.Sort(p.NSIDs)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Pds) NSIDStringList() []string {
|
||||
var ret []string
|
||||
for _, wrk := range p.NSIDs {
|
||||
ret = append(ret, wrk.String())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Pds) List() (map[string]string, error) {
|
||||
ret := make(map[string]string)
|
||||
ctx := context.Background()
|
||||
fi, err := os.Open(p.localPath)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("error opening local car: %w", err)
|
||||
}
|
||||
// read repository tree in to memory
|
||||
_, r, err := repo.LoadRepoFromCAR(ctx, fi)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("error loading repo from car: %w", err)
|
||||
}
|
||||
|
||||
err = r.MST.Walk(func(k []byte, v cid.Cid) error {
|
||||
ret[string(k)] = v.String()
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("error walking repo: %w", err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Pds) GetRecordIdsFor(n syntax.NSID) []string { return p.nsidToRecordIds[n] }
|
||||
Reference in New Issue
Block a user