porting all github actions from bluesky-social/indigo to tangled CI
at main 6.5 kB view raw
1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 "github.com/bluesky-social/indigo/api/agnostic" 10 comatproto "github.com/bluesky-social/indigo/api/atproto" 11 _ "github.com/bluesky-social/indigo/api/bsky" 12 "github.com/bluesky-social/indigo/atproto/data" 13 "github.com/bluesky-social/indigo/atproto/identity" 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 "github.com/bluesky-social/indigo/xrpc" 16 17 "github.com/flosch/pongo2/v6" 18 "github.com/labstack/echo/v4" 19) 20 21func (srv *Server) WebHome(c echo.Context) error { 22 info := pongo2.Context{} 23 return c.Render(http.StatusOK, "home.html", info) 24} 25 26func (srv *Server) WebQuery(c echo.Context) error { 27 28 // parse the q query param, redirect based on that 29 q := c.QueryParam("q") 30 if q == "" { 31 return c.Redirect(http.StatusFound, "/") 32 } 33 if strings.HasPrefix(q, "https://") { 34 q = ParseServiceURL(q) 35 } 36 if strings.HasPrefix(q, "at://") { 37 if strings.HasSuffix(q, "/") { 38 q = q[0 : len(q)-1] 39 } 40 41 aturi, err := syntax.ParseATURI(q) 42 if err != nil { 43 return err 44 } 45 if aturi.RecordKey() != "" { 46 return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s/%s/%s", aturi.Authority(), aturi.Collection(), aturi.RecordKey())) 47 } 48 if aturi.Collection() != "" { 49 return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s/%s", aturi.Authority(), aturi.Collection())) 50 } 51 return c.Redirect(http.StatusFound, fmt.Sprintf("/at/%s", aturi.Authority())) 52 } 53 if strings.HasPrefix(q, "did:") { 54 return c.Redirect(http.StatusFound, fmt.Sprintf("/account/%s", q)) 55 } 56 _, err := syntax.ParseHandle(q) 57 if nil == err { 58 return c.Redirect(http.StatusFound, fmt.Sprintf("/account/%s", q)) 59 } 60 return echo.NewHTTPError(400, "failed to parse query") 61} 62 63// e.GET("/account/:atid", srv.WebAccount) 64func (srv *Server) WebAccount(c echo.Context) error { 65 ctx := c.Request().Context() 66 //req := c.Request() 67 info := pongo2.Context{} 68 69 atid, err := syntax.ParseAtIdentifier(c.Param("atid")) 70 if err != nil { 71 return echo.NewHTTPError(404, "failed to parse handle or DID") 72 } 73 74 ident, err := srv.dir.Lookup(ctx, *atid) 75 if err != nil { 76 // TODO: proper error page? 77 return err 78 } 79 80 bdir := identity.BaseDirectory{} 81 doc, err := bdir.ResolveDID(ctx, ident.DID) 82 if nil == err { 83 b, err := json.MarshalIndent(doc, "", " ") 84 if err != nil { 85 return err 86 } 87 info["didDocJSON"] = string(b) 88 } 89 info["atid"] = atid 90 info["ident"] = ident 91 info["uri"] = atid 92 return c.Render(http.StatusOK, "account.html", info) 93} 94 95// e.GET("/at/:atid", srv.WebRepo) 96func (srv *Server) WebRepo(c echo.Context) error { 97 ctx := c.Request().Context() 98 //req := c.Request() 99 info := pongo2.Context{} 100 101 atid, err := syntax.ParseAtIdentifier(c.Param("atid")) 102 if err != nil { 103 return echo.NewHTTPError(400, "failed to parse handle or DID") 104 } 105 106 ident, err := srv.dir.Lookup(ctx, *atid) 107 if err != nil { 108 // TODO: proper error page? 109 return err 110 } 111 info["atid"] = atid 112 info["ident"] = ident 113 info["uri"] = fmt.Sprintf("at://%s", atid) 114 115 // create a new API client to connect to the account's PDS 116 xrpcc := xrpc.Client{ 117 Host: ident.PDSEndpoint(), 118 } 119 if xrpcc.Host == "" { 120 return fmt.Errorf("no PDS endpoint for identity") 121 } 122 123 desc, err := comatproto.RepoDescribeRepo(ctx, &xrpcc, ident.DID.String()) 124 if err != nil { 125 return err 126 } 127 info["collections"] = desc.Collections 128 129 return c.Render(http.StatusOK, "repo.html", info) 130} 131 132// e.GET("/at/:atid/:collection", srv.WebCollection) 133func (srv *Server) WebRepoCollection(c echo.Context) error { 134 ctx := c.Request().Context() 135 //req := c.Request() 136 info := pongo2.Context{} 137 138 atid, err := syntax.ParseAtIdentifier(c.Param("atid")) 139 if err != nil { 140 return echo.NewHTTPError(400, "failed to parse handle or DID") 141 } 142 143 collection, err := syntax.ParseNSID(c.Param("collection")) 144 if err != nil { 145 return echo.NewHTTPError(400, "failed to parse collection NSID") 146 } 147 148 ident, err := srv.dir.Lookup(ctx, *atid) 149 if err != nil { 150 // TODO: proper error page? 151 return err 152 } 153 info["atid"] = atid 154 info["ident"] = ident 155 info["collection"] = collection 156 info["uri"] = fmt.Sprintf("at://%s/%s", atid, collection) 157 158 // create a new API client to connect to the account's PDS 159 xrpcc := xrpc.Client{ 160 Host: ident.PDSEndpoint(), 161 } 162 if xrpcc.Host == "" { 163 return fmt.Errorf("no PDS endpoint for identity") 164 } 165 166 cursor := c.QueryParam("cursor") 167 // collection string, cursor string, limit int64, repo string, reverse bool 168 resp, err := agnostic.RepoListRecords(ctx, &xrpcc, collection.String(), cursor, 100, ident.DID.String(), false) 169 if err != nil { 170 return err 171 } 172 recordURIs := make([]syntax.ATURI, len(resp.Records)) 173 for i, rec := range resp.Records { 174 aturi, err := syntax.ParseATURI(rec.Uri) 175 if err != nil { 176 return err 177 } 178 recordURIs[i] = aturi 179 } 180 if resp.Cursor != nil && *resp.Cursor != "" { 181 cursor = *resp.Cursor 182 } 183 184 info["records"] = resp.Records 185 info["recordURIs"] = recordURIs 186 info["cursor"] = cursor 187 return c.Render(http.StatusOK, "repo_collection.html", info) 188} 189 190// e.GET("/at/:atid/:collection/:rkey", srv.WebRecord) 191func (srv *Server) WebRepoRecord(c echo.Context) error { 192 ctx := c.Request().Context() 193 //req := c.Request() 194 info := pongo2.Context{} 195 196 atid, err := syntax.ParseAtIdentifier(c.Param("atid")) 197 if err != nil { 198 return echo.NewHTTPError(400, "failed to parse handle or DID") 199 } 200 201 collection, err := syntax.ParseNSID(c.Param("collection")) 202 if err != nil { 203 return echo.NewHTTPError(400, "failed to parse collection NSID") 204 } 205 206 rkey, err := syntax.ParseRecordKey(c.Param("rkey")) 207 if err != nil { 208 return echo.NewHTTPError(400, "failed to parse record key") 209 } 210 211 ident, err := srv.dir.Lookup(ctx, *atid) 212 if err != nil { 213 // TODO: proper error page? 214 return err 215 } 216 info["atid"] = atid 217 info["ident"] = ident 218 info["collection"] = collection 219 info["rkey"] = rkey 220 info["uri"] = fmt.Sprintf("at://%s/%s/%s", atid, collection, rkey) 221 222 xrpcc := xrpc.Client{ 223 Host: ident.PDSEndpoint(), 224 } 225 resp, err := agnostic.RepoGetRecord(ctx, &xrpcc, "", collection.String(), ident.DID.String(), rkey.String()) 226 if err != nil { 227 return echo.NewHTTPError(400, fmt.Sprintf("failed to load record: %s", err)) 228 } 229 230 if nil == resp.Value { 231 return fmt.Errorf("empty record in response") 232 } 233 234 record, err := data.UnmarshalJSON(*resp.Value) 235 if err != nil { 236 return fmt.Errorf("fetched record was invalid data: %w", err) 237 } 238 info["record"] = record 239 240 b, err := json.MarshalIndent(record, "", " ") 241 if err != nil { 242 return err 243 } 244 info["recordJSON"] = string(b) 245 246 return c.Render(http.StatusOK, "repo_record.html", info) 247}