Added Paginated Endpoint
Reformatted Code
This commit is contained in:
16
src/index.ts
16
src/index.ts
@@ -10,6 +10,7 @@ import authRoutesAuthenticated from "./routes/auth/auth-authenticated";
|
|||||||
import authPlugin from "./plugins/auth";
|
import authPlugin from "./plugins/auth";
|
||||||
import adminRoutes from "./routes/admin";
|
import adminRoutes from "./routes/admin";
|
||||||
import corsPlugin from "./plugins/cors";
|
import corsPlugin from "./plugins/cors";
|
||||||
|
import queryConfigPlugin from "./plugins/queryconfig";
|
||||||
import resourceRoutes from "./routes/resources";
|
import resourceRoutes from "./routes/resources";
|
||||||
import resourceRoutesSpecial from "./routes/resourcesSpecial";
|
import resourceRoutesSpecial from "./routes/resourcesSpecial";
|
||||||
import fastifyCookie from "@fastify/cookie";
|
import fastifyCookie from "@fastify/cookie";
|
||||||
@@ -20,6 +21,7 @@ import functionRoutes from "./routes/functions";
|
|||||||
import bankingRoutes from "./routes/banking";
|
import bankingRoutes from "./routes/banking";
|
||||||
import exportRoutes from "./routes/exports"
|
import exportRoutes from "./routes/exports"
|
||||||
import emailAsUserRoutes from "./routes/emailAsUser";
|
import emailAsUserRoutes from "./routes/emailAsUser";
|
||||||
|
import authProfilesRoutes from "./routes/profiles";
|
||||||
|
|
||||||
import {sendMail} from "./utils/mailer";
|
import {sendMail} from "./utils/mailer";
|
||||||
import {loadSecrets, secrets} from "./utils/secrets";
|
import {loadSecrets, secrets} from "./utils/secrets";
|
||||||
@@ -27,7 +29,7 @@ import {initMailer} from "./utils/mailer"
|
|||||||
import {initS3} from "./utils/s3";
|
import {initS3} from "./utils/s3";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const app = Fastify({ logger: true });
|
const app = Fastify({ logger: false });
|
||||||
await loadSecrets();
|
await loadSecrets();
|
||||||
await initMailer();
|
await initMailer();
|
||||||
await initS3();
|
await initS3();
|
||||||
@@ -42,6 +44,17 @@ async function main() {
|
|||||||
await app.register(corsPlugin);
|
await app.register(corsPlugin);
|
||||||
await app.register(supabasePlugin);
|
await app.register(supabasePlugin);
|
||||||
await app.register(tenantPlugin);
|
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, {
|
app.register(fastifyCookie, {
|
||||||
secret: secrets.COOKIE_SECRET,
|
secret: secrets.COOKIE_SECRET,
|
||||||
})
|
})
|
||||||
@@ -68,6 +81,7 @@ async function main() {
|
|||||||
await subApp.register(bankingRoutes);
|
await subApp.register(bankingRoutes);
|
||||||
await subApp.register(exportRoutes);
|
await subApp.register(exportRoutes);
|
||||||
await subApp.register(emailAsUserRoutes);
|
await subApp.register(emailAsUserRoutes);
|
||||||
|
await subApp.register(authProfilesRoutes);
|
||||||
|
|
||||||
},{prefix: "/api"})
|
},{prefix: "/api"})
|
||||||
|
|
||||||
|
|||||||
125
src/plugins/queryconfig.ts
Normal file
125
src/plugins/queryconfig.ts
Normal 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' })
|
||||||
Reference in New Issue
Block a user