From 3eee34bfd410f058e0516983c2a5fd870dac0010 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Thu, 9 Oct 2025 16:34:15 -0700 Subject: [PATCH] remove astrolabe (moved to cobalt) --- cmd/astrolabe/README.md | 36 +-- cmd/astrolabe/handlers.go | 247 ------------------- cmd/astrolabe/main.go | 66 ----- cmd/astrolabe/parse.go | 23 -- cmd/astrolabe/parse_test.go | 23 -- cmd/astrolabe/renderer.go | 85 ------- cmd/astrolabe/service.go | 178 ------------- cmd/astrolabe/static/apple-touch-icon.png | Bin 14742 -> 0 bytes cmd/astrolabe/static/default-avatar.png | Bin 1624 -> 0 bytes cmd/astrolabe/static/favicon-16x16.png | Bin 316 -> 0 bytes cmd/astrolabe/static/favicon-32x32.png | Bin 769 -> 0 bytes cmd/astrolabe/static/favicon.ico | Bin 15406 -> 0 bytes cmd/astrolabe/static/favicon.png | Bin 6405 -> 0 bytes cmd/astrolabe/static/robots.txt | 9 - cmd/astrolabe/templates/account.html | 24 -- cmd/astrolabe/templates/base.html | 40 --- cmd/astrolabe/templates/error.html | 14 -- cmd/astrolabe/templates/home.html | 17 -- cmd/astrolabe/templates/repo.html | 16 -- cmd/astrolabe/templates/repo_collection.html | 19 -- cmd/astrolabe/templates/repo_record.html | 12 - 21 files changed, 1 insertion(+), 808 deletions(-) delete mode 100644 cmd/astrolabe/handlers.go delete mode 100644 cmd/astrolabe/main.go delete mode 100644 cmd/astrolabe/parse.go delete mode 100644 cmd/astrolabe/parse_test.go delete mode 100644 cmd/astrolabe/renderer.go delete mode 100644 cmd/astrolabe/service.go delete mode 100644 cmd/astrolabe/static/apple-touch-icon.png delete mode 100644 cmd/astrolabe/static/default-avatar.png delete mode 100644 cmd/astrolabe/static/favicon-16x16.png delete mode 100644 cmd/astrolabe/static/favicon-32x32.png delete mode 100644 cmd/astrolabe/static/favicon.ico delete mode 100644 cmd/astrolabe/static/favicon.png delete mode 100644 cmd/astrolabe/static/robots.txt delete mode 100644 cmd/astrolabe/templates/account.html delete mode 100644 cmd/astrolabe/templates/base.html delete mode 100644 cmd/astrolabe/templates/error.html delete mode 100644 cmd/astrolabe/templates/home.html delete mode 100644 cmd/astrolabe/templates/repo.html delete mode 100644 cmd/astrolabe/templates/repo_collection.html delete mode 100644 cmd/astrolabe/templates/repo_record.html diff --git a/cmd/astrolabe/README.md b/cmd/astrolabe/README.md index 9d04a032..7e738253 100644 --- a/cmd/astrolabe/README.md +++ b/cmd/astrolabe/README.md @@ -2,38 +2,4 @@ astrolabe: basic atproto network data explorer ============================================== -⚠️ This is a fun little proof-of-concept ⚠️ - - -## Run It - -The recommended way to run `astrolabe` is behind a `caddy` HTTPS server which does automatic on-demand SSL certificate registration (using Let's Encrypt). - -Build and run `astrolabe`: - - go build ./cmd/astrolabe - - # will listen on :8400 by default - ./astrolabe serve - -Create a `Caddyfile`: - -``` -{ - on_demand_tls { - interval 1h - burst 8 - } -} - -:443 { - reverse_proxy localhost:8400 - tls YOUREMAIL@example.com { - on_demand - } -} -``` - -Run `caddy`: - - caddy run +**NOTE: this proof-of-concept has moved to [cobalt](https://tangled.org/@bnewbold.net/cobalt/tree/main/cmd/astrolabe)** diff --git a/cmd/astrolabe/handlers.go b/cmd/astrolabe/handlers.go deleted file mode 100644 index fdfca1a5..00000000 --- a/cmd/astrolabe/handlers.go +++ /dev/null @@ -1,247 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net/http" - "strings" - - "github.com/bluesky-social/indigo/api/agnostic" - comatproto "github.com/bluesky-social/indigo/api/atproto" - _ "github.com/bluesky-social/indigo/api/bsky" - "github.com/bluesky-social/indigo/atproto/atdata" - "github.com/bluesky-social/indigo/atproto/identity" - "github.com/bluesky-social/indigo/atproto/syntax" - "github.com/bluesky-social/indigo/xrpc" - - "github.com/flosch/pongo2/v6" - "github.com/labstack/echo/v4" -) - -func (srv *Server) WebHome(c echo.Context) error { - info := pongo2.Context{} - return c.Render(http.StatusOK, "home.html", info) -} - -func (srv *Server) WebQuery(c echo.Context) error { - - // parse the q query param, redirect based on that - q := c.QueryParam("q") - if q == "" { - return c.Redirect(http.StatusFound, "/") - } - if strings.HasPrefix(q, "https://") { - q = ParseServiceURL(q) - } - if strings.HasPrefix(q, "at://") { - if strings.HasSuffix(q, "/") { - q = q[0 : len(q)-1] - } - - aturi, err := syntax.ParseATURI(q) - if err != nil { - return err - } - if aturi.RecordKey() != "" { - return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s/%s/%s", aturi.Authority(), aturi.Collection(), aturi.RecordKey())) - } - if aturi.Collection() != "" { - return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s/%s", aturi.Authority(), aturi.Collection())) - } - return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s", aturi.Authority())) - } - if strings.HasPrefix(q, "did:") { - return c.Redirect(http.StatusFound, fmt.Sprintf("/account/%s", q)) - } - _, err := syntax.ParseHandle(q) - if nil == err { - return c.Redirect(http.StatusFound, fmt.Sprintf("/account/%s", q)) - } - return echo.NewHTTPError(400, "failed to parse query") -} - -// e.GET("/account/:atid", srv.WebAccount) -func (srv *Server) WebAccount(c echo.Context) error { - ctx := c.Request().Context() - //req := c.Request() - info := pongo2.Context{} - - atid, err := syntax.ParseAtIdentifier(c.Param("atid")) - if err != nil { - return echo.NewHTTPError(404, "failed to parse handle or DID") - } - - ident, err := srv.dir.Lookup(ctx, *atid) - if err != nil { - // TODO: proper error page? - return err - } - - bdir := identity.BaseDirectory{} - doc, err := bdir.ResolveDID(ctx, ident.DID) - if nil == err { - b, err := json.MarshalIndent(doc, "", " ") - if err != nil { - return err - } - info["didDocJSON"] = string(b) - } - info["atid"] = atid - info["ident"] = ident - info["uri"] = atid - return c.Render(http.StatusOK, "account.html", info) -} - -// e.GET("/at/:atid", srv.WebRepo) -func (srv *Server) WebRepo(c echo.Context) error { - ctx := c.Request().Context() - //req := c.Request() - info := pongo2.Context{} - - atid, err := syntax.ParseAtIdentifier(c.Param("atid")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse handle or DID") - } - - ident, err := srv.dir.Lookup(ctx, *atid) - if err != nil { - // TODO: proper error page? - return err - } - info["atid"] = atid - info["ident"] = ident - info["uri"] = fmt.Sprintf("at://%s", atid) - - // create a new API client to connect to the account's PDS - xrpcc := xrpc.Client{ - Host: ident.PDSEndpoint(), - } - if xrpcc.Host == "" { - return fmt.Errorf("no PDS endpoint for identity") - } - - desc, err := comatproto.RepoDescribeRepo(ctx, &xrpcc, ident.DID.String()) - if err != nil { - return err - } - info["collections"] = desc.Collections - - return c.Render(http.StatusOK, "repo.html", info) -} - -// e.GET("/at/:atid/:collection", srv.WebCollection) -func (srv *Server) WebRepoCollection(c echo.Context) error { - ctx := c.Request().Context() - //req := c.Request() - info := pongo2.Context{} - - atid, err := syntax.ParseAtIdentifier(c.Param("atid")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse handle or DID") - } - - collection, err := syntax.ParseNSID(c.Param("collection")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse collection NSID") - } - - ident, err := srv.dir.Lookup(ctx, *atid) - if err != nil { - // TODO: proper error page? - return err - } - info["atid"] = atid - info["ident"] = ident - info["collection"] = collection - info["uri"] = fmt.Sprintf("at://%s/%s", atid, collection) - - // create a new API client to connect to the account's PDS - xrpcc := xrpc.Client{ - Host: ident.PDSEndpoint(), - } - if xrpcc.Host == "" { - return fmt.Errorf("no PDS endpoint for identity") - } - - cursor := c.QueryParam("cursor") - // collection string, cursor string, limit int64, repo string, reverse bool - resp, err := agnostic.RepoListRecords(ctx, &xrpcc, collection.String(), cursor, 100, ident.DID.String(), false) - if err != nil { - return err - } - recordURIs := make([]syntax.ATURI, len(resp.Records)) - for i, rec := range resp.Records { - aturi, err := syntax.ParseATURI(rec.Uri) - if err != nil { - return err - } - recordURIs[i] = aturi - } - if resp.Cursor != nil && *resp.Cursor != "" { - cursor = *resp.Cursor - } - - info["records"] = resp.Records - info["recordURIs"] = recordURIs - info["cursor"] = cursor - return c.Render(http.StatusOK, "repo_collection.html", info) -} - -// e.GET("/at/:atid/:collection/:rkey", srv.WebRecord) -func (srv *Server) WebRepoRecord(c echo.Context) error { - ctx := c.Request().Context() - //req := c.Request() - info := pongo2.Context{} - - atid, err := syntax.ParseAtIdentifier(c.Param("atid")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse handle or DID") - } - - collection, err := syntax.ParseNSID(c.Param("collection")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse collection NSID") - } - - rkey, err := syntax.ParseRecordKey(c.Param("rkey")) - if err != nil { - return echo.NewHTTPError(400, "failed to parse record key") - } - - ident, err := srv.dir.Lookup(ctx, *atid) - if err != nil { - // TODO: proper error page? - return err - } - info["atid"] = atid - info["ident"] = ident - info["collection"] = collection - info["rkey"] = rkey - info["uri"] = fmt.Sprintf("at://%s/%s/%s", atid, collection, rkey) - - xrpcc := xrpc.Client{ - Host: ident.PDSEndpoint(), - } - resp, err := agnostic.RepoGetRecord(ctx, &xrpcc, "", collection.String(), ident.DID.String(), rkey.String()) - if err != nil { - return echo.NewHTTPError(400, fmt.Sprintf("failed to load record: %s", err)) - } - - if nil == resp.Value { - return fmt.Errorf("empty record in response") - } - - record, err := atdata.UnmarshalJSON(*resp.Value) - if err != nil { - return fmt.Errorf("fetched record was invalid data: %w", err) - } - info["record"] = record - - b, err := json.MarshalIndent(record, "", " ") - if err != nil { - return err - } - info["recordJSON"] = string(b) - - return c.Render(http.StatusOK, "repo_record.html", info) -} diff --git a/cmd/astrolabe/main.go b/cmd/astrolabe/main.go deleted file mode 100644 index b9075b13..00000000 --- a/cmd/astrolabe/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "fmt" - slogging "log/slog" - "os" - - "github.com/carlmjohnson/versioninfo" - "github.com/urfave/cli/v2" - - _ "github.com/joho/godotenv/autoload" -) - -var ( - slog = slogging.New(slogging.NewJSONHandler(os.Stdout, nil)) - version = versioninfo.Short() -) - -func main() { - if err := run(os.Args); err != nil { - slog.Error("fatal", "err", err) - os.Exit(-1) - } -} - -func run(args []string) error { - - app := cli.App{ - Name: "astrolabe", - Usage: "public web interface to explore atproto network content", - } - - app.Commands = []*cli.Command{ - &cli.Command{ - Name: "serve", - Usage: "run the server", - Action: serve, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "bind", - Usage: "Specify the local IP/port to bind to", - Required: false, - Value: ":8400", - EnvVars: []string{"ASTROLABE_BIND"}, - }, - &cli.BoolFlag{ - Name: "debug", - Usage: "Enable debug mode", - Value: false, - Required: false, - EnvVars: []string{"DEBUG"}, - }, - }, - }, - &cli.Command{ - Name: "version", - Usage: "print version", - Action: func(cctx *cli.Context) error { - fmt.Println(version) - return nil - }, - }, - } - - return app.Run(args) -} diff --git a/cmd/astrolabe/parse.go b/cmd/astrolabe/parse.go deleted file mode 100644 index 89209f3e..00000000 --- a/cmd/astrolabe/parse.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "fmt" - "strings" -) - -// attempts to parse a service URL to an AT-URI, handle, or DID. if it can't, passes string through as-is -func ParseServiceURL(raw string) string { - parts := strings.Split(raw, "/") - if len(parts) < 3 || parts[0] != "https:" { - return raw - } - if parts[2] == "bsky.app" && len(parts) >= 5 && parts[3] == "profile" { - if len(parts) == 5 { - return parts[4] - } - if len(parts) == 7 && parts[5] == "post" { - return fmt.Sprintf("at://%s/app.bsky.feed.post/%s", parts[4], parts[6]) - } - } - return raw -} diff --git a/cmd/astrolabe/parse_test.go b/cmd/astrolabe/parse_test.go deleted file mode 100644 index c3c65cca..00000000 --- a/cmd/astrolabe/parse_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseServiceURL(t *testing.T) { - assert := assert.New(t) - - testVec := [][]string{ - {"", ""}, - {"atproto.com", "atproto.com"}, - {"https://bsky.app/profile/atproto.com", "atproto.com"}, - {"https://bsky.app/profile/did:plc:ewvi7nxzyoun6zhxrhs64oiz", "did:plc:ewvi7nxzyoun6zhxrhs64oiz"}, - {"https://bsky.app/profile/atproto.com/post/3lffzv6f4o22r", "at://atproto.com/app.bsky.feed.post/3lffzv6f4o22r"}, - } - - for _, pair := range testVec { - assert.Equal(pair[1], ParseServiceURL(pair[0])) - } -} diff --git a/cmd/astrolabe/renderer.go b/cmd/astrolabe/renderer.go deleted file mode 100644 index 0fa3cb1c..00000000 --- a/cmd/astrolabe/renderer.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "bytes" - "embed" - "errors" - "fmt" - "io" - "path/filepath" - - "github.com/flosch/pongo2/v6" - "github.com/labstack/echo/v4" -) - -//go:embed templates/* -var TemplateFS embed.FS - -type RendererLoader struct { - prefix string - fs *embed.FS -} - -func NewRendererLoader(prefix string, fs *embed.FS) pongo2.TemplateLoader { - return &RendererLoader{ - prefix: prefix, - fs: fs, - } -} -func (l *RendererLoader) Abs(_, name string) string { - // TODO: remove this workaround - // Figure out why this method is being called - // twice on template names resulting in a failure to resolve - // the template name. - if filepath.HasPrefix(name, l.prefix) { - return name - } - return filepath.Join(l.prefix, name) -} - -func (l *RendererLoader) Get(path string) (io.Reader, error) { - b, err := l.fs.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("reading template %q failed: %w", path, err) - } - return bytes.NewReader(b), nil -} - -type Renderer struct { - TemplateSet *pongo2.TemplateSet - Debug bool -} - -func NewRenderer(prefix string, fs *embed.FS, debug bool) *Renderer { - return &Renderer{ - TemplateSet: pongo2.NewSet(prefix, NewRendererLoader(prefix, fs)), - Debug: debug, - } -} - -func (r Renderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error { - var ctx pongo2.Context - - if data != nil { - var ok bool - ctx, ok = data.(pongo2.Context) - if !ok { - return errors.New("no pongo2.Context data was passed") - } - } - - var t *pongo2.Template - var err error - - if r.Debug { - t, err = pongo2.FromFile(name) - } else { - t, err = r.TemplateSet.FromFile(name) - } - - if err != nil { - return err - } - - return t.ExecuteWriter(ctx, w) -} diff --git a/cmd/astrolabe/service.go b/cmd/astrolabe/service.go deleted file mode 100644 index f7300c45..00000000 --- a/cmd/astrolabe/service.go +++ /dev/null @@ -1,178 +0,0 @@ -package main - -import ( - "context" - "embed" - "errors" - "fmt" - "io/fs" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "github.com/bluesky-social/indigo/atproto/identity" - - "github.com/flosch/pongo2/v6" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - slogecho "github.com/samber/slog-echo" - "github.com/urfave/cli/v2" -) - -//go:embed static/* -var StaticFS embed.FS - -type Server struct { - echo *echo.Echo - httpd *http.Server - dir identity.Directory -} - -func serve(cctx *cli.Context) error { - debug := cctx.Bool("debug") - httpAddress := cctx.String("bind") - - e := echo.New() - - // httpd - var ( - httpTimeout = 1 * time.Minute - httpMaxHeaderBytes = 1 * (1024 * 1024) - ) - - srv := &Server{ - echo: e, - dir: identity.DefaultDirectory(), - } - srv.httpd = &http.Server{ - Handler: srv, - Addr: httpAddress, - WriteTimeout: httpTimeout, - ReadTimeout: httpTimeout, - MaxHeaderBytes: httpMaxHeaderBytes, - } - - e.HideBanner = true - e.Use(slogecho.New(slog)) - e.Use(middleware.Recover()) - e.Use(middleware.BodyLimit("64M")) - e.HTTPErrorHandler = srv.errorHandler - e.Renderer = NewRenderer("templates/", &TemplateFS, debug) - e.Use(middleware.SecureWithConfig(middleware.SecureConfig{ - ContentTypeNosniff: "nosniff", - XFrameOptions: "SAMEORIGIN", - HSTSMaxAge: 31536000, // 365 days - // TODO: - // ContentSecurityPolicy - // XSSProtection - })) - - // redirect trailing slash to non-trailing slash. - // all of our current endpoints have no trailing slash. - e.Use(middleware.RemoveTrailingSlashWithConfig(middleware.TrailingSlashConfig{ - RedirectCode: http.StatusFound, - })) - - staticHandler := http.FileServer(func() http.FileSystem { - if debug { - return http.FS(os.DirFS("static")) - } - fsys, err := fs.Sub(StaticFS, "static") - if err != nil { - slog.Error("static template error", "err", err) - os.Exit(-1) - } - return http.FS(fsys) - }()) - - e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", staticHandler))) - e.GET("/_health", srv.HandleHealthCheck) - - // basic static routes - e.GET("/robots.txt", echo.WrapHandler(staticHandler)) - e.GET("/favicon.ico", echo.WrapHandler(staticHandler)) - - // actual content - e.GET("/", srv.WebHome) - e.GET("/query", srv.WebQuery) - //e.GET("/at://:rkey", srv.WebRedirect) - e.GET("/account/:atid", srv.WebAccount) - e.GET("/at/:atid", srv.WebRepo) - e.GET("/at/:atid/:collection", srv.WebRepoCollection) - e.GET("/at/:atid/:collection/:rkey", srv.WebRepoRecord) - - // Start the server - slog.Info("starting server", "bind", httpAddress) - go func() { - if err := srv.httpd.ListenAndServe(); err != nil { - if !errors.Is(err, http.ErrServerClosed) { - slog.Error("HTTP server shutting down unexpectedly", "err", err) - } - } - }() - - // Wait for a signal to exit. - slog.Info("registering OS exit signal handler") - quit := make(chan struct{}) - exitSignals := make(chan os.Signal, 1) - signal.Notify(exitSignals, syscall.SIGINT, syscall.SIGTERM) - go func() { - sig := <-exitSignals - slog.Info("received OS exit signal", "signal", sig) - - // Shut down the HTTP server - if err := srv.Shutdown(); err != nil { - slog.Error("HTTP server shutdown error", "err", err) - } - - // Trigger the return that causes an exit. - close(quit) - }() - <-quit - slog.Info("graceful shutdown complete") - return nil -} - -type GenericStatus struct { - Daemon string `json:"daemon"` - Status string `json:"status"` - Message string `json:"msg,omitempty"` -} - -func (srv *Server) errorHandler(err error, c echo.Context) { - code := http.StatusInternalServerError - var errorMessage string - if he, ok := err.(*echo.HTTPError); ok { - code = he.Code - errorMessage = fmt.Sprintf("%s", he.Message) - } - if code >= 500 { - slog.Warn("astrolabe-http-internal-error", "err", err) - } - data := pongo2.Context{ - "statusCode": code, - "errorMessage": errorMessage, - } - if !c.Response().Committed { - c.Render(code, "error.html", data) - } -} - -func (srv *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - srv.echo.ServeHTTP(rw, req) -} - -func (srv *Server) Shutdown() error { - slog.Info("shutting down") - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - return srv.httpd.Shutdown(ctx) -} - -func (s *Server) HandleHealthCheck(c echo.Context) error { - return c.JSON(200, GenericStatus{Status: "ok", Daemon: "astrolabe"}) -} diff --git a/cmd/astrolabe/static/apple-touch-icon.png b/cmd/astrolabe/static/apple-touch-icon.png deleted file mode 100644 index 5ebb6787c999108d56d1b2f2aa4a5f2275ac5e37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14742 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 diff --git a/cmd/astrolabe/static/default-avatar.png b/cmd/astrolabe/static/default-avatar.png deleted file mode 100644 index c732d942b33e04f1be7a9dced9211ec676de1056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1624 zcmeAS@N?(olHy`uVBq!ia0y~yVDMpJVDRQ(Vqjnh{qXGw0|T49r;B4q#hkaZbA6_! zN*u3WVsTDN>99>ltFwt^AD@{)p|9<$64|ik3kkxx$-WWOiVti$8`c+@JM9qr4Bi_> zXQ%7x3Wx7}Skr9Tuj+lW%)yyaxK&gCkp-94uZDvb=WFME-Xq8Hx4!z_^Ih*=|F?f` z`F!X5*Sp>=Xl!IWW<5_jm#Oi9@x;fh=Uw{5eN0NfP4kO68=@s`NKTOPgXo@*x7^oZPi zyYSA@d7oEp^-SD6Y43zNDTj2}1dQEMwzseM3#^gy-~aDqTOz00`GeC^tCmVBY+BNm zd;GhZrN>@PD~`L7h`yAl+z@}K`c1{;Bh!zRXXk(Tcz(`~MdqijO|6-zv`Nf5@L! zJ>gUFE~dz)ouL~4jGop_eyX>Z>$-|Y5XY3%4Qh_zI{x-g?c&snl)lfoE?Qc|v2=Cl zjp^&siaOSH?BnRR_PPn%B2v7U-%B?w%UU4I{xEOx(#zEWI+w1l(H2VJ z9jwQvUZBy(}Rs;+JI>Omy;!)7f;oBlq|{?hw-z8`ftfyk}*8XLoikc0-_F+I+9nY`du_h@%8s|8Uu;C~ zysbR(O>0uuihOgOMN{@v*h_BhjsDKlZPno^>`~i#eNnqH^Zu;?Kh9<_*i$#Vf`6^w!w%I*89(wpz)f)$|I31&NSyrN-6;$=bW-eHH&C(6b zvbkKM^~@0@YbA60|L4xFk*sGO?8KSznW1QPu8ko;mG_j>&;E(d|Vi*WTYGzUk?TZxb}l z?rqzgwQf=0&g}A)Sr3+5l}d~2r|66H&kj;MQU2lI>oeD;Oe)&G@wd*>O`%$fCet_% z={&K#;uIPN3&^!b!L zqo>~Gb>Ai@igC^Tm+aCSxc6j2tHU`ZzuE&LtaqDR{dTH3yV^2qmz@@!t$T3J&k3v- z|E_cl^{LM)%v2`77wE~4TU5Zb_r{(OgBkAwu9&P;nz&Va z>efzWw`ZfU9!8& z&67FGkz3|X$jydTV)y@^J0}0)ZFNy*mG%pTJ+rFf=N|0a9`W+=enCD6F6FOB~ zh2=fpge*G5vHimzm4^2I7j*~TR-2SQ$a~yByQ$=xgw-FTmcqK8liZJgI?NJz{4?WW o#NpsVp>H!!d~90o^kjd*Yh$nGg-&}J7#J8lUHx3vIVCg!0O3p$mjD0& diff --git a/cmd/astrolabe/static/favicon-16x16.png b/cmd/astrolabe/static/favicon-16x16.png deleted file mode 100644 index 4267562b808197d5b00b58f3ffdedde4b689b3c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7nLS+`LoEE4h8S`kauAr~ znXdTa@fud{jRGv(2OAO;mAJb6KQwFIxNmharl>PLL?mg_?z=PZ?wu)Mo4xSuwS-IU zk?ahmw*TLL4EZm4V)Zw_9g3=KWe!P`l`giYpY&fLwBnN=<4$G6Et{2g@tesDsPY}? zvWV-LSUfc(zG`o4!Cn=Eq&I60z5AV~vww+-8dHUA@YcC)-#Ri`6wFST7b~VQI?UBP z_U8I0Ys35%3tpOhvQ7G^f8Y~)!-A6Vf+P=Ug_CHvJE`*K V@_u*pU|?Wi@O1TaS?83{1OOS_e`^2$ diff --git a/cmd/astrolabe/static/favicon-32x32.png b/cmd/astrolabe/static/favicon-32x32.png deleted file mode 100644 index 869f6df5fc6bdd2c78f2ac91679fb6814f999485..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 769 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANM!Cp=voLn7SQhVRV^aTNJ? z?x=|thh59mt-TMtJvLl+Di9Uyb#!tO5o~?npD(#vVPA`YK(@C0A+1{~XDZU(&E5Tc z-dtnj@|&8QZqBu=zyDKx$>z8EkComnIILR{!}_Ok-=YfHf4@FP`wQ;dE??*S|Hn`L zW!8r{`94nlr*iE;Zar)Ck25jrKOZjsbCcVzsfcR}cl^FDPj1dI{`XK~dBg+mziKR} z^&d*d-)ZY;H{*apnF2}VvG9j!;jSyANoi9X!T5Z+7f-?nuvEv zWYsV49q*g$Z@txZj1ErwIxn&Qfm6z@%&FX4JQE}hjz=UWx$P?9;E!zdoojl>rNTrb zKxvv*)Wv+ujS|d^i;u5qXl;Bd6+7culEw>8f2r7j=Iv$qJ7#WB7P%l6UFx`dNyOil zl(YIJ9B;H(QbgCVE*IJ3SJkF+aPuT?k?U%TuL6&nw%9s1dPFoFe#N`59|wVvq6EYR*NG<|*i0k8ARYPr*$$BWLNdh$nm~)O+W164QsIPo3e$=4;Xn!Z!DEi1>8;y2_Ji%KcnffBD=9 zzh>(wfA4KQU+wdp)8BkbHmhp9yJ5~U;Ry|ozwUW`z4+1BJ=0{@dL3J2zvlTlS;o7H e^S-_QS+9J7zr7+tu!n(xfx*+&&t;ucLK6T@oM(;z diff --git a/cmd/astrolabe/static/favicon.ico b/cmd/astrolabe/static/favicon.ico deleted file mode 100644 index b4290a797a9f7ae4d14373ff1608be81d5940725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 aVb=d72Jin73{tQ(19I;u9t{CXLjVA0l*gw4 diff --git a/cmd/astrolabe/static/favicon.png b/cmd/astrolabe/static/favicon.png deleted file mode 100644 index 61cf7c943b6ac083ab0752f283391e37b39c1221..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6405 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 Q6Eu|X>FVdQ&MBb@0K66k1ONa4 diff --git a/cmd/astrolabe/static/robots.txt b/cmd/astrolabe/static/robots.txt deleted file mode 100644 index 4f8510d1..00000000 --- a/cmd/astrolabe/static/robots.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Hello Friends! -# If you are considering bulk or automated crawling, you may want to look in -# to our protocol (API), including a firehose of updates. See: https://atproto.com/ - -# By default, may crawl anything on this domain. HTTP 429 ("backoff") status -# codes are used for rate-limiting. Up to a handful concurrent requests should -# be ok. -User-Agent: * -Allow: / diff --git a/cmd/astrolabe/templates/account.html b/cmd/astrolabe/templates/account.html deleted file mode 100644 index dd27bc31..00000000 --- a/cmd/astrolabe/templates/account.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "base.html" %} - -{% block main_content %} -

