import { Authorizer } from "@authorizerdev/authorizer-js"; import { Server } from "@hocuspocus/server"; import Sentry from "@sentry/node"; Sentry.init({ dsn: process.env.GLITCHTIP_DSN }); console.info("sentry initialized"); 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", }); console.info("authorizer initialized"); console.debug(authorizer.config.extraHeaders); const startServer = async () => { console.info("Starting server..."); 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, 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")); }); }); }, }); server.listen(); }; process.on("unhandledRejection", (reason, promise) => { Sentry.captureException(reason); }); process.on("uncaughtException", (error) => { Sentry.captureException(error); }); startServer();