diff --git a/packages/xrpc/src/xrpc/__init__.py b/packages/xrpc/src/xrpc/__init__.py index 1dfd94b..4d18ca5 100644 --- a/packages/xrpc/src/xrpc/__init__.py +++ b/packages/xrpc/src/xrpc/__init__.py @@ -1,26 +1,35 @@ from urllib.parse import urljoin from aiohttp.client import ClientSession -from flask import Blueprint, request +from flask import Blueprint, current_app, request xrpc = Blueprint("xrpc", __name__, url_prefix="/xrpc") @xrpc.get("/app.bsky.actor.searchActorsTypeahead") async def search_actors_typeahead(): - base = "https://public.api.bsky.app" + client_header = request.headers.get("X-Client") + if not client_header: + return ("missing X-Client header", 400) + base = "https://typeahead.waow.tech" url = urljoin(base, "xrpc/app.bsky.actor.searchActorsTypeahead") url += f"?{request.query_string.decode('utf-8')}" async with ClientSession() as client: - async with client.get(url) as response: - json = await response.json() + if not current_app.debug: + client.headers.add("X-Client", client_header) + async with client.get(url) as typeahead_response: + response = await typeahead_response.json() + status = 200 try: - res = [ - {"avatar": actor["avatar"], "handle": actor["handle"]} - for actor in json["actors"] + actors = [ + {"avatar": actor.get("avatar"), "handle": actor["handle"]} + for actor in response["actors"] ] - return { - "actors": res, + response = { + "actors": actors, } - except KeyError: - return json + except (KeyError, TypeError) as error: + current_app.logger.error(error) + response = {"actors": []} + status = 500 + return (response, status) diff --git a/src/static/actor-typeahead.js b/src/static/actor-typeahead.js index 6f56ac8..39158c2 100644 --- a/src/static/actor-typeahead.js +++ b/src/static/actor-typeahead.js @@ -6,6 +6,8 @@ * Copyright (c) 2026 ligo.at contributors * * Changes + * 2026-03-19 + * New: Attribute `client` to send via the `X-Client` HTTP header. * 2026-03-17 * Fix: Abort previous HTTP request before starting the next. * New: Add 250ms debounce to oninput. @@ -127,6 +129,7 @@ function clone(tmpl) { /** * @attribute {string} [host] - The host to which to make the typeahead API call. If set to "location" it uses window.location. + * @attribute {string} [client] - The optional value to send in the X-Client HTTP header. * @attribute {number} [rows] - The maximum number of rows to display in the dropdown. * * @csspart menu - The dropdown menu. @@ -327,8 +330,17 @@ export default class ActorTypeahead extends HTMLElement { this.#controller?.abort(); this.#controller = new AbortController(); + const headers = {}; + const client = this.getAttribute("client"); + if (client) { + headers["X-Client"] = client; + } + try { - const res = await fetch(url, { signal: this.#controller.signal }); + const res = await fetch(url, { + headers, + signal: this.#controller.signal, + }); const json = await res.json(); this.#actors = json.actors; this.#index = -1; diff --git a/src/templates/login.html b/src/templates/login.html index a2ca015..244a950 100644 --- a/src/templates/login.html +++ b/src/templates/login.html @@ -21,7 +21,7 @@