All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Introduced `banners.ts` with CRUD operations for managing banners. - Added `/banners/active` endpoint to fetch active banners. - Secured admin-only routes for banner creation, update, and deletion. - Created `Banner.css` for banner styling.
148 lines
3.4 KiB
TypeScript
148 lines
3.4 KiB
TypeScript
import { FastifyPluginAsync } from 'fastify';
|
|
import { db } from '../config/database.js';
|
|
import { banners } from '../db/schema.js';
|
|
import { eq, and, lte, gte } from 'drizzle-orm';
|
|
|
|
const bannerBodyJsonSchema = {
|
|
type: 'object',
|
|
required: ['text', 'startDate', 'endDate'],
|
|
properties: {
|
|
text: { type: 'string' },
|
|
startDate: { type: 'string' },
|
|
endDate: { type: 'string' },
|
|
isActive: { type: 'boolean' },
|
|
},
|
|
} as const;
|
|
|
|
const bannersRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
|
// Get active banner (public endpoint)
|
|
fastify.get('/banners/active', async (request, reply) => {
|
|
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
|
|
const [activeBanner] = await db
|
|
.select()
|
|
.from(banners)
|
|
.where(
|
|
and(
|
|
eq(banners.isActive, true),
|
|
lte(banners.startDate, today),
|
|
gte(banners.endDate, today)
|
|
)
|
|
)
|
|
.limit(1);
|
|
|
|
if (!activeBanner) {
|
|
return { banner: null };
|
|
}
|
|
|
|
return {
|
|
banner: {
|
|
id: activeBanner.id,
|
|
text: activeBanner.text,
|
|
startDate: activeBanner.startDate,
|
|
endDate: activeBanner.endDate,
|
|
},
|
|
};
|
|
});
|
|
|
|
// Get all banners (admin only)
|
|
fastify.get('/banners', {
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
const allBanners = await db.select().from(banners);
|
|
|
|
return {
|
|
banners: allBanners.map((b: any) => ({
|
|
id: b.id,
|
|
text: b.text,
|
|
startDate: b.startDate,
|
|
endDate: b.endDate,
|
|
isActive: b.isActive,
|
|
createdAt: b.createdAt,
|
|
updatedAt: b.updatedAt,
|
|
})),
|
|
};
|
|
});
|
|
|
|
// Create banner (admin only)
|
|
fastify.post('/banners', {
|
|
schema: {
|
|
body: bannerBodyJsonSchema,
|
|
},
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
const { text, startDate, endDate, isActive = true } = request.body as any;
|
|
|
|
const [newBanner] = await db
|
|
.insert(banners)
|
|
.values({
|
|
text,
|
|
startDate,
|
|
endDate,
|
|
isActive,
|
|
})
|
|
.returning();
|
|
|
|
return {
|
|
banner: {
|
|
id: newBanner.id,
|
|
text: newBanner.text,
|
|
startDate: newBanner.startDate,
|
|
endDate: newBanner.endDate,
|
|
isActive: newBanner.isActive,
|
|
},
|
|
};
|
|
});
|
|
|
|
// Update banner (admin only)
|
|
fastify.put('/banners/:id', {
|
|
schema: {
|
|
body: bannerBodyJsonSchema,
|
|
},
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
const { id } = request.params as { id: string };
|
|
const { text, startDate, endDate, isActive } = request.body as any;
|
|
|
|
const [updated] = await db
|
|
.update(banners)
|
|
.set({
|
|
text,
|
|
startDate,
|
|
endDate,
|
|
isActive,
|
|
updatedAt: new Date(),
|
|
})
|
|
.where(eq(banners.id, id))
|
|
.returning();
|
|
|
|
if (!updated) {
|
|
return reply.code(404).send({ error: 'Banner not found' });
|
|
}
|
|
|
|
return {
|
|
banner: {
|
|
id: updated.id,
|
|
text: updated.text,
|
|
startDate: updated.startDate,
|
|
endDate: updated.endDate,
|
|
isActive: updated.isActive,
|
|
},
|
|
};
|
|
});
|
|
|
|
// Delete banner (admin only)
|
|
fastify.delete('/banners/:id', {
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
const { id } = request.params as { id: string };
|
|
|
|
await db.delete(banners).where(eq(banners.id, id));
|
|
|
|
return { success: true };
|
|
});
|
|
};
|
|
|
|
export default bannersRoute;
|