Added Paginated Endpoint

Reformatted Code
This commit is contained in:
2025-10-27 17:40:25 +01:00
parent 8af09e1841
commit 8ff63fadec
2 changed files with 140 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ import authRoutesAuthenticated from "./routes/auth/auth-authenticated";
import authPlugin from "./plugins/auth";
import adminRoutes from "./routes/admin";
import corsPlugin from "./plugins/cors";
import queryConfigPlugin from "./plugins/queryconfig";
import resourceRoutes from "./routes/resources";
import resourceRoutesSpecial from "./routes/resourcesSpecial";
import fastifyCookie from "@fastify/cookie";
@@ -20,6 +21,7 @@ import functionRoutes from "./routes/functions";
import bankingRoutes from "./routes/banking";
import exportRoutes from "./routes/exports"
import emailAsUserRoutes from "./routes/emailAsUser";
import authProfilesRoutes from "./routes/profiles";
import {sendMail} from "./utils/mailer";
import {loadSecrets, secrets} from "./utils/secrets";
@@ -27,7 +29,7 @@ import {initMailer} from "./utils/mailer"
import {initS3} from "./utils/s3";
async function main() {
const app = Fastify({ logger: true });
const app = Fastify({ logger: false });
await loadSecrets();
await initMailer();
await initS3();
@@ -42,6 +44,17 @@ async function main() {
await app.register(corsPlugin);
await app.register(supabasePlugin);
await app.register(tenantPlugin);
app.addHook('preHandler', (req, reply, done) => {
console.log('Matched path:', req.routeOptions.url)
done()
})
//Plugin nur auf bestimmten Routes
await app.register(queryConfigPlugin, {
routes: ['/api/resource/:resource/paginated']
})
app.register(fastifyCookie, {
secret: secrets.COOKIE_SECRET,
})
@@ -68,6 +81,7 @@ async function main() {
await subApp.register(bankingRoutes);
await subApp.register(exportRoutes);
await subApp.register(emailAsUserRoutes);
await subApp.register(authProfilesRoutes);
},{prefix: "/api"})

125
src/plugins/queryconfig.ts Normal file
View File

@@ -0,0 +1,125 @@
import fp from 'fastify-plugin'
import { FastifyPluginAsync, FastifyRequest } from 'fastify'
export interface QueryConfigPagination {
page: number
limit: number
offset: number
}
export interface QueryConfigSort {
field: string
direction: 'asc' | 'desc'
}
export interface QueryConfig {
pagination: QueryConfigPagination | null
sort: QueryConfigSort[]
filters: Record<string, string>
paginationDisabled: boolean
}
declare module 'fastify' {
interface FastifyRequest {
queryConfig: QueryConfig
}
}
interface QueryConfigPluginOptions {
routes?: string[]
}
function matchRoutePattern(currentPath: string, patterns: string[]): boolean {
return patterns.some(pattern => {
// Beispiel: /users/:id -> /^\/users\/[^/]+$/
const regex = new RegExp(
'^' +
pattern
.replace(/\*/g, '.*') // wildcard
.replace(/:[^/]+/g, '[^/]+') +
'$'
)
return regex.test(currentPath)
})
}
const queryConfigPlugin: FastifyPluginAsync<QueryConfigPluginOptions> = async (
fastify,
opts
) => {
const routePatterns = opts.routes || []
fastify.addHook('preHandler', async (req: FastifyRequest, reply) => {
const path = req.routeOptions.url || req.raw.url || ''
if (!matchRoutePattern(path, routePatterns)) {
return
}
const query = req.query as Record<string, any>
console.log(query)
// Pagination deaktivieren?
const disablePagination =
query.noPagination === 'true' ||
query.pagination === 'false' ||
query.limit === '0'
// Pagination berechnen
let pagination: QueryConfigPagination | null = null
if (!disablePagination) {
const page = Math.max(parseInt(query.page) || 1, 1)
const limit = Math.max(parseInt(query.limit) || 25, 1)
const offset = (page - 1) * limit
pagination = { page, limit, offset }
}
// Sortierung
const sort: QueryConfigSort[] = []
if (typeof query.sort === 'string') {
const items = query.sort.split(',')
for (const item of items) {
const [field, direction] = item.split(':')
sort.push({
field: field.trim(),
direction: (direction || 'asc').toLowerCase() === 'desc' ? 'desc' : 'asc'
})
}
}
// Filterung
const filters: Record<string, any> = {}
for (const [key, value] of Object.entries(query)) {
const match = key.match(/^filter\[(.+)\]$/)
if (!match) continue
const filterKey = match[1]
if (typeof value === 'string') {
// Split bei Komma → mehrere Werte
const parts = value.split(',').map(v => v.trim()).filter(Boolean)
// Automatische Typkonvertierung je Element
const parsedValues = parts.map(v => {
if (v === 'true') return true
if (v === 'false') return false
if (v === 'null') return null
return v
})
filters[filterKey] = parsedValues.length > 1 ? parsedValues : parsedValues[0]
}
}
req.queryConfig = {
pagination,
sort,
filters,
paginationDisabled: disablePagination
}
})
}
export default fp(queryConfigPlugin, { name: 'query-config' })