tangled
alpha
login
or
join now
ligo.at
/
core
6
fork
atom
decentralized and customizable links page on top of atproto
ligo.at
atproto
link-in-bio
python
uv
6
fork
atom
overview
issues
2
pulls
pipelines
cache profile and links when updating them
nauta.one
5 months ago
806fa876
dfc84f50
+48
-30
1 changed file
expand all
collapse all
unified
split
src
main.py
+48
-30
src/main.py
···
120
121
async with ClientSession() as client:
122
(profile, from_bluesky), links = await asyncio.gather(
123
-
load_profile(client, pds, did, reload=True),
124
-
load_links(client, pds, did, reload=True),
125
)
126
127
return render_template(
···
140
return redirect("/login", 303)
141
142
display_name = request.form.get("displayName")
143
-
description = request.form.get("description") or ""
144
if not display_name:
145
return redirect("/editor", 303)
146
147
-
await put_record(
0
0
0
0
0
0
148
user=user,
149
pds=user.pds_url,
150
repo=user.did,
151
collection=f"{SCHEMA}.actor.profile",
152
rkey="self",
153
-
record={
154
-
"$type": f"{SCHEMA}.actor.profile",
155
-
"displayName": display_name,
156
-
"description": description,
157
-
},
158
)
159
160
-
return redirect("/editor", 303)
0
0
0
0
161
162
163
@app.post("/editor/links")
···
183
link["subtitle"] = subtitle
184
links.append(link)
185
186
-
await put_record(
0
0
0
0
0
187
user=user,
188
pds=user.pds_url,
189
repo=user.did,
190
collection=f"{SCHEMA}.actor.links",
191
rkey="self",
192
-
record={
193
-
"$type": f"{SCHEMA}.actor.links",
194
-
"links": links,
195
-
},
196
)
197
198
-
return redirect("/editor", 303)
0
0
0
0
199
200
201
@app.get("/terms")
···
210
reload: bool = False,
211
) -> list[dict[str, str]] | None:
212
kv = KV(app, app.logger, "links_from_did")
213
-
recordstr = kv.get(did)
214
215
-
if recordstr is not None and not reload:
216
-
return json.loads(recordstr)["links"]
217
218
record = await get_record(client, pds, did, f"{SCHEMA}.actor.links", "self")
219
if record is None:
···
231
reload: bool = False,
232
) -> tuple[dict[str, str] | None, bool]:
233
kv = KV(app, app.logger, "profile_from_did")
234
-
recordstr = kv.get(did)
0
0
0
235
236
-
if recordstr is not None and not reload:
237
-
return json.loads(recordstr), False
0
0
238
239
from_bluesky = False
240
-
record = await get_record(client, pds, did, f"{SCHEMA}.actor.profile", "self")
241
if record is None and fallback_with_bluesky:
242
-
record = await get_record(client, pds, did, "app.bsky.actor.profile", "self")
243
from_bluesky = True
244
-
if record is None:
245
-
return None, False
0
246
247
-
kv.set(did, value=json.dumps(record))
248
return record, from_bluesky
249
250
···
256
collection: str,
257
rkey: str,
258
record: dict[str, Any],
259
-
):
0
0
260
endpoint = f"{pds}/xrpc/com.atproto.repo.putRecord"
261
body = {
262
"repo": repo,
···
276
user=user,
277
update_dpop_pds_nonce=update_dpop_pds_nonce,
278
)
279
-
if not response or not response.ok:
280
-
app.logger.warning("PDS HTTP ERROR")
281
282
283
def _is_did_blocked(did: str) -> bool:
···
120
121
async with ClientSession() as client:
122
(profile, from_bluesky), links = await asyncio.gather(
123
+
load_profile(client, pds, did),
124
+
load_links(client, pds, did),
125
)
126
127
return render_template(
···
140
return redirect("/login", 303)
141
142
display_name = request.form.get("displayName")
143
+
description = request.form.get("description", "")
144
if not display_name:
145
return redirect("/editor", 303)
146
147
+
record = {
148
+
"$type": f"{SCHEMA}.actor.profile",
149
+
"displayName": display_name,
150
+
"description": description,
151
+
}
152
+
153
+
success = await put_record(
154
user=user,
155
pds=user.pds_url,
156
repo=user.did,
157
collection=f"{SCHEMA}.actor.profile",
158
rkey="self",
159
+
record=record,
0
0
0
0
160
)
161
162
+
if success:
163
+
kv = KV(app, app.logger, "profile_from_did")
164
+
kv.set(user.did, json.dumps(record))
165
+
166
+
return redirect(url_for("page_editor"), 303)
167
168
169
@app.post("/editor/links")
···
189
link["subtitle"] = subtitle
190
links.append(link)
191
192
+
record = {
193
+
"$type": f"{SCHEMA}.actor.links",
194
+
"links": links,
195
+
}
196
+
197
+
success = await put_record(
198
user=user,
199
pds=user.pds_url,
200
repo=user.did,
201
collection=f"{SCHEMA}.actor.links",
202
rkey="self",
203
+
record=record,
0
0
0
204
)
205
206
+
if success:
207
+
kv = KV(app, app.logger, "links_from_did")
208
+
kv.set(user.did, json.dumps(record))
209
+
210
+
return redirect(url_for("page_editor"), 303)
211
212
213
@app.get("/terms")
···
222
reload: bool = False,
223
) -> list[dict[str, str]] | None:
224
kv = KV(app, app.logger, "links_from_did")
225
+
record_json = kv.get(did)
226
227
+
if record_json is not None and not reload:
228
+
return json.loads(record_json)["links"]
229
230
record = await get_record(client, pds, did, f"{SCHEMA}.actor.links", "self")
231
if record is None:
···
243
reload: bool = False,
244
) -> tuple[dict[str, str] | None, bool]:
245
kv = KV(app, app.logger, "profile_from_did")
246
+
record_json = kv.get(did)
247
+
248
+
if record_json is not None and not reload:
249
+
return json.loads(record_json), False
250
251
+
(record, bsky_record) = await asyncio.gather(
252
+
get_record(client, pds, did, f"{SCHEMA}.actor.profile", "self"),
253
+
get_record(client, pds, did, "app.bsky.actor.profile", "self"),
254
+
)
255
256
from_bluesky = False
0
257
if record is None and fallback_with_bluesky:
258
+
record = bsky_record
259
from_bluesky = True
260
+
261
+
if record is not None:
262
+
kv.set(did, value=json.dumps(record))
263
0
264
return record, from_bluesky
265
266
···
272
collection: str,
273
rkey: str,
274
record: dict[str, Any],
275
+
) -> bool:
276
+
"""Writes the record onto the users PDS. Returns bool for success."""
277
+
278
endpoint = f"{pds}/xrpc/com.atproto.repo.putRecord"
279
body = {
280
"repo": repo,
···
294
user=user,
295
update_dpop_pds_nonce=update_dpop_pds_nonce,
296
)
297
+
298
+
return response is not None and response.ok
299
300
301
def _is_did_blocked(did: str) -> bool: