Working on Posting

This commit is contained in:
2026-02-04 22:06:36 -06:00
parent 4ed5d821da
commit 687e64e701
4 changed files with 79 additions and 14 deletions

View File

@@ -22,7 +22,6 @@ THE SOFTWARE.
package app package app
import ( import (
"context"
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
@@ -311,6 +310,7 @@ func (s *ScreenHome) initCli() {
s.cli.AddCommand(w.NewCliCommand("getpds", s.cliGetPds)) s.cli.AddCommand(w.NewCliCommand("getpds", s.cliGetPds))
s.cli.AddCommand(w.NewCliCommand("authpds", s.cliAuthPds)) s.cli.AddCommand(w.NewCliCommand("authpds", s.cliAuthPds))
s.cli.AddCommand(w.NewCliCommand("backuppds", s.cliBackupPds)) s.cli.AddCommand(w.NewCliCommand("backuppds", s.cliBackupPds))
s.cli.AddCommand(w.NewCliCommand("sendstatus", s.cliSendStatus))
} }
func (s *ScreenHome) toggleCli() { func (s *ScreenHome) toggleCli() {
@@ -373,19 +373,18 @@ func (s *ScreenHome) cliAuthPds(args ...string) bool {
return true return true
} }
s.isLoading = true s.isLoading = true
ctx := context.Background()
go func() { go func() {
defer func() { s.isLoading = false }() defer func() { s.isLoading = false }()
atid := s.activePds.AtId.String() atid := s.activePds.AtId.String()
callbackRes := make(chan url.Values, 1) callbackRes := make(chan url.Values, 1)
listenPort, err := s.r.Auth.ListenForCallback(ctx, callbackRes) listenPort, err := s.r.Auth.ListenForCallback(callbackRes)
if err != nil { if err != nil {
s.Log("Error Instantiating HTTP Server for Callback: %w", err) s.Log("Error Instantiating HTTP Server for Callback: %w", err)
return return
} }
s.Log("Listening on %d", listenPort) s.Log("Listening on %d", listenPort)
var authUrl string var authUrl string
authUrl, err = s.r.Auth.StartAuthFlow(listenPort, ctx, atid, callbackRes) authUrl, err = s.r.Auth.StartAuthFlow(listenPort, atid, callbackRes)
if err != nil { if err != nil {
s.Log("Error starting auth flow: %w", err) s.Log("Error starting auth flow: %w", err)
} }
@@ -414,6 +413,23 @@ func (s *ScreenHome) cliBackupPds(args ...string) bool {
return true return true
} }
func (s *ScreenHome) cliSendStatus(args ...string) bool {
if s.activePds == nil {
s.Log("No active PDS.")
return true
}
s.isLoading = true
go func() {
defer func() { s.isLoading = false }()
if !s.r.Auth.HasAuth() {
s.Log("Not authorized. Run `authpds`")
return
}
s.r.SendToPDS()
}()
return true
}
func (s *ScreenHome) updatePdsListing() { func (s *ScreenHome) updatePdsListing() {
s.pdsListing.SetTitle(fmt.Sprintf("─ %s (%s)", s.activePds.AtId.String(), s.activePds.Did.String())) s.pdsListing.SetTitle(fmt.Sprintf("─ %s (%s)", s.activePds.AtId.String(), s.activePds.Did.String()))
s.pdsListing.Clear() s.pdsListing.Clear()

View File

@@ -22,10 +22,13 @@ THE SOFTWARE.
package data package data
import ( import (
"context"
"fmt"
"log/slog" "log/slog"
"time" "time"
"git.bullercodeworks.com/brian/expds/data/models" "git.bullercodeworks.com/brian/expds/data/models"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@@ -37,6 +40,8 @@ type Repo struct {
handler *AppLogHandler handler *AppLogHandler
logFunc func(string, ...any) logFunc func(string, ...any)
context context.Context
} }
func NewRepo() (*Repo, error) { func NewRepo() (*Repo, error) {
@@ -44,6 +49,7 @@ func NewRepo() (*Repo, error) {
LoadedPDSs: make(map[string]*models.Pds), LoadedPDSs: make(map[string]*models.Pds),
BestBy: time.Minute * 15, BestBy: time.Minute * 15,
handler: NewAppLogHandler(nil), handler: NewAppLogHandler(nil),
context: context.Background(),
} }
if viper.GetBool(KeyDebug) { if viper.GetBool(KeyDebug) {
r.handler.SetLevel(slog.LevelDebug) r.handler.SetLevel(slog.LevelDebug)
@@ -70,6 +76,32 @@ func (r *Repo) GetPDS(atId string) (*models.Pds, error) {
return p, nil return p, nil
} }
func (r *Repo) SendToPDS() error {
session, err := r.Auth.GetSession()
if err != nil {
return err
}
c := session.APIClient()
body := map[string]any{
"repo": c.AccountDID.String(),
"collection": "com.bullercodeworks.expds.status",
"record": map[string]any{
"$type": "com.bullercodeworks.expds.status",
"text": "writeable",
"createdAt": syntax.DatetimeNow(),
},
}
var resp struct {
Uri syntax.ATURI `json:"uri"`
}
slog.Debug("posting expds status...")
if err := c.Post(r.context, "com.atproto.repo.CreateRecord", body, &resp); err != nil {
return err
}
slog.Debug(fmt.Sprintf("posted: %s :: %s", resp.Uri.Authority(), resp.Uri.RecordKey()))
return nil
}
func (r *Repo) SetLogFunc(l func(string, ...any)) { func (r *Repo) SetLogFunc(l func(string, ...any)) {
r.logFunc = l r.logFunc = l
r.handler = NewAppLogHandler(r.logFunc) r.handler = NewAppLogHandler(r.logFunc)

View File

@@ -21,12 +21,16 @@ type AuthRepo struct {
oauthClient *oauth.ClientApp oauthClient *oauth.ClientApp
oauthConfig *oauth.ClientConfig oauthConfig *oauth.ClientConfig
store *SqliteStore store *SqliteStore
context context.Context
session *oauth.ClientSessionData session *oauth.ClientSessionData
} }
func NewAuthRepo(r *Repo) (*AuthRepo, error) { func NewAuthRepo(r *Repo) (*AuthRepo, error) {
a := &AuthRepo{r: r} a := &AuthRepo{
r: r,
context: r.context,
}
var err error var err error
a.oauthConfig, a.oauthClient, a.store, err = a.buildOAuthClient() a.oauthConfig, a.oauthClient, a.store, err = a.buildOAuthClient()
if err != nil { if err != nil {
@@ -39,7 +43,7 @@ func NewAuthRepo(r *Repo) (*AuthRepo, error) {
func (r *AuthRepo) buildOAuthClient() (*oauth.ClientConfig, *oauth.ClientApp, *SqliteStore, error) { func (r *AuthRepo) buildOAuthClient() (*oauth.ClientConfig, *oauth.ClientApp, *SqliteStore, error) {
config := oauth.ClientConfig{ config := oauth.ClientConfig{
ClientID: "https://expds.bullercodeworks.com/oauth-client-metadata.json", ClientID: "https://expds.bullercodeworks.com/oauth-client-metadata.json",
Scopes: []string{"atproto", "repo:*"}, Scopes: []string{"atproto", "repo:*", "blob:*/*"},
UserAgent: "expds", UserAgent: "expds",
} }
@@ -57,9 +61,9 @@ func (r *AuthRepo) buildOAuthClient() (*oauth.ClientConfig, *oauth.ClientApp, *S
return &config, oauthClient, store, nil return &config, oauthClient, store, nil
} }
func (r *AuthRepo) StartAuthFlow(port int, ctx context.Context, identifier string, callbackRes chan url.Values) (string, error) { func (r *AuthRepo) StartAuthFlow(port int, identifier string, callbackRes chan url.Values) (string, error) {
r.oauthConfig.CallbackURL = fmt.Sprintf("http://127.0.0.1:%d/callback", port) r.oauthConfig.CallbackURL = fmt.Sprintf("http://127.0.0.1:%d/callback", port)
authUrl, err := r.oauthClient.StartAuthFlow(ctx, identifier) authUrl, err := r.oauthClient.StartAuthFlow(r.context, identifier)
if err != nil { if err != nil {
return "", fmt.Errorf("error logging in: %w", err) return "", fmt.Errorf("error logging in: %w", err)
} }
@@ -68,7 +72,7 @@ func (r *AuthRepo) StartAuthFlow(port int, ctx context.Context, identifier strin
} }
exec.Command("xdg-open", authUrl).Run() exec.Command("xdg-open", authUrl).Run()
r.session, err = r.oauthClient.ProcessCallback(ctx, <-callbackRes) r.session, err = r.oauthClient.ProcessCallback(r.context, <-callbackRes)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -82,7 +86,7 @@ func (r *AuthRepo) prepareDbPath() string {
} }
// HTTP Server listening for OAuth Response // HTTP Server listening for OAuth Response
func (r *AuthRepo) ListenForCallback(ctx context.Context, res chan url.Values) (int, error) { func (r *AuthRepo) ListenForCallback(res chan url.Values) (int, error) {
listener, err := net.Listen("tcp", ":0") listener, err := net.Listen("tcp", ":0")
if err != nil { if err != nil {
return 0, err return 0, err
@@ -93,12 +97,12 @@ func (r *AuthRepo) ListenForCallback(ctx context.Context, res chan url.Values) (
Handler: mux, Handler: mux,
} }
mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/callback", func(w http.ResponseWriter, req *http.Request) {
res <- r.URL.Query() res <- req.URL.Query()
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(200) w.WriteHeader(200)
w.Write([]byte("<!DOCTYPE html><html><body><h2>expds</h2><p>You can safely close this window and return to your application.</p></body></html>\n")) w.Write([]byte("<!DOCTYPE html><html><body><h2>expds</h2><p>You can safely close this window and return to your application.</p></body></html>\n"))
go server.Shutdown(ctx) go server.Shutdown(r.context)
}) })
go func() { go func() {
@@ -110,3 +114,16 @@ func (r *AuthRepo) ListenForCallback(ctx context.Context, res chan url.Values) (
return listener.Addr().(*net.TCPAddr).Port, nil return listener.Addr().(*net.TCPAddr).Port, nil
} }
func (r *AuthRepo) HasAuth() bool {
sess, err := r.store.GetMostRecentSession(r.context)
return err != nil || sess == nil
}
func (r *AuthRepo) GetSession() (*oauth.ClientSession, error) {
sess, err := r.store.GetMostRecentSession(r.context)
if err != nil {
return nil, fmt.Errorf("error getting most recent session: %w", err)
}
return r.oauthClient.ResumeSession(r.context, sess.AccountDID, sess.SessionID)
}

View File

@@ -5,7 +5,7 @@
"authorization_code", "authorization_code",
"refresh_token" "refresh_token"
], ],
"scope": "atproto repo:*", "scope": "atproto repo:* blob:*/*",
"response_types": [ "response_types": [
"code" "code"
], ],