An HTML-only Bluesky frontend
1package main
2
3import (
4 "embed"
5 "encoding/json"
6 "fmt"
7 "io/fs"
8 "log"
9 "net/http"
10
11 "git.sr.ht/~jordanreger/bsky"
12 "git.sr.ht/~jordanreger/bsky/util"
13)
14
15var host = "htmlsky.app"
16var handle = "jordanreger.com"
17var did = "did:plc:27rjcwbur2bizjjx3zakeme5"
18
19//go:embed all:public
20var publicFiles embed.FS
21var publicFS = fs.FS(publicFiles)
22var public, _ = fs.Sub(publicFS, "public")
23
24func main() {
25 mux := http.NewServeMux()
26
27 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
28 if r.URL.Path != "/" {
29 // serve DID
30 if r.URL.Path == "/.well-known/atproto-did" {
31 fmt.Fprint(w, did)
32 return
33 }
34 // otherwise serve static
35 http.ServeFileFS(w, r, public, r.URL.Path)
36 return
37 }
38
39 // serve homepage
40 did := util.GetDID(handle)
41 actor := bsky.GetActorProfile(did)
42 page := GetActorPage(actor)
43
44 fmt.Fprint(w, page)
45 })
46
47 /* REDIRECTS */
48 mux.HandleFunc("/raw/", func(w http.ResponseWriter, r *http.Request) {
49 fmt.Fprint(w, "Usage: /raw/profile/{handle}[/post/{rkey}]")
50 })
51 mux.HandleFunc("/raw/{handle}/", func(w http.ResponseWriter, r *http.Request) {
52 http.Redirect(w, r, "/raw/", http.StatusSeeOther)
53 })
54 mux.HandleFunc("/raw/profile/{handle}/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
55 http.Redirect(w, r, "/raw/", http.StatusSeeOther)
56 })
57 mux.HandleFunc("/embed/", func(w http.ResponseWriter, r *http.Request) {
58 fmt.Fprint(w, "Usage: /embed/profile/{handle}[/post/{rkey}]")
59 })
60 mux.HandleFunc("/embed/{handle}/", func(w http.ResponseWriter, r *http.Request) {
61 http.Redirect(w, r, "/embed/", http.StatusSeeOther)
62 })
63 mux.HandleFunc("/profile/", func(w http.ResponseWriter, r *http.Request) {
64 http.Redirect(w, r, "/", http.StatusSeeOther)
65 })
66 mux.HandleFunc("/profile/{handle}/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
67 http.Redirect(w, r, "/", http.StatusSeeOther)
68 })
69
70 /* ROUTES */
71
72 // actor
73 mux.HandleFunc("/profile/{handle}/", func(w http.ResponseWriter, r *http.Request) {
74 handle := r.PathValue("handle")
75
76 did := util.GetDID(handle)
77 actor := bsky.GetActorProfile(did)
78 page := GetActorPage(actor)
79
80 fmt.Fprint(w, page)
81 })
82 mux.HandleFunc("/raw/profile/{handle}/", func(w http.ResponseWriter, r *http.Request) {
83 handle := r.PathValue("handle")
84
85 did := util.GetDID(handle)
86 actor := bsky.GetActorProfile(did)
87 type raw struct {
88 *bsky.Actor
89 *bsky.Feed `json:"feed,omitempty"`
90 }
91 feed := actor.Feed()
92 actor_feed := raw{
93 &actor,
94 &feed,
95 }
96 res, _ := json.MarshalIndent(actor_feed, "", " ")
97
98 w.Header().Add("Content-Type", "application/json")
99 fmt.Fprint(w, string(res))
100 })
101 mux.HandleFunc("/embed/profile/{handle}/", func(w http.ResponseWriter, r *http.Request) {
102 handle := r.PathValue("handle")
103
104 did := util.GetDID(handle)
105 actor := bsky.GetActorProfile(did)
106 page := GetActorPageEmbed(actor)
107
108 fmt.Fprint(w, page)
109 })
110
111 // thread
112 mux.HandleFunc("/profile/{handle}/post/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
113 handle := r.PathValue("handle")
114 rkey := r.PathValue("rkey")
115
116 did := util.GetDID(handle)
117 at_uri := util.GetPostURI(did, rkey)
118 thread := bsky.GetThread(at_uri)
119 page := GetThreadPage(thread)
120
121 fmt.Fprint(w, page)
122 })
123
124 mux.HandleFunc("/raw/profile/{handle}/post/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
125 handle := r.PathValue("handle")
126 rkey := r.PathValue("rkey")
127
128 did := util.GetDID(handle)
129 at_uri := util.GetPostURI(did, rkey)
130 res, _ := json.MarshalIndent(bsky.GetThread(at_uri), "", " ")
131
132 w.Header().Add("Content-Type", "application/json")
133 fmt.Fprint(w, string(res))
134 })
135
136 mux.HandleFunc("/embed/profile/{handle}/post/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
137 handle := r.PathValue("handle")
138 rkey := r.PathValue("rkey")
139
140 did := util.GetDID(handle)
141 at_uri := util.GetPostURI(did, rkey)
142 thread := bsky.GetThread(at_uri)
143 page := GetThreadPageEmbed(thread)
144
145 fmt.Fprint(w, page)
146 })
147
148 // list
149 mux.HandleFunc("/profile/{handle}/lists/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
150 handle := r.PathValue("handle")
151 rkey := r.PathValue("rkey")
152
153 did := util.GetDID(handle)
154 at_uri := util.GetListURI(did, rkey)
155 list := bsky.GetList(at_uri)
156 page := GetListPage(list)
157
158 fmt.Fprint(w, page)
159 })
160 mux.HandleFunc("/raw/profile/{handle}/lists/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
161 handle := r.PathValue("handle")
162 rkey := r.PathValue("rkey")
163
164 did := util.GetDID(handle)
165 at_uri := util.GetListURI(did, rkey)
166 list := bsky.GetList(at_uri)
167 type raw struct {
168 *bsky.List
169 *bsky.Feed `json:"feed,omitempty"`
170 }
171 feed := list.Feed()
172 actor_feed := raw{
173 &list,
174 &feed,
175 }
176 res, _ := json.MarshalIndent(actor_feed, "", " ")
177
178 w.Header().Add("Content-Type", "application/json")
179 fmt.Fprint(w, string(res))
180
181 })
182 mux.HandleFunc("/embed/profile/{handle}/lists/{rkey}/", func(w http.ResponseWriter, r *http.Request) {
183 handle := r.PathValue("handle")
184 rkey := r.PathValue("rkey")
185
186 did := util.GetDID(handle)
187 at_uri := util.GetListURI(did, rkey)
188 list := bsky.GetList(at_uri)
189 page := GetListPageEmbed(list)
190
191 fmt.Fprint(w, page)
192 })
193
194 log.Fatal(http.ListenAndServe(":8080", mux))
195}