91 lines
2.3 KiB
TypeScript
91 lines
2.3 KiB
TypeScript
|
|
import Fastify from "fastify"
|
||
|
|
|
||
|
|
import type { GatewayConfig } from "./config.js"
|
||
|
|
import { proxyToUpstream } from "./proxy.js"
|
||
|
|
|
||
|
|
export function createGatewayServer(config: GatewayConfig) {
|
||
|
|
const app = Fastify({
|
||
|
|
logger: true,
|
||
|
|
})
|
||
|
|
|
||
|
|
app.get("/health", async () => ({
|
||
|
|
service: "image-platform-gateway",
|
||
|
|
status: "ok",
|
||
|
|
}))
|
||
|
|
|
||
|
|
app.get<{ Params: ImageParams; Querystring: ImageQuery }>(
|
||
|
|
"/images/:assetId/:version/:preset",
|
||
|
|
async (request, reply) => {
|
||
|
|
const version = parseVersionParam(request.params.version)
|
||
|
|
|
||
|
|
if (version === null) {
|
||
|
|
return reply.code(400).send({
|
||
|
|
message: "image version must use v{number} format",
|
||
|
|
statusCode: 400,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
const width = parseOptionalInteger(request.query.w)
|
||
|
|
const quality = parseOptionalInteger(request.query.q)
|
||
|
|
const format = request.query.f ?? "auto"
|
||
|
|
|
||
|
|
return reply.code(501).header("cache-control", "no-store").send({
|
||
|
|
assetId: request.params.assetId,
|
||
|
|
format,
|
||
|
|
message: "image gateway read-through pipeline is not implemented yet",
|
||
|
|
preset: request.params.preset,
|
||
|
|
quality,
|
||
|
|
status: "not_implemented",
|
||
|
|
version,
|
||
|
|
width,
|
||
|
|
})
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
app.all("/api/*", async (request, reply) => proxyToUpstream(request, reply, config.backendUpstream))
|
||
|
|
app.all("/docs", async (request, reply) => proxyToUpstream(request, reply, config.backendUpstream))
|
||
|
|
app.all("/docs/*", async (request, reply) => proxyToUpstream(request, reply, config.backendUpstream))
|
||
|
|
app.all("/docs-json", async (request, reply) => proxyToUpstream(request, reply, config.backendUpstream))
|
||
|
|
|
||
|
|
app.setNotFoundHandler(async (_request, reply) => {
|
||
|
|
return reply.code(404).send({
|
||
|
|
message: "route not found",
|
||
|
|
statusCode: 404,
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
return app
|
||
|
|
}
|
||
|
|
|
||
|
|
type ImageQuery = {
|
||
|
|
f?: string
|
||
|
|
q?: string
|
||
|
|
w?: string
|
||
|
|
}
|
||
|
|
|
||
|
|
type ImageParams = {
|
||
|
|
assetId: string
|
||
|
|
preset: string
|
||
|
|
version: string
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseVersionParam(value: string) {
|
||
|
|
if (!value.startsWith("v")) {
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
const parsed = Number.parseInt(value.slice(1), 10)
|
||
|
|
|
||
|
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseOptionalInteger(value: string | undefined) {
|
||
|
|
if (!value) {
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
const parsed = Number.parseInt(value, 10)
|
||
|
|
|
||
|
|
return Number.isFinite(parsed) ? parsed : null
|
||
|
|
}
|