Skip to content

Commit

Permalink
api: add fastify (#94)
Browse files Browse the repository at this point in the history
* api: add `fastify`

* clean up

* address related PR review
  • Loading branch information
juliangruber authored Jan 28, 2025
1 parent b0cb123 commit 0e029fc
Show file tree
Hide file tree
Showing 8 changed files with 653 additions and 186 deletions.
24 changes: 10 additions & 14 deletions api/bin/server.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import '../lib/instrument.js'

import http from 'node:http'
import { once } from 'node:events'
import { createHandler } from '../lib/handler.js'
import { createApp } from '../lib/app.js'
import { RedisRepository } from '@filecoin-station/spark-piece-indexer-repository'
import { Redis } from 'ioredis'

Expand All @@ -27,15 +25,13 @@ const redis = new Redis({
await redis.connect()
const repository = new RedisRepository(redis)

const logger = {
error: console.error,
info: console.info,
request: ['1', 'true'].includes(requestLogging) ? console.info : () => {}
}

const handler = createHandler({ repository, domain, logger })
const server = http.createServer(handler)
const app = createApp({
repository,
domain,
logger: {
level: ['1', 'true'].includes(requestLogging) ? 'info' : 'error'
}
})
console.log('Starting the http server on host %j port %s', HOST, PORT)
server.listen(Number(PORT), HOST)
await once(server, 'listening')
console.log(`http://${HOST}:${server.address().port}`)
const baseUrl = await app.listen({ host: HOST, port: Number(PORT) })
console.log(baseUrl)
62 changes: 62 additions & 0 deletions api/lib/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as Sentry from '@sentry/node'
import Fastify from 'fastify'

/**
* @param {object} args
* @param {Repository} args.repository
* @param {string|boolean} args.domain
* @param {Fastify.FastifyLoggerOptions} args.logger
*/
export function createApp ({ repository, domain, logger }) {
const app = Fastify({ logger })
Sentry.setupFastifyErrorHandler(app)

if (typeof domain === 'string') {
app.addHook('onRequest', async (request, reply) => {
if (request.headers.host.split(':')[0] !== domain) {
reply.redirect(`https://${domain}${request.url}`, 301)
}
})
}

app.get('/sample/:providerId/:pieceCid', async (request, reply) => {
const { providerId, pieceCid } = request.params
const payloadCids = await repository.getPiecePayloadBlocks(providerId, pieceCid)
const body = {}
if (payloadCids.length) {
body.samples = payloadCids.slice(0, 1)
reply.header('cache-control', `public, max-age=${24 * 3600 /* 24 hours */}, immutable`)
} else {
body.error = 'PROVIDER_OR_PIECE_NOT_FOUND'
reply.header('cache-control', `public, max-age=${60 /* 1min */}`)
}
reply.send(body)
})

app.get('/ingestion-status/:providerId', async (request, reply) => {
const { providerId } = request.params
const walkerState = await repository.getWalkerState(providerId)
reply.header('cache-control', `public, max-age=${60 /* 1min */}`)

if (!walkerState) {
return reply.send({
providerId,
ingestionStatus: 'Unknown provider ID'
})
}

return reply.send({
providerId,
// Discussion point:
// We don't have providerAddress in the walker state.
// Is it a problem if our observability API does not tell the provider address?
ingestionStatus: walkerState.status,
lastHeadWalkedFrom: walkerState.lastHead ?? walkerState.head,
adsMissingPieceCID: walkerState.adsMissingPieceCID ?? 0,
entriesNotRetrievable: walkerState.entriesNotRetrievable ?? 0,
piecesIndexed: await repository.countPiecesIndexed(providerId)
})
})

return app
}
124 changes: 0 additions & 124 deletions api/lib/handler.js

This file was deleted.

6 changes: 0 additions & 6 deletions api/lib/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
export { RedisRepository as Repository } from '@filecoin-station/spark-piece-indexer-repository'

export interface Logger {
info: typeof console.info;
error: typeof console.error;
request: typeof console.info;
}
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"@filecoin-station/spark-piece-indexer-repository": "^1.0.0",
"@sentry/node": "^8.51.0",
"http-responders": "^2.2.0",
"fastify": "^5.2.1",
"ioredis": "^5.4.2"
}
}
32 changes: 11 additions & 21 deletions api/test/handler.test.js → api/test/app.test.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,43 @@
import { RedisRepository } from '@filecoin-station/spark-piece-indexer-repository'
import createDebug from 'debug'
import { Redis } from 'ioredis'
import assert from 'node:assert'
import { once } from 'node:events'
import http from 'node:http'
import { after, before, beforeEach, describe, it } from 'node:test'
import { createHandler } from '../lib/handler.js'
import { assertResponseStatus, getPort } from './test-helpers.js'

const debug = createDebug('test')
import { createApp } from '../lib/app.js'
import { assertResponseStatus } from './test-helpers.js'

describe('HTTP request handler', () => {
/** @type {Redis} */
let redis
/** @type {RedisRepository} */
let repository

/** @type {http.Server} */
let server
/** @type {import('fastify').FastifyInstance} */
let app
/** @type {string} */
let baseUrl

before(async () => {
redis = new Redis({ db: 1 })
repository = new RedisRepository(redis)

const handler = createHandler({
app = createApp({
repository,
domain: '127.0.0.1',
domain: false,
logger: {
info: debug,
error: console.error,
request: debug
level: process.env.DEBUG === '*' || process.env.DEBUG?.includes('test')
? 'debug'
: 'error'
}
})

server = http.createServer(handler)
server.listen()
await once(server, 'listening')
baseUrl = `http://127.0.0.1:${getPort(server)}`
baseUrl = await app.listen()
})

beforeEach(async () => {
await redis.flushall()
})

after(async () => {
server.closeAllConnections()
server.close()
await app.close()
await redis?.disconnect()
})

Expand Down
11 changes: 1 addition & 10 deletions api/test/test-helpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert, { AssertionError } from 'node:assert'
import { AssertionError } from 'node:assert'

export const assertResponseStatus = async (res, status) => {
if (res.status !== status) {
Expand All @@ -9,12 +9,3 @@ export const assertResponseStatus = async (res, status) => {
})
}
}

/**
* @param {import('http').Server} server
*/
export const getPort = (server) => {
const address = server.address()
assert(typeof address === 'object')
return address.port
}
Loading

0 comments on commit 0e029fc

Please sign in to comment.