Lasa is a stateless proxy that generates a RSS or an Atom feed from a Standard.site publication. lasa.anhgelus.world
rss atom atprotocol standard-site atproto
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(cli): author page

+120 -4
+73
cmd/lasad/author.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>{{ .Author }}'s publications - Lasa</title> 7 + <style> 8 + :root { 9 + font-size: 18px; 10 + } 11 + body { 12 + margin: 0; 13 + padding-bottom: 4rem; 14 + font-family: system-ui, Inter, sans-serif; 15 + line-height: 1.6em; 16 + display: flex; 17 + flex-direction: column; 18 + gap: 1rem; 19 + } 20 + body > * { 21 + display: block; 22 + width: 100%; 23 + max-width: 850px; 24 + margin: 0 auto; 25 + } 26 + h1, h2, h3 { 27 + line-height: 1.6em; 28 + margin-top: 2rem; 29 + } 30 + h1 { 31 + text-align: center; 32 + } 33 + code { 34 + font-family: monospace; 35 + font-size: 0.9em; 36 + letter-spacing: -0.2px; 37 + } 38 + a { 39 + color: #023e8a; 40 + padding: 0.1em; 41 + } 42 + a:hover { 43 + background: #023e8a; 44 + color: white; 45 + } 46 + 47 + @media only screen and (prefers-color-scheme: dark) { 48 + :root { 49 + color: #f0ebd8; 50 + background-color: #0d1321; 51 + } 52 + a { 53 + color: #90e0ef; 54 + } 55 + a:hover { 56 + background: #90e0ef; 57 + color: black; 58 + } 59 + } 60 + </style> 61 + </head> 62 + <body> 63 + <h1>{{ .Author }}'s publications</h1> 64 + <h3>RSS</h3> 65 + <ul> 66 + {{- range .Publications }}<li><a href="{{ .Link }}/rss">{{ .Name }}</a></li>{{ end -}} 67 + </ul> 68 + <h3>Atom</h3> 69 + <ul> 70 + {{- range .Publications }}<li><a href="{{ .Link }}/atom">{{ .Name }}</a></li>{{ end -}} 71 + </ul> 72 + </body> 73 + </html>
+47 -4
cmd/lasad/run.go
··· 2 2 3 3 import ( 4 4 "context" 5 - _ "embed" 5 + "embed" 6 6 "errors" 7 7 "fmt" 8 + "html/template" 8 9 "log/slog" 9 10 "net" 10 11 "net/http" ··· 22 23 "tangled.org/anhgelus.world/xrpc/atproto" 23 24 ) 24 25 25 - //go:embed index.html 26 - var index []byte 26 + //go:embed index.html author.html 27 + var files embed.FS 27 28 28 29 func handleRunHelp() { 29 30 internal.Usage( ··· 38 39 if !help { 39 40 os.Exit(1) 40 41 } 42 + } 43 + 44 + type Publication struct { 45 + Link string 46 + Name string 41 47 } 42 48 43 49 func handleRun(args []string) { ··· 91 97 } 92 98 }) 93 99 mux.HandleFunc("GET /{id}/{$}", func(w http.ResponseWriter, r *http.Request) { 100 + ctx := r.Context() 101 + client := ctx.Value(keyClient).(xrpc.Client) 102 + did, err := lasa.Resolve(ctx, client.Directory(), r.PathValue("id")) 103 + if err != nil { 104 + w.WriteHeader(http.StatusBadRequest) 105 + return 106 + } 107 + doc, err := client.Directory().ResolveDID(ctx, did) 108 + if err != nil { 109 + panic(err) 110 + } 111 + h, _ := doc.Handle() 112 + v := struct { 113 + Author string 114 + Publications []Publication 115 + }{Author: h.String()} 116 + pubs, _, err := xrpc.ListRecords[*site.Publication](ctx, client, did, 0, "", false) 117 + if err != nil { 118 + panic(err) 119 + } 120 + v.Publications = make([]Publication, len(pubs)) 121 + for i, pub := range pubs { 122 + uri, err := pub.URI.URI(ctx, client.Directory()) 123 + if err != nil { 124 + panic(err) 125 + } 126 + link := fmt.Sprintf("/%s/%s", did, uri.RecordKey()) 127 + v.Publications[i] = Publication{link, pub.Value.Name} 128 + } 129 + err = template.Must(template.ParseFS(files, "author.html")).ExecuteTemplate(w, "author.html", v) 130 + if err != nil { 131 + panic(err) 132 + } 94 133 }) 95 134 mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { 96 - w.Write(index) 135 + b, err := files.ReadFile("index.html") 136 + if err != nil { 137 + panic(err) 138 + } 139 + w.Write(b) 97 140 }) 98 141 99 142 ch := make(chan error, 1)