farmcontrol-api/src/routes/sales/marketplaces.js
Tom Butcher 1cef3a8d1d
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
Implement syncWebhooks functionality and refactor ensureWebhookSubscriptions
This commit introduces the `syncWebhooks` function to streamline the process of synchronizing webhook configurations for marketplaces. It enhances the existing `ensureWebhookSubscriptions` function to utilize `syncWebhooks`, ensuring that webhook URLs and verification tokens are correctly set and updated. Additionally, a new module for managing webhook configurations is added, which includes utility functions for building webhook URLs and generating verification tokens. Tests are also included to validate the new functionality and its integration with existing systems, improving the overall reliability of webhook management.
2026-08-29 23:02:13 +01:00

294 lines
7.3 KiB
JavaScript

import express from 'express';
import { isAuthenticated } from '../../keycloak.js';
import { checkPermissions, hasPermission } from '../../database/permissions.js';
import { getFilter, convertPropertiesString, getSort } from '../../utils.js';
const router = express.Router();
const listAllowedFilters = [
'name',
'provider',
'active',
'connected',
'state.type',
'createdAt',
'updatedAt',
'state',
'_reference',
];
const listAllowedSorters = [
'name',
'provider',
'active',
'connected',
'state',
'createdAt',
'updatedAt',
'_id',
];
const propertiesAllowedFilters = [
'name',
'provider',
'active',
'connected',
'state.type',
'createdAt',
'updatedAt',
];
import {
listMarketplacesRouteHandler,
getMarketplaceRouteHandler,
editMarketplaceRouteHandler,
newMarketplaceRouteHandler,
deleteMarketplaceRouteHandler,
listMarketplacesByPropertiesRouteHandler,
getMarketplaceStatsRouteHandler,
getMarketplaceHistoryRouteHandler,
getMarketplaceAuthUrlRouteHandler,
exchangeMarketplaceAuthCodeRouteHandler,
refreshMarketplaceAuthRouteHandler,
syncMarketplaceRouteHandler,
syncMarketplaceItemsRouteHandler,
syncMarketplaceOrdersRouteHandler,
syncMarketplaceFulfillmentPoliciesRouteHandler,
syncMarketplacePaymentPoliciesRouteHandler,
syncMarketplaceReturnPoliciesRouteHandler,
syncMarketplaceTaxRatesRouteHandler,
syncMarketplaceWebhooksRouteHandler,
marketplaceWebhookRouteHandler,
marketplaceWebhookChallengeRouteHandler,
subscribeMarketplaceWebhooksRouteHandler,
debugEbayRouteHandler,
searchMarketplacesRouteHandler,
getMarketplacePropertyValuesRouteHandler,
getMarketplaceNeighborsRouteHandler,
} from '../../services/sales/marketplaces.js';
router.get('/', isAuthenticated, checkPermissions('marketplace', 'list'), async (req, res) => {
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listMarketplacesRouteHandler(
req,
res,
page,
limit,
property,
filter,
search,
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
router.get(
'/properties',
checkPermissions('marketplace', 'list'),
isAuthenticated,
async (req, res) => {
let properties = convertPropertiesString(req.query.properties);
const filter = await getFilter(req.query, propertiesAllowedFilters, false);
var masterFilter = {};
if (req.query.masterFilter) {
masterFilter = JSON.parse(req.query.masterFilter);
}
listMarketplacesByPropertiesRouteHandler(req, res, properties, filter, masterFilter);
}
);
router.get(
'/values',
checkPermissions('marketplace', 'list'),
isAuthenticated,
async (req, res) => {
const { property } = req.query;
getMarketplacePropertyValuesRouteHandler(req, res, property);
}
);
router.get(
'/search',
checkPermissions('marketplace', 'list'),
isAuthenticated,
async (req, res) => {
const { search } = req.query;
searchMarketplacesRouteHandler(req, res, search);
}
);
router.post('/', isAuthenticated, checkPermissions('marketplace', 'new'), async (req, res) => {
newMarketplaceRouteHandler(req, res);
});
router.get('/stats', isAuthenticated, async (req, res) => {
getMarketplaceStatsRouteHandler(req, res);
});
router.get('/history', isAuthenticated, async (req, res) => {
getMarketplaceHistoryRouteHandler(req, res);
});
router.get('/:id/auth/url', isAuthenticated, async (req, res) => {
getMarketplaceAuthUrlRouteHandler(req, res);
});
router.post(
'/:id/auth/exchange',
isAuthenticated,
async (req, res, next) => {
try {
const allowed =
(await hasPermission(req.user, 'marketplace', 'connect')) ||
(await hasPermission(req.user, 'marketplace', 'reconnect'));
if (!allowed) {
return res.status(403).json({ error: 'Forbidden', code: 'FORBIDDEN' });
}
return next();
} catch (err) {
return next(err);
}
},
async (req, res) => {
exchangeMarketplaceAuthCodeRouteHandler(req, res);
}
);
router.post(
'/:id/auth/refresh',
isAuthenticated,
checkPermissions('marketplace', 'refreshToken'),
async (req, res) => {
refreshMarketplaceAuthRouteHandler(req, res);
}
);
router.post(
'/:id/sync',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceRouteHandler(req, res);
}
);
router.post(
'/:id/sync/fulfillmentPolicies',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceFulfillmentPoliciesRouteHandler(req, res);
}
);
router.post(
'/:id/sync/paymentPolicies',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplacePaymentPoliciesRouteHandler(req, res);
}
);
router.post(
'/:id/sync/returnPolicies',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceReturnPoliciesRouteHandler(req, res);
}
);
router.post(
'/:id/sync/taxRates',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceTaxRatesRouteHandler(req, res);
}
);
router.post(
'/:id/sync/items',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceItemsRouteHandler(req, res);
}
);
router.post(
'/:id/sync/orders',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceOrdersRouteHandler(req, res);
}
);
router.post(
'/:id/sync/webhooks',
isAuthenticated,
checkPermissions('marketplace', 'sync'),
async (req, res) => {
syncMarketplaceWebhooksRouteHandler(req, res);
}
);
router.get('/:id/hook', async (req, res) => {
marketplaceWebhookChallengeRouteHandler(req, res);
});
router.get('/:id/ebay-challenge', async (req, res) => {
marketplaceWebhookChallengeRouteHandler(req, res);
});
// Webhook endpoint — no auth, provider verifies via signature
router.post('/:id/hook', async (req, res) => {
marketplaceWebhookRouteHandler(req, res);
});
router.post('/:id/ebay-webhook', async (req, res) => {
marketplaceWebhookRouteHandler(req, res);
});
router.post(
'/:id/webhooks/subscribe',
isAuthenticated,
checkPermissions('marketplace', 'connect'),
async (req, res) => {
subscribeMarketplaceWebhooksRouteHandler(req, res);
}
);
router.get('/:id/debug-ebay', isAuthenticated, async (req, res) => {
debugEbayRouteHandler(req, res);
});
router.get('/:id/debug/ebay', isAuthenticated, async (req, res) => {
debugEbayRouteHandler(req, res);
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getMarketplaceNeighborsRouteHandler(
req,
res,
property,
filter,
search,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});
router.get('/:id', isAuthenticated, checkPermissions('marketplace', 'info'), async (req, res) => {
getMarketplaceRouteHandler(req, res);
});
router.put('/:id', isAuthenticated, checkPermissions('marketplace', 'edit'), async (req, res) => {
editMarketplaceRouteHandler(req, res);
});
router.delete('/:id', isAuthenticated, async (req, res) => {
deleteMarketplaceRouteHandler(req, res);
});
export default router;