Repository: cloudflare/wildebeest
File: frontend/mock-db/init.ts (TypeScript)

Code before:

/**
 * Run helper commands to initialize the database with actors, statuses, etc.
 */
export async function init(domain: string, db: D1Database) {
	const loadedStatuses: MastodonStatus[] = []
	for (const status of statuses) {
		const actor = await getOrCreatePerson(domain, db, status.account)
		loadedStatuses.push(await createStatus(db, actor, status.content))
	}

	// Grab the account from an arbitrary status to use as the reblogger
	const rebloggerAccount = loadedStatuses[1].account
	const reblogger = await getOrCreatePerson(domain, db, rebloggerAccount)
	// Reblog an arbitrary status with this reblogger
	const statusToReblog = loadedStatuses[2]
	await reblogStatus(db, reblogger, statusToReblog, domain)
}

/**
 * Create a status object in the given actors outbox.
 */
async function createStatus(db: D1Database, actor: Person, status: string, visibility = 'public') {
	const body = {
		status,
		visibility,
	}
	const headers = {
		'content-type': 'application/json',
	}
	const req = new Request('https://example.com', {
		method: 'POST',
		headers,
		body: JSON.stringify(body),
	})
	const resp = await statusesAPI.handleRequest(req, db, actor, kek, queue, cache)
	return (await resp.json()) as MastodonStatus
}

async function getOrCreatePerson(
	domain: string,
	db: D1Database,
	{ username, avatar, display_name }: Account
): Promise<Person> {
	const person = await getPersonByEmail(db, username)
	if (person) return person
	const newPerson = await createPerson(domain, db, kek, username, {
		icon: { url: avatar },
		name: display_name,
	})
	if (!newPerson) {

Review comment (refactor):
I like the idea of avoiding the full external REST API here, especially given that we don't care about triggering notifications.

Could we move this to somewhere in the `backend/src/` project and reuse it here and in `functions/api/v1/statuses/[id]/reblog.ts`?

And similarly with other functions that are making assumptions about how the internals of the request handlers work?

Code after:
	}

	const { reblogger, noteToReblog } = await pickReblogDetails(loadedStatuses, domain, db)
	reblogNote(db, reblogger, noteToReblog)

	for (const reply of replies) {
		await createReply(domain, db, reply, loadedStatuses)
	}
}

/**
 * Create a status object in the given actor's outbox.
 */
async function createStatus(domain: string, db: D1Database, actor: Person, content: string) {
	const note = await createPublicNote(domain, db, content, actor)
	await addObjectInOutbox(db, actor, note)
	return note
}

/**
 * Reblogs a note (representing a status)
 */
async function reblogNote(db: D1Database, reblogger: Person, noteToReblog: Note) {
	await addObjectInOutbox(db, reblogger, noteToReblog)
	await insertReblog(db, reblogger, noteToReblog)
}

/**
 * Creates a reply for a note (representing a status)
 */
async function createReply(
	domain: string,
	db: D1Database,
	reply: MastodonStatus,
	loadedStatuses: { status: MastodonStatus; note: Note }[]
) {
	if (!reply.in_reply_to_id) {
		console.warn(`Ignoring reply with id ${reply.id} since it doesn't have a in_reply_to_id field`)
		return
	}

	const originalStatus = loadedStatuses.find(({ status: { id } }) => id === reply.in_reply_to_id)
	if (!originalStatus) {
		console.warn(`Ignoring reply since no status matching the in_reply_to_id ${reply.id} has been found`)
		return
	}

	const inReplyTo = originalStatus.note.mastodonId
	const actor = await getOrCreatePerson(domain, db, reply.account)
	const replyNote = await createPublicNote(domain, db, reply.content, actor, [], { inReplyTo })
	await insertReply(db, actor, replyNote, originalStatus.note)