import { Router } from 'express';
import { paywallService } from '../services/paywall.js';
import { authenticate, requireCreator } from '../middleware/auth.js';
import { validateBody, createPaywallSchema, updatePaywallSchema } from '../utils/validation.js';
import { prisma } from '../config/database.js';
const router = Router();
// All routes require authentication
router.use(authenticate);
router.use(requireCreator);
// Create paywall
router.post('/', validateBody(createPaywallSchema), async (req, res, next) => {
try {
const paywall = await paywallService.create(req.user.id, req.body);
res.status(201).json({ paywall });
} catch (error) {
next(error);
}
});
// List paywalls
router.get('/', async (req, res, next) => {
try {
const { status, page = 1, limit = 20 } = req.query;
const result = await paywallService.listByCreator(req.user.id, {
status,
page: parseInt(page),
limit: parseInt(limit),
});
res.json(result);
} catch (error) {
next(error);
}
});
// Get paywall stats
router.get('/stats', async (req, res, next) => {
try {
const stats = await paywallService.getStats(req.user.id);
res.json(stats);
} catch (error) {
next(error);
}
});
// Fetch URL metadata
router.post('/fetch-metadata', async (req, res, next) => {
try {
const { url } = req.body;
if (!url) {
return res.status(400).json({ error: 'URL is required' });
}
const metadata = await paywallService.fetchUrlMetadata(url);
res.json(metadata);
} catch (error) {
next(error);
}
});
// Get single paywall
router.get('/:id', async (req, res, next) => {
try {
const paywall = await paywallService.findByIdAndCreator(req.params.id, req.user.id);
// Get additional stats
const [salesCount, totalRevenue] = await Promise.all([
prisma.sale.count({ where: { paywallId: paywall.id } }),
prisma.sale.aggregate({
where: { paywallId: paywall.id },
_sum: { netSats: true },
}),
]);
res.json({
paywall,
stats: {
salesCount,
totalRevenue: totalRevenue._sum.netSats || 0,
},
});
} catch (error) {
next(error);
}
});
// Update paywall
router.patch('/:id', validateBody(updatePaywallSchema), async (req, res, next) => {
try {
const paywall = await paywallService.update(req.user.id, req.params.id, req.body);
res.json({ paywall });
} catch (error) {
next(error);
}
});
// Archive paywall
router.post('/:id/archive', async (req, res, next) => {
try {
const paywall = await paywallService.archive(req.user.id, req.params.id);
res.json({ paywall });
} catch (error) {
next(error);
}
});
// Activate paywall
router.post('/:id/activate', async (req, res, next) => {
try {
await paywallService.findByIdAndCreator(req.params.id, req.user.id);
const paywall = await prisma.paywall.update({
where: { id: req.params.id },
data: { status: 'ACTIVE' },
});
res.json({ paywall });
} catch (error) {
next(error);
}
});
// Get paywall sales
router.get('/:id/sales', async (req, res, next) => {
try {
await paywallService.findByIdAndCreator(req.params.id, req.user.id);
const { page = 1, limit = 20 } = req.query;
const skip = (parseInt(page) - 1) * parseInt(limit);
const [sales, total] = await Promise.all([
prisma.sale.findMany({
where: { paywallId: req.params.id },
orderBy: { createdAt: 'desc' },
skip,
take: parseInt(limit),
}),
prisma.sale.count({ where: { paywallId: req.params.id } }),
]);
res.json({
sales,
total,
page: parseInt(page),
totalPages: Math.ceil(total / parseInt(limit)),
});
} catch (error) {
next(error);
}
});
// Get embed code
router.get('/:id/embed', async (req, res, next) => {
try {
const paywall = await paywallService.findByIdAndCreator(req.params.id, req.user.id);
const baseUrl = process.env.FRONTEND_URL || 'http://localhost:5173';
const apiUrl = process.env.API_URL || 'http://localhost:3001';
const embedCode = {
iframe: ``,
button: ``,
link: `${baseUrl}/p/${paywall.slug || paywall.id}`,
};
res.json(embedCode);
} catch (error) {
next(error);
}
});
export default router;