diff --git a/modules/shared/tid.ts b/modules/shared/tid.ts index 7a00070f..f239752f 100644 --- a/modules/shared/tid.ts +++ b/modules/shared/tid.ts @@ -17,3 +17,6 @@ export function tidFromDate (date: string | Date): string { const timestamp = (typeof date === 'string' ? new Date(date) : date).getTime() * MS_TO_MICROSECONDS return TID.fromTime(timestamp, CLOCK_ID).toString() } + +/** Stable TID for the site's publication record, derived from the first commit date. */ +export const publicationRkey = tidFromDate('2019-12-10T20:18:52.000Z') diff --git a/modules/standard-site/index.ts b/modules/standard-site/index.ts index c0bb8687..bbcac819 100644 --- a/modules/standard-site/index.ts +++ b/modules/standard-site/index.ts @@ -1,6 +1,8 @@ import { addServerHandler, addTemplate, addTypeTemplate, createResolver, defineNuxtModule, useNuxt } from 'nuxt/kit' import { AtpAgent } from '@atproto/api' +import { publicationRkey } from '../shared/tid' + export default defineNuxtModule({ meta: { name: 'standard-site', @@ -16,6 +18,7 @@ export default defineNuxtModule({ getContents: () => ` declare module '#standard-site-did.json' { export const standardSiteDid: string | null + export const standardSitePublicationRkey: string } `, }, { nitro: true }) @@ -29,6 +32,8 @@ declare module '#standard-site-did.json' { getContents: () => ` /** @type {string | null} */ export const standardSiteDid = null +/** @type {string} */ +export const standardSitePublicationRkey = ${JSON.stringify(publicationRkey)} `, write: true, }) @@ -53,6 +58,8 @@ export const standardSiteDid = null getContents: () => ` /** @type {string | null} */ export const standardSiteDid = ${JSON.stringify(did)} +/** @type {string} */ +export const standardSitePublicationRkey = ${JSON.stringify(publicationRkey)} `, write: true, }) @@ -60,7 +67,7 @@ export const standardSiteDid = ${JSON.stringify(did)} // Server virtual module for .well-known route nuxt.options.nitro.virtual ||= {} nuxt.options.nitro.virtual['#standard-site-did.json'] = () => - `export const standardSiteDid = ${JSON.stringify(did)}` + `export const standardSiteDid = ${JSON.stringify(did)}\nexport const standardSitePublicationRkey = ${JSON.stringify(publicationRkey)}` nuxt.options.nitro.externals ||= {} nuxt.options.nitro.externals.inline ||= [] diff --git a/modules/standard-site/runtime/server/routes/well-known.get.ts b/modules/standard-site/runtime/server/routes/well-known.get.ts index a288b116..a425ee38 100644 --- a/modules/standard-site/runtime/server/routes/well-known.get.ts +++ b/modules/standard-site/runtime/server/routes/well-known.get.ts @@ -1,4 +1,4 @@ -import { standardSiteDid } from '#standard-site-did.json' +import { standardSiteDid, standardSitePublicationRkey } from '#standard-site-did.json' export default defineEventHandler(event => { if (!standardSiteDid) { @@ -6,5 +6,5 @@ export default defineEventHandler(event => { } setResponseHeader(event, 'content-type', 'text/plain') - return `at://${standardSiteDid}/site.standard.publication/self` + return `at://${standardSiteDid}/site.standard.publication/${standardSitePublicationRkey}` }) diff --git a/modules/sync/providers/standard-site.ts b/modules/sync/providers/standard-site.ts index 444b5599..b9d5825d 100644 --- a/modules/sync/providers/standard-site.ts +++ b/modules/sync/providers/standard-site.ts @@ -1,7 +1,7 @@ import { AtpAgent } from '@atproto/api' import type { SyncItem, SyncOptions, SyncProvider } from './index' -import { tidFromDate } from '../../shared/tid' +import { publicationRkey, tidFromDate } from '../../shared/tid' export class StandardSiteProvider implements SyncProvider { name = 'standard-site' @@ -35,7 +35,7 @@ export class StandardSiteProvider implements SyncProvider { await agent.com.atproto.repo.putRecord({ repo: did, collection: 'site.standard.publication', - rkey: 'self', + rkey: publicationRkey, record: { $type: 'site.standard.publication', url: 'https://roe.dev', @@ -45,6 +45,19 @@ export class StandardSiteProvider implements SyncProvider { }, }) + // Delete legacy 'self' rkey publication record if it exists + try { + await agent.com.atproto.repo.deleteRecord({ + repo: did, + collection: 'site.standard.publication', + rkey: 'self', + }) + console.info('[sync:standard-site] Deleted legacy publication record with rkey: self') + } + catch { + // Record may not exist, that's fine + } + // Delete any existing records with non-TID rkeys (slug-based) const tidPattern = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/ try { @@ -78,10 +91,11 @@ export class StandardSiteProvider implements SyncProvider { const record: Record = { $type: 'site.standard.document', - site: `at://${did}/site.standard.publication/self`, + site: `at://${did}/site.standard.publication/${publicationRkey}`, path: `/blog/${slug}`, title: item.title, publishedAt: new Date(item.date).toISOString(), + updatedAt: new Date().toISOString(), } if (item.description) record.description = item.description