{{ atid }}

- - - - - - - - - - -
DID{{ ident.DID }}
Handle{{ ident.Handle }}
PDS{{ ident.PDSEndpoint() }}
- -

Repo Index -

Repo CAR Export - -{% if didDocJSON %} -

DID Document

-
{{ didDocJSON }}
-{% endif %} -{% endblock %} diff --git a/cmd/astrolabe/templates/base.html b/cmd/astrolabe/templates/base.html deleted file mode 100644 index a7e4202c..00000000 --- a/cmd/astrolabe/templates/base.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - {% block head_title %}astrolabe{% endblock %} - - - - -
- {% block main_content %}Base Template{% endblock %} -
- - - diff --git a/cmd/astrolabe/templates/error.html b/cmd/astrolabe/templates/error.html deleted file mode 100644 index 08ea1c6b..00000000 --- a/cmd/astrolabe/templates/error.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} - -{% block head_title %}Error {{ statusCode }} - astrolabe{% endblock %} - -{% block main_content %} -
-
-

{{ statusCode }}

-

Error!

- {% if errorMessage %} -

{{ errorMessage }}

- {% endif %} -
-{% endblock %} diff --git a/cmd/astrolabe/templates/home.html b/cmd/astrolabe/templates/home.html deleted file mode 100644 index dd5a8b3e..00000000 --- a/cmd/astrolabe/templates/home.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} - -{% block main_content %} -

