module.exports = (app) => { /** * Controllers */ const Controllers = app.set('controllers'); const webhooks = Controllers.webhooks; /** * Status. */ app.use('/status', serverStatus(app)); /** * Webhook. */ app.get('/webhook', webhooks.verification); app.post('/webhook', webhooks.messages); /** * Error handler. */ app.use(errorHandler); }
module.exports = (app) => { /** * Redirects */ app.get('/consciousnesshackingsf', (req, res) => res.redirect('/chsf')); app.get('/consciousnesshackingsv', (req, res) => res.redirect('/chsv')); /** * Server status */ app.use('/status', serverStatus(app)); /** * Favicon */ app.use(favicon(path.join(__dirname, '/../frontend/dist/images/favicon.ico.png'))); /** * Static folder */ app.use('/static', express.static(path.join(__dirname, `../frontend/dist`), { maxAge: '1d' })); /** * GET /robots.txt */ app.use(robots(path.join(__dirname, '../frontend/dist/robots.txt'))); /** * Pipe the requests before the middlewares, the piping will only work with raw * data * More infos: https://github.com/request/request/issues/1664#issuecomment-117721025 */ app.all('/api/*', (req, res) => { req .pipe(request(apiUrl(req.url), { followRedirect: false })) .pipe(res); }); /** * Routes */ app.get('/:slug/banner.md', mw.cache(300), mw.fetchGroupBySlug, mw.fetchUsers(), controllers.banner.markdown); app.get('/:slug/banner.js', mw.cache(3000), mw.fetchGroupBySlug, mw.fetchUsers(), controllers.banner.js); app.get('/:slug/:tier.md', mw.cache(300), mw.fetchGroupBySlug, mw.fetchUsers(), controllers.banner.markdown); app.get('/:slug/:tier.:format(svg|png)', mw.cache(300), mw.fetchUsers(), controllers.banner.banner); app.get('/:slug/:tier/badge.svg', mw.cache(300), mw.fetchUsers(), controllers.banner.badge); app.get('/:slug/badge/:tier.svg', mw.cache(300), mw.fetchUsers(), controllers.banner.badge); app.get('/:slug/:tier/:position/avatar(.:format(png|jpg|svg))?', mw.cache(300), mw.ga, mw.fetchUsers({cache: 300}), controllers.banner.avatar); app.get('/:slug/:tier/:position/website', mw.ga, mw.fetchUsers(), controllers.banner.redirect); app.get('/:slug([A-Za-z0-9-]+)/widget', mw.cache(300), mw.fetchGroupBySlug, controllers.collectives.widget); /** * Server side render the react app * * NOTE: * When we refactor PublicGroup to fetch the group in the container, we can remove * the explicit routes and just do `app.use(render)` */ app.get('/', mw.ga, mw.addTitle('OpenCollective - Collect and disburse money transparently'), render); app.get('/login/:token', mw.ga, mw.addTitle('Open Collective'), render); app.get('/login', mw.ga, mw.addTitle('Open Collective Login'), render); app.get('/leaderboard', mw.ga, mw.fetchLeaderboard, mw.addTitle('Open Collective Leaderboard'), render); app.get('/opensource/apply/:token', mw.ga, mw.extractGithubUsernameFromToken, mw.addTitle('Sign up your Github repository'), render); app.get('/opensource/apply', mw.ga, mw.addTitle('Sign up your Github repository'), render); /* Leaving github/apply routes for existing links */ app.get('/github/apply/:token', mw.ga, mw.extractGithubUsernameFromToken, mw.addTitle('Sign up your Github repository'), render); app.get('/github/apply', mw.ga, mw.addTitle('Sign up your Github repository'), render); app.get('/connect/github', mw.ga, render); app.get('/:slug/connect/twitter', mw.ga, render); app.get('/:slug/edit-twitter', mw.ga, mw.fetchGroupBySlug, render); app.get('/subscriptions', mw.ga, mw.addTitle('My Subscriptions'), render); app.get('/:slug([A-Za-z0-9-]+)/:type(expenses|donations)', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/expenses/new', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/donate/:amount', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/donate/:amount/:interval', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)', mw.ga, controllers.profile, mw.addMeta, render); app.use(mw.handleUncaughtError) };
module.exports = (app) => { /** * Server status */ app.use('/status', serverStatus(app)); /** * Favicon */ app.use(favicon(path.join(__dirname, '/../frontend/dist/images/favicon.ico.png'))); /** * Static folder */ app.use('/static', express.static(path.join(__dirname, `../frontend/dist`), { maxAge: '1d' })); /** * GET /robots.txt */ app.use(robots(path.join(__dirname, '../frontend/dist/robots.txt'))); /** * Authentication */ app.get('/api/auth/:service(github)/:slug', aN.authenticateService); app.get('/auth/:service(github)/callback/:slug', aN.authenticateServiceCallback); /** * Pipe the requests before the middlewares, the piping will only work with raw * data * More infos: https://github.com/request/request/issues/1664#issuecomment-117721025 */ app.all('/api/*', (req, res) => { req .pipe(request(apiUrl(req.url))) .pipe(res); }); /** * Routes */ app.get('/:slug/banner.md', mw.cache(300), mw.fetchGroupBySlug, mw.fetchUsers(), banner.markdown); app.get('/:slug/:tier/banner.md', mw.cache(300), mw.fetchGroupBySlug, mw.fetchUsers(), banner.markdown); app.get('/:slug/:tier/badge.svg', mw.cache(300), mw.fetchUsers(), banner.badge); app.get('/:slug/badge/:tier.svg', mw.cache(300), mw.fetchUsers(), banner.badge); app.get('/:slug/:tier/:position/avatar(.:format(png|jpg|svg))?', mw.cache(300), mw.ga, mw.fetchUsers({cache: 300}), banner.avatar); app.get('/:slug/:tier/:position/website', mw.ga, mw.fetchUsers(), banner.redirect); app.get('/:slug([A-Za-z0-9-]+)/widget', mw.cache(300), mw.fetchGroupBySlug, collectives.widget); /** * Server side render the react app * * NOTE: * When we refactor PublicGroup to fetch the group in the container, we can remove * the explicit routes and just do `app.use(render)` */ app.get('/leaderboard', mw.ga, mw.fetchLeaderboard, mw.addTitle('Open Collective Leaderboard'), render); app.get('/subscriptions/:token', mw.ga, mw.fetchSubscriptionsByUserWithToken, mw.addTitle('My Subscriptions'), render); app.get('/subscriptions', mw.ga, mw.fetchSubscriptionsByUserWithToken, mw.addTitle('My Subscriptions'), render); app.get('/:slug([A-Za-z0-9-]+)', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/:type', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/expenses/new', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/donate/:amount', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/donate/:amount/:interval', mw.ga, mw.fetchGroupBySlug, mw.addMeta, render); app.get('/:slug([A-Za-z0-9-]+)/connect/:service(github)', mw.ga, render); };
module.exports = (app) => { const aN = require('./middleware/security/authentication')(app); const aZ = require('./middleware/security/authorization')(app); const errorHandler = require('./middleware/error_handler'); const cache = require('./middleware/cache'); const params = require('./params')(app); /** * Controllers */ const Controllers = app.set('controllers'); const mw = Controllers.middlewares; const users = Controllers.users; const groups = Controllers.groups; const activities = Controllers.activities; const notifications = Controllers.notifications; const transactions = Controllers.transactions; const donations = Controllers.donations; const paypal = Controllers.paypal; const images = Controllers.images; const paymentMethods = Controllers.paymentmethods; const webhooks = Controllers.webhooks; const stripe = Controllers.stripe; const test = Controllers.test; const subscriptions = Controllers.subscriptions; const HOST = roles.HOST; const MEMBER = roles.MEMBER; /** * Status. */ app.use('/status', serverStatus(app)); /** * Parameters. */ app.param('userid', params.userid); app.param('groupid', params.groupid); app.param('transactionid', params.transactionid); app.param('paranoidtransactionid', params.paranoidtransactionid); /** * User reset password flow (no jwt verification) */ app.post('/users/password/forgot', aN.authenticateAppByApiKey, required('email'), users.forgotPassword); // Send forgot password email app.post('/users/password/reset/:userid_enc/:reset_token', aN.authenticateAppByApiKey, required('password', 'passwordConfirmation'), users.resetPassword); // Reset password app.post('/subscriptions/new_token', aN.authenticateAppByApiKey, required('email'), subscriptions.sendNewTokenByEmail); /** * Routes without expiration validation */ app.post('/subscriptions/refresh_token', aN.authenticateUserAndAppByJwtNoExpiry(), subscriptions.refreshTokenByEmail); /** * For testing the email templates */ app.get('/email/:template', test.generateTestEmail); /** * Users. */ app.post('/users', aN.authenticateAppByApiKey, aZ.appAccess(0.5), required('user'), users.create); // Create a user. app.get('/users/:userid', aN.authenticateUserByJwt(), users.show); // Get a user. app.put('/users/:userid', aN.authenticateAppByApiKey, required('user'), users.updateUserWithoutLoggedIn); // Update a user. app.put('/users/:userid/password', aZ.authorizeUserToAccessUser(), required('password', 'passwordConfirmation'), users.updatePassword); // Update a user password. app.put('/users/:userid/paypalemail', required('paypalEmail'), aZ.authorizeUserToAccessUser(), users.updatePaypalEmail); // Update a user paypal email. app.put('/users/:userid/avatar', required('avatar'), aZ.authorizeUserToAccessUser(), users.updateAvatar); // Update a user's avatar app.get('/users/:userid/email', NotImplemented); // Confirm a user's email. // TODO why is this route duplicated? app.post('/users', aN.authenticateAppByApiKey, aZ.appAccess(0.5), required('user'), users.create); // Create a user. /** * Authentication. */ app.post('/authenticate', aN.authenticateAppByApiKey, aN.authenticateUserByPassword, users.getToken); // Authenticate user to get a token. app.post('/authenticate/refresh', NotImplemented); // Refresh the token (using a valid token OR a expired token + refresh_token). app.post('/authenticate/reset', NotImplemented); // Reset the refresh_token. /** * Credit paymentMethod. * * Let's assume for now a paymentMethod is linked to a user. */ // delete this route #postmigration, once frontend is updated app.get('/users/:userid/cards', aZ.authorizeUserToAccessUser(), paymentMethods.getPaymentMethods); // Get a user's paymentMethods. app.get('/users/:userid/payment-methods', aZ.authorizeUserToAccessUser(), paymentMethods.getPaymentMethods); // Get a user's paymentMethods. app.post('/users/:userid/payment-methods', NotImplemented); // Create a user's paymentMethod. app.put('/users/:userid/payment-methods/:paymentMethodid', NotImplemented); // Update a user's paymentMethod. app.delete('/users/:userid/payment-methods/:paymentMethodid', NotImplemented); // Delete a user's paymentMethod. /** * Paypal Preapproval. */ app.get('/users/:userid/paypal/preapproval', aZ.authorizeUserToAccessUser(), paypal.getPreapprovalKey); // Get a user's preapproval key. app.post('/users/:userid/paypal/preapproval/:preapprovalkey', aZ.authorizeUserToAccessUser(), paypal.confirmPreapproval); // Confirm a preapproval key. app.get('/users/:userid/paypal/preapproval/:preapprovalkey', aZ.authorizeUserToAccessUser(), paypal.getDetails); // Get a preapproval key details. /** * Groups. */ app.post('/groups', aN.authenticateUserByJwt(), required('group'), groups.create); // Create a group. Option `role` to assign the caller directly (default to null). app.get('/groups/:groupid', aZ.authorizeAccessToGroup({authIfPublic: true}), groups.getOne); app.get('/groups/:groupid/users', aZ.authorizeAccessToGroup({authIfPublic: true}), cache(60), groups.getUsers); // Get group users app.put('/groups/:groupid', aZ.authorizeAccessToGroup({userRoles: [HOST, MEMBER], bypassUserRolesCheckIfAuthenticatedAsAppAndNotUser: true}), required('group'), groups.update); // Update a group. app.delete('/groups/:groupid', NotImplemented); // Delete a group. app.post('/groups/:groupid/donations', aN.authenticateUserOrApp(), required('payment'), mw.getOrCreateUser, donations.post); // Make a payment/donation. app.post('/groups/:groupid/donations/paypal', aN.authenticateUserOrApp(), required('payment'), donations.paypal); // Make a payment/donation. // TODO: Remove after frontend migrates to new calls above app.post('/groups/:groupid/payments', aN.authenticateUserOrApp(), required('payment'), mw.getOrCreateUser, donations.post); // Make a payment/donation. app.post('/groups/:groupid/payments/paypal', aN.authenticateUserOrApp(), required('payment'), donations.paypal); // Make a payment/donation. /** * UserGroup. * * Relations between a group and a user. */ app.get('/users/:userid/groups', aZ.authorizeUserToAccessUser(), users.getGroups); // Get user's groups. app.post('/groups/:groupid/users/:userid', aZ.authorizeAccessToGroup({userRoles: [HOST]}), groups.addUser); // Add a user to a group. app.put('/groups/:groupid/users/:userid', aZ.authorizeAccessToGroup({userRoles: [HOST]}), groups.updateUser); // Update a user's role in a group. app.delete('/groups/:groupid/users/:userid', aZ.authorizeAccessToGroup({userRoles: [HOST]}), groups.deleteUser); // Remove a user from a group. /** * Transactions (financial). */ app.get('/groups/:groupid/transactions', aZ.authorizeAccessToGroup({authIfPublic: true}), mw.paginate(), mw.sorting({key: 'createdAt', dir: 'DESC'}), groups.getTransactions); // Get a group's transactions. // xdamman: having two times the same route is a mess (hard to read and error prone if we forget to return) // This is caused by mw.authorizeIfGroupPublic that is doing a next('route') // TODO refactor with single route using authentication.js and authorization.js middleware const commonLegacySecurityMw = [mw.apiKey, jwt, mw.identifyFromToken, mw.checkJWTExpiration]; app.post('/groups/:groupid/transactions', commonLegacySecurityMw, mw.authorizeIfGroupPublic, mw.authorizeAuthUserOrApp, mw.authorizeGroup, required('transaction'), mw.getOrCreateUser, groups.createTransaction); // Create a transaction for a group. app.post('/groups/:groupid/transactions', commonLegacySecurityMw, required('transaction'), mw.getOrCreateUser, groups.createTransaction); // Create a transaction for a group. app.get('/groups/:groupid/transactions/:transactionid', aZ.authorizeAccessToGroup({authIfPublic: true}), aZ.authorizeGroupAccessToTransaction({authIfPublic: true}), groups.getTransaction); // Get a transaction. app.put('/groups/:groupid/transactions/:transactionid', aZ.authorizeAccessToGroup(), aZ.authorizeGroupAccessToTransaction(), required('transaction'), groups.updateTransaction); // Update a transaction. app.delete('/groups/:groupid/transactions/:transactionid', aZ.authorizeAccessToGroup({userRoles: [HOST], bypassUserRolesCheckIfAuthenticatedAsAppAndNotUser: true}), aZ.authorizeGroupAccessToTransaction(), groups.deleteTransaction); // Delete a transaction. app.post('/groups/:groupid/transactions/:transactionid/approve', aZ.authorizeAccessToGroup(), aZ.authorizeGroupAccessToTransaction(), required('approved'), transactions.setApprovedState); // Approve a transaction. app.post('/groups/:groupid/transactions/:transactionid/pay', aZ.authorizeAccessToGroup({userRoles: [HOST, MEMBER]}), aZ.authorizeGroupAccessToTransaction(), required('service'), transactions.pay); // Pay a transaction. app.post('/groups/:groupid/transactions/:transactionid/attribution/:userid', aZ.authorizeAccessToGroup({userRoles: [HOST, MEMBER], bypassUserRolesCheckIfAuthenticatedAsAppAndNotUser: true}), aZ.authorizeGroupAccessToTransaction(), transactions.attributeUser); // Attribute a transaction to a user. app.get('/groups/:groupid/transactions/:paranoidtransactionid/callback', donations.paypalCallback); // Callback after a payment /** * Activities. * * An activity is any action linked to a User or a Group. */ app.get('/groups/:groupid/activities', aZ.authorizeAccessToGroup(), mw.paginate(), mw.sorting({key: 'createdAt', dir: 'DESC'}), activities.group); // Get a group's activities. app.get('/users/:userid/activities', aZ.authorizeUserToAccessUser(), mw.paginate(), mw.sorting({key: 'createdAt', dir: 'DESC'}), activities.user); // Get a user's activities. /** * Notifications. * * A user can subscribe by email to any type of activity of a Group. */ app.post('/groups/:groupid/activities/:activityType/subscribe', aN.authenticateUserByJwt(), aZ.authorizeAccessToGroup(), notifications.subscribe); // Subscribe to a group's activities app.post('/groups/:groupid/activities/:activityType/unsubscribe', aN.authenticateUserByJwt(), aZ.authorizeAccessToGroup(), notifications.unsubscribe); // Unsubscribe to a group's activities /** * Separate route for uploading images to S3 */ app.post('/images', aN.authenticateUserByJwt(), images.upload); /** * Webhook for stripe when it gets a new subscription invoice */ app.post('/webhooks/stripe', webhooks.stripe); /** * Stripe oAuth */ app.get('/stripe/authorize', aN.authenticateUserByJwt(), stripe.authorize); app.get('/stripe/oauth/callback', stripe.callback); /** * Reset test-api database */ app.get('/database/reset', test.resetTestDatabase); /** * Stripe subscriptions (recurring payments) */ app.get('/subscriptions', aZ.authorizeUserToAccessScope('subscriptions'), subscriptions.getAll); app.post('/subscriptions/:subscriptionid/cancel', aZ.authorizeUserToAccessScope('subscriptions'), subscriptions.cancel); /** * Leaderboard */ app.get('/leaderboard', aN.authenticateAppByApiKey, groups.getLeaderboard); // Create a user. /** * Error handler. */ app.use(errorHandler); };
import busted from './locals/busted'; /** * Express app */ const app = express(); /** * Locals for the templates */ app.locals.busted = busted; /** * Server status */ app.use('/status', serverStatus(app)); /** * Favicon */ app.use(favicon(path.join(__dirname, '/../frontend/dist/images/favicon.ico.png'))); /** * Log */ app.use(morgan('dev')); /** * Static folder */ app.use('/static', express.static(path.join(__dirname, '../frontend/dist')));