feat: добавить базовые сервисы image-platform

- добавлены backend, admin, gateway и worker skeleton
- добавлены Drizzle schema, database package и initial migration
- добавлены shared packages для RabbitMQ topology и S3 helpers
- обновлены dev-инфраструктура, env example, scripts и dependencies
- обновлена документация под versioned image URLs и read-through flow
This commit is contained in:
2026-05-05 09:59:21 +03:00
parent 37592c8b81
commit bcadb85a83
66 changed files with 8698 additions and 213 deletions

View File

@@ -0,0 +1,90 @@
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
}