astrolabe: AT Protocol Repository Browser

- -

This is a tool for browsing AT Protocol ("atproto") repositories and records. You can enter an account identifier or full URI to view content as JSON. It works by fetching data directly from account PDS instances: the data itself is not hosted by this service. - -

Examples: -

- -

Other similar services include atproto-browser.vercel.app and pdsls.dev. -{% endblock %} diff --git a/cmd/astrolabe/templates/repo.html b/cmd/astrolabe/templates/repo.html deleted file mode 100644 index d139ec83..00000000 --- a/cmd/astrolabe/templates/repo.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "base.html" %} - -{% block main_content %} -

at://{{ atid }}

- -

Index

- - - - {% for collection in collections %} - - {% endfor %} - -
..
{{ collection }}/
- -{% endblock %} diff --git a/cmd/astrolabe/templates/repo_collection.html b/cmd/astrolabe/templates/repo_collection.html deleted file mode 100644 index 802b1358..00000000 --- a/cmd/astrolabe/templates/repo_collection.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "base.html" %} - -{% block main_content %} -

at://{{ atid }}/{{ collection }}

- -

Index

- - - - {% for uri in recordURIs %} - - {% endfor %} - {% if cursor != "" %} - - {% endif %} - -
..
{{ collection }}/{{ uri.RecordKey() }}
[more]
- -{% endblock %} diff --git a/cmd/astrolabe/templates/repo_record.html b/cmd/astrolabe/templates/repo_record.html deleted file mode 100644 index 09da3c57..00000000 --- a/cmd/astrolabe/templates/repo_record.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} - -{% block main_content %} -

at://{{ atid }}/{{ collection }}/{{ rkey }}

-

Back to Collection - -{% if recordJSON %} -

Record JSON

-
{{ recordJSON }}
-{% endif %} - -{% endblock %} -- 2.51.2