porting all github actions from bluesky-social/indigo to tangled CI
at main 6.7 kB view raw
1package main 2 3import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/atproto/identity" 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 12 "github.com/labstack/echo/v4" 13) 14 15// GET /xrpc/com.atproto.identity.resolveHandle 16func (srv *Server) ResolveHandle(c echo.Context) error { 17 ctx := c.Request().Context() 18 19 hdl, err := syntax.ParseHandle(c.QueryParam("handle")) 20 if err != nil { 21 return c.JSON(400, GenericError{ 22 Error: "InvalidHandleSyntax", 23 Message: err.Error(), 24 }) 25 } 26 27 did, err := srv.dir.ResolveHandle(ctx, hdl) 28 if err != nil && errors.Is(err, identity.ErrHandleNotFound) { 29 return c.JSON(404, GenericError{ 30 Error: "HandleNotFound", 31 Message: err.Error(), 32 }) 33 } else if err != nil { 34 return c.JSON(500, GenericError{ 35 Error: "InternalError", 36 Message: err.Error(), 37 }) 38 } 39 return c.JSON(200, comatproto.IdentityResolveHandle_Output{ 40 Did: did.String(), 41 }) 42} 43 44// GET /xrpc/com.atproto.identity.resolveDid 45func (srv *Server) ResolveDid(c echo.Context) error { 46 ctx := c.Request().Context() 47 48 did, err := syntax.ParseDID(c.QueryParam("did")) 49 if err != nil { 50 return c.JSON(400, GenericError{ 51 Error: "InvalidDidSyntax", 52 Message: err.Error(), 53 }) 54 } 55 56 rawDoc, err := srv.dir.ResolveDIDRaw(ctx, did) 57 if err != nil && errors.Is(err, identity.ErrDIDNotFound) { 58 return c.JSON(404, GenericError{ 59 Error: "DidNotFound", 60 Message: err.Error(), 61 }) 62 } else if err != nil { 63 return c.JSON(500, GenericError{ 64 Error: "InternalError", 65 Message: err.Error(), 66 }) 67 } 68 return c.JSON(200, comatproto.IdentityResolveDid_Output{ 69 DidDoc: rawDoc, 70 }) 71} 72 73// helper for resolveIdentity 74func (srv *Server) resolveIdentityFromHandle(c echo.Context, handle syntax.Handle) error { 75 ctx := c.Request().Context() 76 77 handle = handle.Normalize() 78 79 did, err := srv.dir.ResolveHandle(ctx, handle) 80 if err != nil && errors.Is(err, identity.ErrHandleNotFound) { 81 return c.JSON(404, GenericError{ 82 Error: "HandleNotFound", 83 Message: err.Error(), 84 }) 85 } else if err != nil { 86 srv.logger.Warn("failed handle resolution", "err", err, "handle", handle) 87 return c.JSON(502, GenericError{ 88 Error: "HandleResolutionFailed", 89 Message: err.Error(), 90 }) 91 } 92 93 rawDoc, err := srv.dir.ResolveDIDRaw(ctx, did) 94 if err != nil && errors.Is(err, identity.ErrDIDNotFound) { 95 return c.JSON(404, GenericError{ 96 Error: "DidNotFound", 97 Message: err.Error(), 98 }) 99 } else if err != nil { 100 return c.JSON(502, GenericError{ 101 Error: "DIDResolutionFailed", 102 Message: err.Error(), 103 }) 104 } 105 106 var doc identity.DIDDocument 107 if err := json.Unmarshal(rawDoc, &doc); err != nil { 108 return c.JSON(400, GenericError{ 109 Error: "InvalidDidDocument", 110 Message: err.Error(), 111 }) 112 } 113 114 ident := identity.ParseIdentity(&doc) 115 // NOTE: 'handle' was resolved above, and 'DeclaredHandle()' returns a normalized handle 116 declHandle, err := ident.DeclaredHandle() 117 if err != nil || declHandle != handle { 118 return c.JSON(400, GenericError{ 119 Error: "HandleMismatch", 120 Message: err.Error(), 121 }) 122 } 123 124 return c.JSON(200, comatproto.IdentityDefs_IdentityInfo{ 125 Did: ident.DID.String(), 126 Handle: handle.String(), 127 DidDoc: rawDoc, 128 }) 129} 130 131// helper for resolveIdentity 132func (srv *Server) resolveIdentityFromDID(c echo.Context, did syntax.DID) error { 133 ctx := c.Request().Context() 134 135 rawDoc, err := srv.dir.ResolveDIDRaw(ctx, did) 136 if err != nil && errors.Is(err, identity.ErrDIDNotFound) { 137 return c.JSON(404, GenericError{ 138 Error: "DidNotFound", 139 Message: err.Error(), 140 }) 141 } else if err != nil { 142 return c.JSON(502, GenericError{ 143 Error: "DIDResolutionFailed", 144 Message: err.Error(), 145 }) 146 } 147 148 var doc identity.DIDDocument 149 if err := json.Unmarshal(rawDoc, &doc); err != nil { 150 return c.JSON(400, GenericError{ 151 Error: "InvalidDidDocument", 152 Message: err.Error(), 153 }) 154 } 155 156 ident := identity.ParseIdentity(&doc) 157 handle, err := ident.DeclaredHandle() 158 if err != nil { 159 // no handle declared, or invalid syntax 160 handle = syntax.HandleInvalid 161 } 162 163 checkDID, err := srv.dir.ResolveHandle(ctx, handle) 164 if err != nil || checkDID != did { 165 handle = syntax.HandleInvalid 166 } 167 168 return c.JSON(200, comatproto.IdentityDefs_IdentityInfo{ 169 Did: ident.DID.String(), 170 Handle: handle.String(), 171 DidDoc: rawDoc, 172 }) 173} 174 175// GET /xrpc/com.atproto.identity.resolveIdentity 176func (srv *Server) ResolveIdentity(c echo.Context) error { 177 // we partially re-implement the "Lookup()" logic here, but returning the full DID document, not `identity.Identity` 178 atid, err := syntax.ParseAtIdentifier(c.QueryParam("identifier")) 179 if err != nil { 180 return c.JSON(400, GenericError{ 181 Error: "InvalidIdentifierSyntax", 182 Message: err.Error(), 183 }) 184 } 185 186 handle, err := atid.AsHandle() 187 if nil == err { 188 return srv.resolveIdentityFromHandle(c, handle) 189 } 190 did, err := atid.AsDID() 191 if nil == err { 192 return srv.resolveIdentityFromDID(c, did) 193 } 194 return fmt.Errorf("unreachable code path") 195} 196 197// POST /xrpc/com.atproto.identity.refreshIdentity 198func (srv *Server) RefreshIdentity(c echo.Context) error { 199 ctx := c.Request().Context() 200 201 var body comatproto.IdentityRefreshIdentity_Input 202 if err := c.Bind(&body); err != nil { 203 return c.JSON(400, GenericError{ 204 Error: "InvalidRequestBody", 205 Message: err.Error(), 206 }) 207 } 208 209 atid, err := syntax.ParseAtIdentifier(body.Identifier) 210 if err != nil { 211 return c.JSON(400, GenericError{ 212 Error: "InvalidIdentifierSyntax", 213 Message: err.Error(), 214 }) 215 } 216 217 did, err := atid.AsDID() 218 if nil == err { 219 if err := srv.dir.PurgeDID(ctx, did); err != nil { 220 return err 221 } 222 return srv.resolveIdentityFromDID(c, did) 223 } 224 handle, err := atid.AsHandle() 225 if nil == err { 226 if err := srv.dir.PurgeHandle(ctx, handle); err != nil { 227 return err 228 } 229 return srv.resolveIdentityFromHandle(c, handle) 230 } 231 232 return fmt.Errorf("unreachable code path") 233} 234 235type GenericStatus struct { 236 Daemon string `json:"daemon"` 237 Status string `json:"status"` 238 Message string `json:"msg,omitempty"` 239} 240 241func (s *Server) HandleHealthCheck(c echo.Context) error { 242 return c.JSON(200, GenericStatus{Status: "ok", Daemon: "bluepages"}) 243} 244 245func (srv *Server) WebHome(c echo.Context) error { 246 return c.String(200, ` 247eeeee e e e eeee eeeee eeeee eeeee eeee eeeee 2488 8 8 8 8 8 8 8 8 8 8 8 8 8 " 2498eee8e 8e 8e 8 8eee 8eee8 8eee8 8e 8eee 8eeee 25088 8 88 88 8 88 88 88 8 88 "8 88 88 25188eee8 88eee 88ee8 88ee 88 88 8 88ee8 88ee 8ee88 252 253This is an AT Protocol Identity Service 254 255Most API routes are under /xrpc/ 256 257 Code: https://github.com/bluesky-social/indigo/tree/main/cmd/bluepages 258 Protocol: https://atproto.com 259 `) 260 261}