html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app https://github.com/simonw/datasette/issues/647#issuecomment-1061282743,https://api.github.com/repos/simonw/datasette/issues/647,1061282743,IC_kwDOBm6k_c4_QeO3,9599,2022-03-08T00:32:34Z,2022-03-08T00:32:47Z,OWNER,It would be neat if the plugin could spot old-style hyphen hash URLs (maybe on 404) and redirect those too.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",531755959, https://github.com/simonw/datasette/issues/647#issuecomment-1061276646,https://api.github.com/repos/simonw/datasette/issues/647,1061276646,IC_kwDOBm6k_c4_Qcvm,9599,2022-03-08T00:22:11Z,2022-03-08T00:22:11Z,OWNER,I'm now convinced this is feasible enough that it's worth doing in time for Datasette 1.0.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",531755959, https://github.com/simonw/datasette/issues/647#issuecomment-1061276399,https://api.github.com/repos/simonw/datasette/issues/647,1061276399,IC_kwDOBm6k_c4_Qcrv,9599,2022-03-08T00:21:47Z,2022-03-08T00:21:47Z,OWNER,"This seems to do the job: ```python @hookimpl def startup(datasette): for name, database in datasette.databases.items(): if database.hash: new_name = ""{}_{}"".format(name, database.hash[:7]) del datasette.databases[name] datasette.databases[new_name] = database ``` Would have to teach the rest of the plugin to split on `_` and to only redirect if the user seems to be hitting the URL for an old hash after which Datasette has been restarted with an updated database.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",531755959, https://github.com/simonw/datasette/issues/647#issuecomment-1061272544,https://api.github.com/repos/simonw/datasette/issues/647,1061272544,IC_kwDOBm6k_c4_Qbvg,9599,2022-03-08T00:14:42Z,2022-03-08T00:14:42Z,OWNER,Maybe the plugin should interfere with `datasette.databases` on startup and change the registered name for each one?,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",531755959, https://github.com/simonw/datasette/issues/647#issuecomment-1061267615,https://api.github.com/repos/simonw/datasette/issues/647,1061267615,IC_kwDOBm6k_c4_Qaif,9599,2022-03-08T00:05:43Z,2022-03-08T00:05:43Z,OWNER,"Built a prototype of that plugin: ```python from datasette import hookimpl from functools import wraps @hookimpl def asgi_wrapper(datasette): def wrap_with_hashed_urls(app): @wraps(app) async def hashed_urls(scope, receive, send): # Only triggers on pages with a path not starting in /-/ # and where the first page component matches a database name if scope.get(""type"") != ""http"": await app(scope, receive, send) return path = scope[""path""].lstrip(""/"") if not path or path.startswith(""-/""): await app(scope, receive, send) return potential_database = path.split(""/"")[0] # It may or may not be already dbname~hash if ""~"" in potential_database: db_name, hash = potential_database.split(""~"", 1) else: db_name = potential_database hash = """" # Is db_name a database we have a hash for? try: db = datasette.get_database(db_name) except KeyError: await app(scope, receive, send) return if db.hash is not None: # TODO: make sure db.hash is documented if db.hash[:7] != hash: # Send a redirect path_bits = path.split(""/"") new_path = ""/"" + ""/"".join([""{}-{}"".format(db_name, db.hash[:7])] + path_bits[1:]) if scope.get(""query_string""): new_path += ""?"" + scope[""query_string""].decode(""latin-1"") await send({ ""type"": ""http.response.start"", ""status"": 302, ""headers"": [ [b""location"", new_path.encode(""latin1"")] ], }) await send({""type"": ""http.response.body"", ""body"": b""""}) return else: # Add a far-future cache header async def wrapped_send(event): if event[""type""] == ""http.response.start"": original_headers = event.get(""headers"") or [] event = { ""type"": event[""type""], ""status"": event[""status""], ""headers"": original_headers + [ [b""Cache-Control"", b""max-age=31536000""] ], } await send(event) await app(scope, receive, wrapped_send) return await app(scope, receive, send) return hashed_urls return wrap_with_hashed_urls ``` One catch: it doesn't affect the way URLs are generated - so every internal link within Datasette links to the non-hash version and then triggers a 302 redirect to the hashed version.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",531755959,