import { Authorizer } from '@authorizerdev/authorizer-js'; import { Server } from '@hocuspocus/server'; import * as Sentry from "@sentry/node"; const port = process.env.PORT || 4000; const authorizer = new Authorizer({ clientID: process.env.AUTHORIZER_CLIENT_ID || '', authorizerURL: process.env.AUTHORIZER_URL || 'https://auth.discours.io', redirectURL: process.env.REDIRECT_URL || 'https://testing.discours.io', }); Sentry.init({ dsn: process.env.GLITCHTIP_DSN }); const startServer = async () => { const server = await Server.configure({ port: process.env.PORT || 4000, async onConnect({ connection }) { connection.requiresAuthentication = true; }, onAuthenticate(data) { return new Promise((resolve, reject) => { const headers = data.requestHeaders; if (!headers) { console.error('Request headers not found'); return reject(new Error('Required header is not present')); } const shout_id = parseInt(data.documentName.replace('shout-', ''), 10); console.debug(`shout_id extracted: ${shout_id}`); const token = data.token || headers['authorization'] || ''; if (!token) { console.error('Authorization token not found'); return reject(new Error('Token is not found')); } authorizer.validateJWTToken({ token_type: 'access_token', token }) .then(response => { if (!response?.data?.is_valid) { console.error('Invalid authorization token'); return reject(new Error('Token is invalid')); } const { sub: user, allowed_roles: roles } = response.data.claims; console.debug(`user: ${user} roles: ${roles}`); if (roles.includes('editor')) { return resolve({ id: user, roles: Array.isArray(roles) ? roles : roles.split(','), }); } fetch('https://core.discours.io/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: ` query { get_author_id(user: "${user}") { id slug user } } `, }), }) .then(res => res.json()) .then(({ data }) => { // console.debug(data) const { id: author_id } = data.get_author_id if (author_id) { fetch('https://core.discours.io/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: `query { get_shout(shout_id: ${shout_id}) { id slug authors { id } } } ` }), }) .then(res => res.json()) .then(({data}) => { // console.debug('shout data:', data) const { authors } = data.get_shout; if (authors.some(author => author.id === author_id)) { return resolve({ id: user, author: author_id, roles: Array.isArray(roles) ? roles : roles.split(','), }); } return reject(new Error('User is not in authors list')); }) .catch(e => { console.error('Error fetching shout data:', e.message); console.error(e.stack); return reject(new Error('Error fetching shout data')); }); } }) .catch(e => { console.error('Error fetching author data:', e.message); console.error(e.stack); return reject(new Error('Error fetching author data')); }); }) .catch(e => { console.error('Error validating authorization token:', e.message); console.error(e.stack); return reject(new Error('Error validating authorization token')); }); }); }, }); process.on('unhandledRejection', (reason, promise) => { Sentry.captureException(reason); }); process.on('uncaughtException', (error) => { Sentry.captureException(error); }); }; startServer();