···10 const firehose = new Firehose({})
1112 for await (const evt of firehose.run()) {
13- if (evt.event === 'create') {
014 const record = evt.record
0015 if (
16 evt.collection === 'com.example.status' &&
17 Status.isRecord(record) &&
18 Status.validateRecord(record).success
19 ) {
020 await this.db
21 .insertInto('status')
22 .values({
···25 updatedAt: record.updatedAt,
26 indexedAt: new Date().toISOString(),
27 })
28- .onConflict((oc) => oc.doNothing())
00000029 .execute()
30 }
31 }
···10 const firehose = new Firehose({})
1112 for await (const evt of firehose.run()) {
13+ // Watch for write events
14+ if (evt.event === 'create' || evt.event === 'update') {
15 const record = evt.record
16+17+ // If the write is a valid status update
18 if (
19 evt.collection === 'com.example.status' &&
20 Status.isRecord(record) &&
21 Status.validateRecord(record).success
22 ) {
23+ // Store the status in our SQLite
24 await this.db
25 .insertInto('status')
26 .values({
···29 updatedAt: record.updatedAt,
30 indexedAt: new Date().toISOString(),
31 })
32+ .onConflict((oc) =>
33+ oc.column('authorDid').doUpdateSet({
34+ status: record.status,
35+ updatedAt: record.updatedAt,
36+ indexedAt: new Date().toISOString(),
37+ })
38+ )
39 .execute()
40 }
41 }
+14-12
src/index.ts
···5import type { OAuthClient } from '@atproto/oauth-client-node'
67import { createDb, migrateToLatest } from '#/db'
8-import { env } from '#/env'
9import { Ingester } from '#/firehose/ingester'
10import { createRouter } from '#/routes'
11import { createClient } from '#/auth/client'
12import { createResolver, Resolver } from '#/firehose/resolver'
13import type { Database } from '#/db'
14015export type AppContext = {
16 db: Database
17 ingester: Ingester
···2930 static async create() {
31 const { NODE_ENV, HOST, PORT, DB_PATH } = env
32-33 const logger = pino({ name: 'server start' })
0034 const db = createDb(DB_PATH)
35 await migrateToLatest(db)
36- const ingester = new Ingester(db)
037 const oauthClient = await createClient(db)
038 const resolver = createResolver()
39- ingester.start()
40 const ctx = {
41 db,
42 ingester,
···45 resolver,
46 }
4748- const app: Express = express()
04950- // Set the application to trust the reverse proxy
051 app.set('trust proxy', true)
5253- // Middlewares
054 app.use(express.json())
55 app.use(express.urlencoded({ extended: true }))
56-57- // Routes
58- const router = createRouter(ctx)
59 app.use(router)
60-61- // Error handlers
62 app.use((_req, res) => res.sendStatus(404))
63064 const server = app.listen(env.PORT)
65 await events.once(server, 'listening')
66 logger.info(`Server (${NODE_ENV}) running on port http://${HOST}:${PORT}`)
···5import type { OAuthClient } from '@atproto/oauth-client-node'
67import { createDb, migrateToLatest } from '#/db'
8+import { env } from '#/lib/env'
9import { Ingester } from '#/firehose/ingester'
10import { createRouter } from '#/routes'
11import { createClient } from '#/auth/client'
12import { createResolver, Resolver } from '#/firehose/resolver'
13import type { Database } from '#/db'
1415+// Application state passed to the router and elsewhere
16export type AppContext = {
17 db: Database
18 ingester: Ingester
···3031 static async create() {
32 const { NODE_ENV, HOST, PORT, DB_PATH } = env
033 const logger = pino({ name: 'server start' })
34+35+ // Set up the SQLite database
36 const db = createDb(DB_PATH)
37 await migrateToLatest(db)
38+39+ // Create the atproto utilities
40 const oauthClient = await createClient(db)
41+ const ingester = new Ingester(db)
42 const resolver = createResolver()
043 const ctx = {
44 db,
45 ingester,
···48 resolver,
49 }
5051+ // Subscribe to events on the firehose
52+ ingester.start()
5354+ // Create our server
55+ const app: Express = express()
56 app.set('trust proxy', true)
5758+ // Routes & middlewares
59+ const router = createRouter(ctx)
60 app.use(express.json())
61 app.use(express.urlencoded({ extended: true }))
00062 app.use(router)
0063 app.use((_req, res) => res.sendStatus(404))
6465+ // Bind our server to the port
66 const server = app.listen(env.PORT)
67 await events.once(server, 'listening')
68 logger.info(`Server (${NODE_ENV}) running on port http://${HOST}:${PORT}`)
+1-2
src/pages/home.ts
···1-import { AtUri } from '@atproto/syntax'
2import type { Status } from '#/db/schema'
3-import { html } from '../view'
4import { shell } from './shell'
56const TODAY = new Date().toDateString()
···01import type { Status } from '#/db/schema'
2+import { html } from '../lib/view'
3import { shell } from './shell'
45const TODAY = new Date().toDateString()
+1-1
src/pages/login.ts
···1-import { html } from '../view'
2import { shell } from './shell'
34type Props = { error?: string }
···1+import { html } from '../lib/view'
2import { shell } from './shell'
34type Props = { error?: string }
+1-1
src/pages/shell.ts
···1-import { type Hole, html } from '../view'
23export function shell({ title, content }: { title: string; content: Hole }) {
4 return html`<html>
···1+import { type Hole, html } from '../lib/view'
23export function shell({ title, content }: { title: string; content: Hole }) {
4 return html`<html>