feat: Complete admin panel implementation
- Add user management system with all CRUD operations - Add promotion statistics dashboard with export - Simplify Trading & Market settings UI - Fix promotion schema (dates now optional) - Add missing API endpoints and PATCH support - Add comprehensive documentation - Fix critical bugs (deletePromotion, duplicate endpoints) All features tested and production-ready.
This commit is contained in:
1478
routes/admin-management.js
Normal file
1478
routes/admin-management.js
Normal file
File diff suppressed because it is too large
Load Diff
218
routes/config.js
Normal file
218
routes/config.js
Normal file
@@ -0,0 +1,218 @@
|
||||
import SiteConfig from "../models/SiteConfig.js";
|
||||
|
||||
/**
|
||||
* Public configuration routes
|
||||
* These endpoints are accessible without authentication
|
||||
* @param {FastifyInstance} fastify
|
||||
* @param {Object} options
|
||||
*/
|
||||
export default async function configRoutes(fastify, options) {
|
||||
// GET /config/public - Get public site configuration
|
||||
fastify.get("/public", async (request, reply) => {
|
||||
try {
|
||||
const config = await SiteConfig.getConfig();
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
config: {
|
||||
// Site status
|
||||
maintenance: {
|
||||
enabled: config.isMaintenanceActive(),
|
||||
message: config.maintenance.message,
|
||||
scheduledEnd: config.maintenance.scheduledEnd,
|
||||
},
|
||||
|
||||
// Features
|
||||
features: {
|
||||
twoFactorAuth: config.features.twoFactorAuth,
|
||||
emailVerification: config.features.emailVerification,
|
||||
giveaways: config.features.giveaways,
|
||||
affiliateProgram: config.features.affiliateProgram,
|
||||
},
|
||||
|
||||
// Trading settings (public info only)
|
||||
trading: {
|
||||
enabled: config.trading.enabled,
|
||||
depositEnabled: config.trading.depositEnabled,
|
||||
withdrawEnabled: config.trading.withdrawEnabled,
|
||||
minDeposit: config.trading.minDeposit,
|
||||
minWithdraw: config.trading.minWithdraw,
|
||||
withdrawFee: config.trading.withdrawFee,
|
||||
},
|
||||
|
||||
// Market settings (public info only)
|
||||
market: {
|
||||
enabled: config.market.enabled,
|
||||
commission: config.market.commission,
|
||||
minListingPrice: config.market.minListingPrice,
|
||||
maxListingPrice: config.market.maxListingPrice,
|
||||
},
|
||||
|
||||
// Social links
|
||||
social: config.social,
|
||||
|
||||
// Support
|
||||
support: {
|
||||
email: config.support.email,
|
||||
liveChatEnabled: config.support.liveChatEnabled,
|
||||
ticketSystemEnabled: config.support.ticketSystemEnabled,
|
||||
},
|
||||
|
||||
// SEO
|
||||
seo: config.seo,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to get public config:", error);
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve configuration",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// GET /config/announcements - Get active announcements
|
||||
fastify.get("/announcements", async (request, reply) => {
|
||||
try {
|
||||
const config = await SiteConfig.getConfig();
|
||||
const announcements = config.getActiveAnnouncements();
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
announcements: announcements.map((announcement) => ({
|
||||
id: announcement.id,
|
||||
type: announcement.type,
|
||||
message: announcement.message,
|
||||
dismissible: announcement.dismissible,
|
||||
createdAt: announcement.createdAt,
|
||||
})),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to get announcements:", error);
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve announcements",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// GET /config/promotions - Get active promotions
|
||||
fastify.get("/promotions", async (request, reply) => {
|
||||
try {
|
||||
const config = await SiteConfig.getConfig();
|
||||
const promotions = config.getActivePromotions();
|
||||
|
||||
// Return public promo info (hide some sensitive details)
|
||||
const publicPromos = promotions.map((promo) => ({
|
||||
id: promo.id,
|
||||
name: promo.name,
|
||||
description: promo.description,
|
||||
type: promo.type,
|
||||
startDate: promo.startDate,
|
||||
endDate: promo.endDate,
|
||||
bonusPercentage: promo.bonusPercentage,
|
||||
minDeposit: promo.minDeposit,
|
||||
maxBonus: promo.maxBonus,
|
||||
discountPercentage: promo.discountPercentage,
|
||||
newUsersOnly: promo.newUsersOnly,
|
||||
requiresCode: !!promo.code,
|
||||
bannerImage: promo.bannerImage,
|
||||
}));
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
promotions: publicPromos,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to get promotions:", error);
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve promotions",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// POST /config/validate-promo - Validate a promo code
|
||||
fastify.post(
|
||||
"/validate-promo",
|
||||
{
|
||||
schema: {
|
||||
body: {
|
||||
type: "object",
|
||||
required: ["code"],
|
||||
properties: {
|
||||
code: { type: "string", minLength: 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (request, reply) => {
|
||||
try {
|
||||
const { code } = request.body;
|
||||
const config = await SiteConfig.getConfig();
|
||||
|
||||
const promo = config.validatePromoCode(code);
|
||||
|
||||
if (!promo) {
|
||||
return reply.status(404).send({
|
||||
success: false,
|
||||
message: "Invalid or expired promo code",
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
valid: true,
|
||||
promotion: {
|
||||
id: promo.id,
|
||||
name: promo.name,
|
||||
description: promo.description,
|
||||
type: promo.type,
|
||||
bonusPercentage: promo.bonusPercentage,
|
||||
minDeposit: promo.minDeposit,
|
||||
maxBonus: promo.maxBonus,
|
||||
discountPercentage: promo.discountPercentage,
|
||||
endDate: promo.endDate,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to validate promo code:", error);
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to validate promo code",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// GET /config/status - Get current site status (simple health check with maintenance info)
|
||||
fastify.get("/status", async (request, reply) => {
|
||||
try {
|
||||
const config = await SiteConfig.getConfig();
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
status: "operational",
|
||||
maintenance: config.isMaintenanceActive(),
|
||||
services: {
|
||||
trading: config.trading.enabled,
|
||||
deposit: config.trading.depositEnabled,
|
||||
withdraw: config.trading.withdrawEnabled,
|
||||
market: config.market.enabled,
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to get status:", error);
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve status",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user