diff options
| author | Raphael <mail@raphaelkabo.com> | 2023-05-13 19:24:13 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-13 19:24:13 +0100 | 
| commit | 3ec47e9c512ebf7432a4fa139bc0478f9ba98d3b (patch) | |
| tree | a54618c5dc9f84a8ba06f9c8c33d244f5f26f779 | |
| parent | f1a5a9b9a0f8357e25e3b2c8d72d745093bda9c3 (diff) | |
| parent | 365398359f2d80345a61f76f3ecf0fd6eb9bdde6 (diff) | |
Merge pull request #98 from lowercasename/rk/new-config
TOML config library and migration from CommonJS to ES modules
| -rw-r--r-- | .github/workflows/build-test.yaml (renamed from .github/workflows/ci.yaml) | 5 | ||||
| -rwxr-xr-x | .gitignore | 4 | ||||
| -rw-r--r-- | config/config.example.toml | 33 | ||||
| -rw-r--r-- | package.json | 5 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 15 | ||||
| -rw-r--r-- | src/activitypub.js | 71 | ||||
| -rwxr-xr-x | src/app.js | 13 | ||||
| -rw-r--r-- | src/config/api-example.js | 8 | ||||
| -rw-r--r-- | src/config/database-docker.js | 3 | ||||
| -rw-r--r-- | src/config/database-example.js | 3 | ||||
| -rw-r--r-- | src/config/domain-example.js | 13 | ||||
| -rw-r--r-- | src/config/gathio.service | 13 | ||||
| -rw-r--r-- | src/helpers.js | 23 | ||||
| -rw-r--r-- | src/lib/config.ts | 56 | ||||
| -rwxr-xr-x | src/models/Event.js | 4 | ||||
| -rwxr-xr-x | src/models/EventGroup.js | 4 | ||||
| -rwxr-xr-x | src/models/Log.js | 4 | ||||
| -rwxr-xr-x | src/routes.js | 232 | ||||
| -rwxr-xr-x | src/start.js | 23 | 
19 files changed, 271 insertions, 261 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/build-test.yaml index 882c2c9..d84282c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/build-test.yaml @@ -22,10 +22,7 @@ jobs:        - name: Install dependencies          run: pnpm install        - name: Set up config files -        run: | -          cp src/config/api-example.js src/config/api.js -          cp src/config/database-example.js src/config/database.js -          cp src/config/domain-example.js src/config/domain.js +        run: cp config/config.example.toml config/config.toml        - name: Build          run: pnpm build          continue-on-error: true @@ -2,10 +2,8 @@  # gathio custom +config/config.toml  dist -src/config/api.js -src/config/database.js -src/config/domain.js  public/events/*  !public/events/.gitkeep diff --git a/config/config.example.toml b/config/config.example.toml new file mode 100644 index 0000000..1ffbeb7 --- /dev/null +++ b/config/config.example.toml @@ -0,0 +1,33 @@ +[general] +# Your domain goes here. If there is a port it should be 'domain.com:port', but +# otherwise just 'domain.com'. +domain = "localhost:3000" +port = "3000" +email = "contact@example.com" +site_name = "gathio" +is_federated = true +# If left blank, this defaults to +# https://yourdomain.com/images/gathio-email-logo.gif. Set a full URL here to +# change it to your own logo (or just change the file itself). +email_logo_url = "" +# Show a Ko-Fi box to donate money to Raphael (Gathio's creator) on the front +# page. +show_kofi = false +# Which mail service to use to send emails to hosts and attendees. Options are +# 'nodemailer' or 'sendgrid'. Configure settings for this mail +# service below. +mail_service = "nodemailer" + +[database] +# Set up for a locally running MongoDB connection. Change this to +# 'mongodb://mongo:27017/gathio' for a Dockerised connection. +mongodb_url = "mongodb://localhost:27017/gathio" + +[nodemailer] +smtp_server = "" +smtp_port = "" +smtp_username = "" +smtp_password = "" + +[sendgrid] +api_key = "" diff --git a/package.json b/package.json index db85892..90047c3 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@    "version": "1.3.0",    "description": "",    "main": "index.js", +  "type": "module",    "scripts": {      "build": "tsc",      "start": "node dist/start.js", @@ -19,7 +20,6 @@      "@sendgrid/mail": "^6.5.5",      "body-parser": "^1.20.2",      "cors": "^2.8.5", -    "dotenv": "^6.2.0",      "express": "^4.18.2",      "express-fileupload": "^1.4.0",      "express-handlebars": "^6.0.7", @@ -38,7 +38,8 @@      "nodemailer": "^6.9.2",      "randomstring": "^1.2.3",      "request": "^2.88.2", -    "sanitize-html": "^2.10.0" +    "sanitize-html": "^2.10.0", +    "toml": "^3.0.0"    },    "devDependencies": {      "eslint": "^8.40.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33b638a..e310e2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,9 +10,6 @@ dependencies:    cors:      specifier: ^2.8.5      version: 2.8.5 -  dotenv: -    specifier: ^6.2.0 -    version: 6.2.0    express:      specifier: ^4.18.2      version: 4.18.2 @@ -70,6 +67,9 @@ dependencies:    sanitize-html:      specifier: ^2.10.0      version: 2.10.0 +  toml: +    specifier: ^3.0.0 +    version: 3.0.0  devDependencies:    eslint: @@ -1108,11 +1108,6 @@ packages:        domhandler: 5.0.3      dev: false -  /dotenv@6.2.0: -    resolution: {integrity: sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==} -    engines: {node: '>=6'} -    dev: false -    /ecc-jsbn@0.1.2:      resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}      dependencies: @@ -2739,6 +2734,10 @@ packages:        ieee754: 1.2.1      dev: false +  /toml@3.0.0: +    resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} +    dev: false +    /touch@3.1.0:      resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}      hasBin: true diff --git a/src/activitypub.js b/src/activitypub.js index c8fc682..555f44d 100644 --- a/src/activitypub.js +++ b/src/activitypub.js @@ -1,24 +1,24 @@ -const domain = require("./config/domain.js").domain; -const contactEmail = require("./config/domain.js").email; -const siteName = require("./config/domain.js").sitename; -const isFederated = require("./config/domain.js").isFederated; -const request = require("request"); -const addToLog = require("./helpers.js").addToLog; -const crypto = require("crypto"); +import request from "request"; +import { addToLog } from "./helpers.js"; +import crypto from "crypto"; +import { customAlphabet } from "nanoid"; +import moment from "moment-timezone"; +import sanitizeHtml from "sanitize-html"; +import { getConfig } from "./lib/config.js"; +const config = getConfig(); +const domain = config.general.domain; +const siteName = config.general.site_name; +const isFederated = config.general.is_federated; +import Event from "./models/Event.js"; +  // This alphabet (used to generate all event, group, etc. IDs) is missing '-'  // because ActivityPub doesn't like it in IDs -const { customAlphabet } = require("nanoid");  const nanoid = customAlphabet(    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_",    21  ); -var moment = require("moment-timezone"); -const mongoose = require("mongoose"); -const Event = mongoose.model("Event"); -const EventGroup = mongoose.model("EventGroup"); -var sanitizeHtml = require("sanitize-html"); -function createActivityPubActor( +export function createActivityPubActor(    eventID,    domain,    pubkey, @@ -70,7 +70,7 @@ function createActivityPubActor(    return JSON.stringify(actor);  } -function createActivityPubEvent( +export function createActivityPubEvent(    name,    startUTC,    endUTC, @@ -92,7 +92,7 @@ function createActivityPubEvent(    return JSON.stringify(eventObject);  } -function createFeaturedPost( +export function createFeaturedPost(    eventID,    name,    startUTC, @@ -113,7 +113,7 @@ function createFeaturedPost(    return featured;  } -function updateActivityPubEvent( +export function updateActivityPubEvent(    oldEvent,    name,    startUTC, @@ -137,7 +137,7 @@ function updateActivityPubEvent(    return JSON.stringify(eventObject);  } -function updateActivityPubActor( +export function updateActivityPubActor(    actor,    description,    name, @@ -168,7 +168,7 @@ function updateActivityPubActor(    return JSON.stringify(actor);  } -function signAndSend(message, eventID, targetDomain, inbox, callback) { +export function signAndSend(message, eventID, targetDomain, inbox, callback) {    if (!isFederated) return;    let inboxFragment = inbox.replace("https://" + targetDomain, "");    // get the private key @@ -264,7 +264,7 @@ function signAndSend(message, eventID, targetDomain, inbox, callback) {  // this function sends something to the timeline of every follower in the followers array  // it's also an unlisted public message, meaning non-followers can see the message if they look at  // the profile but it doesn't spam federated timelines -function broadcastCreateMessage(apObject, followers, eventID) { +export function broadcastCreateMessage(apObject, followers, eventID) {    if (!isFederated) return;    let guidCreate = crypto.randomBytes(16).toString("hex");    Event.findOne( @@ -325,7 +325,7 @@ function broadcastCreateMessage(apObject, followers, eventID) {  }  // sends an Announce for the apObject -function broadcastAnnounceMessage(apObject, followers, eventID) { +export function broadcastAnnounceMessage(apObject, followers, eventID) {    if (!isFederated) return;    let guidUpdate = crypto.randomBytes(16).toString("hex");    Event.findOne( @@ -386,7 +386,7 @@ function broadcastAnnounceMessage(apObject, followers, eventID) {  }  // sends an Update for the apObject -function broadcastUpdateMessage(apObject, followers, eventID) { +export function broadcastUpdateMessage(apObject, followers, eventID) {    if (!isFederated) return;    let guidUpdate = crypto.randomBytes(16).toString("hex");    // iterate over followers @@ -440,7 +440,7 @@ function broadcastUpdateMessage(apObject, followers, eventID) {    );  } -function broadcastDeleteMessage(apObject, followers, eventID, callback) { +export function broadcastDeleteMessage(apObject, followers, eventID, callback) {    callback = callback || function () {};    if (!isFederated) {      callback([]); @@ -521,7 +521,7 @@ function broadcastDeleteMessage(apObject, followers, eventID, callback) {  }  // this sends a message "to:" an individual fediverse user -function sendDirectMessage(apObject, actorId, eventID, callback) { +export function sendDirectMessage(apObject, actorId, eventID, callback) {    if (!isFederated) return;    callback = callback || function () {};    const guidCreate = crypto.randomBytes(16).toString("hex"); @@ -567,7 +567,7 @@ function sendDirectMessage(apObject, actorId, eventID, callback) {    );  } -function sendAcceptMessage(thebody, eventID, targetDomain, callback) { +export function sendAcceptMessage(thebody, eventID, targetDomain, callback) {    if (!isFederated) return;    callback = callback || function () {};    const guid = crypto.randomBytes(16).toString("hex"); @@ -1185,7 +1185,7 @@ function _handleCreateNoteComment(req, res) {    } // end public message  } -function processInbox(req, res) { +export function processInbox(req, res) {    if (!isFederated) return res.sendStatus(404);    try {      // if a Follow activity hits the inbox @@ -1251,7 +1251,7 @@ function processInbox(req, res) {    }  } -function createWebfinger(eventID, domain) { +export function createWebfinger(eventID, domain) {    return {      subject: `acct:${eventID}@${domain}`, @@ -1264,20 +1264,3 @@ function createWebfinger(eventID, domain) {      ],    };  } - -module.exports = { -  processInbox, -  sendAcceptMessage, -  sendDirectMessage, -  broadcastAnnounceMessage, -  broadcastUpdateMessage, -  broadcastDeleteMessage, -  broadcastCreateMessage, -  signAndSend, -  createActivityPubActor, -  updateActivityPubActor, -  createActivityPubEvent, -  updateActivityPubEvent, -  createFeaturedPost, -  createWebfinger, -}; @@ -1,10 +1,7 @@ -const express = require("express"); -const path = require("path"); -const session = require("express-session"); -const cors = require("cors"); -const routes = require("./routes"); -const hbs = require("express-handlebars"); -const bodyParser = require("body-parser"); +import express from "express"; +import routes from "./routes.js"; +import hbs from "express-handlebars"; +import bodyParser from "body-parser";  const app = express(); @@ -50,4 +47,4 @@ app.use(bodyParser.json({ type: "application/activity+json" })); // support json  app.use(bodyParser.urlencoded({ extended: true }));  app.use("/", routes); -module.exports = app; +export default app; diff --git a/src/config/api-example.js b/src/config/api-example.js deleted file mode 100644 index 493b9d6..0000000 --- a/src/config/api-example.js +++ /dev/null @@ -1,8 +0,0 @@ -// Which of these fields are used depends on the 'mailService' config entry in config/domain.js -module.exports = { -  sendgrid: "", // If using SendGrid, the Sendgrid API key goes here -  smtpServer: "", // If using Nodemailer, your SMTP server hostname goes here -  smtpPort: "", // If using Nodemailer, your SMTP server port goes here -  smtpUsername: "", // If using Nodemailer, your SMTP server username goes here -  smtpPassword: "", // If using Nodemailer, your SMTP password goes here -}; diff --git a/src/config/database-docker.js b/src/config/database-docker.js deleted file mode 100644 index 96c987d..0000000 --- a/src/config/database-docker.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { -  url: "mongodb://mongo:27017/gathio", // For dockerised MongoDB connection -}; diff --git a/src/config/database-example.js b/src/config/database-example.js deleted file mode 100644 index ca7bdcc..0000000 --- a/src/config/database-example.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { -  url: "mongodb://localhost:27017/gathio", // For local MongoDB connection -}; diff --git a/src/config/domain-example.js b/src/config/domain-example.js deleted file mode 100644 index abac094..0000000 --- a/src/config/domain-example.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { -  // Your domain goes here. If there is a port it should be 'domain:port', but otherwise just 'domain' -  domain: "localhost:3000", -  port: "3000", -  email: "contact@example.com", -  mailService: "nodemailer", // Which mail service to use to send emails to attendees. Options are 'nodemailer' or 'sendgrid'. Configure settings for the mail service in config/api.js.z -  sitename: "gathio", -  isFederated: true, -  // If left blank, this defaults to https://yourdomain.com/images/gathio-email-logo.gif. Set a full URL here to change it to your own logo (or just change the file itself) -  logo_url: "", -  // Show a Ko-Fi box to donate money to Raphael Kabo (Gathio's creator) on the front page -  showKofi: false, -}; diff --git a/src/config/gathio.service b/src/config/gathio.service deleted file mode 100644 index 447d44f..0000000 --- a/src/config/gathio.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=GathIO -After=network.target - -[Service] -Type=simple -User=gathio -WorkingDirectory=/srv/gathio -ExecStart=/usr/bin/npm start -Restart=on-failure - -[Install] -WantedBy=multi-user.target diff --git a/src/helpers.js b/src/helpers.js index 9b7559f..305187f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,14 +1,14 @@ -const domain = require("./config/domain.js").domain; -const siteName = require("./config/domain.js").sitename; - -const mongoose = require("mongoose"); -const Log = mongoose.model("Log"); -var moment = require("moment-timezone"); -const icalGenerator = require("ical-generator"); +import moment from "moment-timezone"; +import icalGenerator from "ical-generator"; +import Log from "./models/Log.js"; +import { getConfig } from "./lib/config.js"; +const config = getConfig(); +const domain = config.general.domain; +const siteName = config.general.site_name;  // LOGGING -function addToLog(process, status, message) { +export function addToLog(process, status, message) {    let logEntry = new Log({      status: status,      process: process, @@ -20,7 +20,7 @@ function addToLog(process, status, message) {    });  } -function exportIcal(events, calendarName) { +export function exportIcal(events, calendarName) {    // Create a new icalGenerator... generator    const cal = icalGenerator({      name: calendarName || siteName, @@ -52,8 +52,3 @@ function exportIcal(events, calendarName) {    const string = cal.toString();    return string;  } - -module.exports = { -  addToLog, -  exportIcal, -}; diff --git a/src/lib/config.ts b/src/lib/config.ts new file mode 100644 index 0000000..d235145 --- /dev/null +++ b/src/lib/config.ts @@ -0,0 +1,56 @@ +import fs from "fs"; +import toml from "toml"; + +interface GathioConfig { +  general: { +    domain: string; +    port: string; +    email: string; +    site_name: string; +    is_federated: boolean; +    email_logo_url: string; +    show_kofi: boolean; +    mail_service: "nodemailer" | "sendgrid"; +  }; +  database: { +    mongodb_url: string; +  }; +  nodemailer?: { +    smtp_server: string; +    smtp_port: string; +    smtp_username: string; +    smtp_password: string; +  }; +  sendgrid?: { +    api_key: string; +  }; +} + +export const publicConfig = () => { +  const config = getConfig(); +  return { +    domain: config.general.domain, +    siteName: config.general.site_name, +    isFederated: config.general.is_federated, +    emailLogoUrl: config.general.email_logo_url, +    showKofi: config.general.show_kofi, +  }; +}; + +// Attempt to load our global config. Will stop the app if the config file +// cannot be read (there's no point trying to continue!) +export const getConfig = (): GathioConfig => { +  try { +    const config = toml.parse( +      fs.readFileSync("./config/config.toml", "utf-8") +    ) as GathioConfig; +    return config; +  } catch { +    console.error( +      "\x1b[31mConfiguration file not found! Have you renamed './config/config-example.toml' to './config/config.toml'?" +    ); +    process.exit(1); +  } +}; + +export default getConfig; diff --git a/src/models/Event.js b/src/models/Event.js index 234084a..63a03ae 100755 --- a/src/models/Event.js +++ b/src/models/Event.js @@ -1,4 +1,4 @@ -const mongoose = require("mongoose"); +import mongoose from "mongoose";  const Attendees = new mongoose.Schema({    name: { @@ -256,4 +256,4 @@ const EventSchema = new mongoose.Schema({    activityPubMessages: [ActivityPubMessages],  }); -module.exports = mongoose.model("Event", EventSchema); +export default mongoose.model("Event", EventSchema); diff --git a/src/models/EventGroup.js b/src/models/EventGroup.js index 1a4ac64..f19e374 100755 --- a/src/models/EventGroup.js +++ b/src/models/EventGroup.js @@ -1,4 +1,4 @@ -const mongoose = require("mongoose"); +import mongoose from "mongoose";  const Subscriber = new mongoose.Schema({    email: { @@ -54,4 +54,4 @@ const EventGroupSchema = new mongoose.Schema({    subscribers: [Subscriber],  }); -module.exports = mongoose.model("EventGroup", EventGroupSchema); +export default mongoose.model("EventGroup", EventGroupSchema); diff --git a/src/models/Log.js b/src/models/Log.js index b048a45..f165900 100755 --- a/src/models/Log.js +++ b/src/models/Log.js @@ -1,4 +1,4 @@ -const mongoose = require("mongoose"); +import mongoose from "mongoose";  const LogSchema = new mongoose.Schema({    status: { @@ -23,4 +23,4 @@ const LogSchema = new mongoose.Schema({    },  }); -module.exports = mongoose.model("Log", LogSchema); +export default mongoose.model("Log", LogSchema); diff --git a/src/routes.js b/src/routes.js index 8c7f630..55436c9 100755 --- a/src/routes.js +++ b/src/routes.js @@ -1,49 +1,56 @@ -const fs = require("fs"); - -const express = require("express"); - -const mongoose = require("mongoose"); +import fs from "fs"; +import express from "express"; +import { customAlphabet } from "nanoid"; +import randomstring from "randomstring"; +import { getConfig } from "./lib/config.js"; +import { addToLog, exportIcal } from "./helpers.js"; +import moment from "moment-timezone"; +import { marked } from "marked"; +import generateRSAKeypair from "generate-rsa-keypair"; +import crypto from "crypto"; +import request from "request"; +import niceware from "niceware"; +import ical from "ical"; +import sgMail from "@sendgrid/mail"; +import nodemailer from "nodemailer"; +import fileUpload from "express-fileupload"; +import Jimp from "jimp"; +import schedule from "node-schedule"; +import { +  createActivityPubActor, +  createActivityPubEvent, +  createFeaturedPost, +  createWebfinger, +  updateActivityPubActor, +  updateActivityPubEvent, +  broadcastCreateMessage, +  broadcastUpdateMessage, +  broadcastDeleteMessage, +  sendDirectMessage, +  processInbox, +} from "./activitypub.js"; +import Event from "./models/Event.js"; +import EventGroup from "./models/EventGroup.js"; +import path from "path"; + +const config = getConfig(); +const domain = config.general.domain; +const contactEmail = config.general.email; +const siteName = config.general.site_name; +const mailService = config.general.mail_service; +const siteLogo = config.general.email_logo_url; +const isFederated = config.general.is_federated || true; +const showKofi = config.general.show_kofi;  // This alphabet (used to generate all event, group, etc. IDs) is missing '-'  // because ActivityPub doesn't like it in IDs -const { customAlphabet } = require("nanoid");  const nanoid = customAlphabet(    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_",    21  ); -const randomstring = require("randomstring"); - -const { body, validationResult } = require("express-validator"); -  const router = express.Router(); -const Event = mongoose.model("Event"); -const EventGroup = mongoose.model("EventGroup"); -const addToLog = require("./helpers.js").addToLog; - -var moment = require("moment-timezone"); - -const marked = require("marked"); - -const generateRSAKeypair = require("generate-rsa-keypair"); -const crypto = require("crypto"); -const request = require("request"); -const niceware = require("niceware"); - -const domain = require("./config/domain.js").domain; -const contactEmail = require("./config/domain.js").email; -const mailService = require("./config/domain.js").mailService; -const siteName = require("./config/domain.js").sitename; -const siteLogo = require("./config/domain.js").logo_url; -let isFederated = require("./config/domain.js").isFederated; -let showKofi = require("./config/domain.js").showKofi; -// if the federation config isn't set, things are federated by default -if (isFederated === undefined) { -  isFederated = true; -} -const ap = require("./activitypub.js"); -  // Extra marked renderer (used to render plaintext event description for page metadata)  // Adapted from https://dustinpfister.github.io/2017/11/19/nodejs-marked/  // ? to ? helper @@ -85,31 +92,23 @@ function render_plain() {    return render;  } -const ical = require("ical"); -const { exportIcal } = require("./helpers.js"); - -const sgMail = require("@sendgrid/mail"); -const nodemailer = require("nodemailer"); - -const apiCredentials = require("./config/api.js"); -  let sendEmails = false;  let nodemailerTransporter; -if (mailService) { -  switch (mailService) { +if (config.general.mail_service) { +  switch (config.general.mail_service) {      case "sendgrid": -      sgMail.setApiKey(apiCredentials.sendgrid); +      sgMail.setApiKey(config.sendgrid?.api_key);        console.log("Sendgrid is ready to send emails.");        sendEmails = true;        break;      case "nodemailer":        nodemailerTransporter = nodemailer.createTransport({ -        host: apiCredentials.smtpServer, -        port: apiCredentials.smtpPort, +        host: config.nodemailer?.smtp_server, +        port: config.nodemailer?.smtp_port,          secure: false, // true for 465, false for other ports          auth: { -          user: apiCredentials.smtpUsername, // generated ethereal user -          pass: apiCredentials.smtpPassword, // generated ethereal password +          user: config.nodemailer?.smtp_username, +          pass: config.nodemailer?.smtp_password,          },        });        nodemailerTransporter.verify((error, success) => { @@ -128,12 +127,9 @@ if (mailService) {    }  } -const fileUpload = require("express-fileupload"); -var Jimp = require("jimp");  router.use(fileUpload());  // SCHEDULED DELETION -const schedule = require("node-schedule");  schedule.scheduleJob("59 23 * * *", function (fireDate) {    const too_old = moment.tz("Etc/UTC").subtract(7, "days").toDate();    console.log( @@ -166,24 +162,27 @@ schedule.scheduleJob("59 23 * * *", function (fireDate) {          };          if (event.image) { -          fs.unlink(global.appRoot + "/public/events/" + event.image, (err) => { -            if (err) { +          fs.unlink( +            path.join(process.cwd(), "/public/events/" + event.image), +            (err) => { +              if (err) { +                addToLog( +                  "deleteOldEvents", +                  "error", +                  "Attempt to delete event image for old event " + +                    event.id + +                    " failed with error: " + +                    err +                ); +              } +              // Image removed                addToLog(                  "deleteOldEvents",                  "error", -                "Attempt to delete event image for old event " + -                  event.id + -                  " failed with error: " + -                  err +                "Image deleted for old event " + event.id                );              } -            // Image removed -            addToLog( -              "deleteOldEvents", -              "error", -              "Image deleted for old event " + event.id -            ); -          }); +          );          }          // Check if event has ActivityPub fields          if (event.activityPubActor && event.activityPubEvent) { @@ -192,12 +191,12 @@ schedule.scheduleJob("59 23 * * *", function (fireDate) {            const jsonUpdateObject = JSON.parse(event.activityPubActor);            const jsonEventObject = JSON.parse(event.activityPubEvent);            // first broadcast AP messages, THEN delete from DB -          ap.broadcastDeleteMessage( +          broadcastDeleteMessage(              jsonUpdateObject,              event.followers,              event.id,              function (statuses) { -              ap.broadcastDeleteMessage( +              broadcastDeleteMessage(                  jsonEventObject,                  event.followers,                  event.id, @@ -274,7 +273,7 @@ router.get("/:eventID/featured", (req, res) => {      "@context": "https://www.w3.org/ns/activitystreams",      id: `https://${domain}/${eventID}/featured`,      type: "OrderedCollection", -    orderedItems: [ap.createFeaturedPost(eventID)], +    orderedItems: [createFeaturedPost(eventID)],    };    if (      req.headers.accept && @@ -367,11 +366,11 @@ router.get("/.well-known/webfinger", (req, res) => {            ) {              res                .header("Content-Type", "application/activity+json") -              .send(ap.createWebfinger(eventID, domain)); +              .send(createWebfinger(eventID, domain));            } else {              res                .header("Content-Type", "application/json") -              .send(ap.createWebfinger(eventID, domain)); +              .send(createWebfinger(eventID, domain));            }          }        }) @@ -938,7 +937,7 @@ router.post("/newevent", async (req, res) => {      usersCanComment: req.body.interactionCheckbox ? true : false,      maxAttendees: req.body.maxAttendees,      firstLoad: true, -    activityPubActor: ap.createActivityPubActor( +    activityPubActor: createActivityPubActor(        eventID,        domain,        pair.public, @@ -950,7 +949,7 @@ router.post("/newevent", async (req, res) => {        endUTC,        req.body.timezone      ), -    activityPubEvent: ap.createActivityPubEvent( +    activityPubEvent: createActivityPubEvent(        req.body.eventName,        startUTC,        endUTC, @@ -962,7 +961,7 @@ router.post("/newevent", async (req, res) => {        {          id: `https://${domain}/${eventID}/m/featuredPost`,          content: JSON.stringify( -          ap.createFeaturedPost( +          createFeaturedPost(              eventID,              req.body.eventName,              startUTC, @@ -1376,7 +1375,7 @@ router.post("/editevent/:eventID/:editToken", (req, res) => {              : null,            eventGroup: isPartOfEventGroup ? eventGroup._id : null,            activityPubActor: event.activityPubActor -            ? ap.updateActivityPubActor( +            ? updateActivityPubActor(                  JSON.parse(event.activityPubActor),                  req.body.eventDescription,                  req.body.eventName, @@ -1388,7 +1387,7 @@ router.post("/editevent/:eventID/:editToken", (req, res) => {                )              : null,            activityPubEvent: event.activityPubEvent -            ? ap.updateActivityPubEvent( +            ? updateActivityPubEvent(                  JSON.parse(event.activityPubEvent),                  req.body.eventName,                  req.body.startUTC, @@ -1463,17 +1462,17 @@ router.post("/editevent/:eventID/:editToken", (req, res) => {                    cc: "https://www.w3.org/ns/activitystreams#Public",                    content: `${diffText} See here: <a href="https://${domain}/${req.params.eventID}">https://${domain}/${req.params.eventID}</a>`,                  }; -                ap.broadcastCreateMessage(jsonObject, event.followers, eventID); +                broadcastCreateMessage(jsonObject, event.followers, eventID);                  // also broadcast an Update profile message to all followers so that at least Mastodon servers will update the local profile information                  const jsonUpdateObject = JSON.parse(event.activityPubActor); -                ap.broadcastUpdateMessage( +                broadcastUpdateMessage(                    jsonUpdateObject,                    event.followers,                    eventID                  );                  // also broadcast an Update/Event for any calendar apps that are consuming our Events                  const jsonEventObject = JSON.parse(event.activityPubEvent); -                ap.broadcastUpdateMessage( +                broadcastUpdateMessage(                    jsonEventObject,                    event.followers,                    eventID @@ -1495,7 +1494,7 @@ router.post("/editevent/:eventID/:editToken", (req, res) => {                      ],                    };                    // send direct message to user -                  ap.sendDirectMessage(jsonObject, attendee.id, eventID); +                  sendDirectMessage(jsonObject, attendee.id, eventID);                  }                }              }); @@ -1709,31 +1708,10 @@ router.post("/deleteimage/:eventID/:editToken", (req, res) => {              "This event doesn't have a linked image. What are you even doing"            );        } -      fs.unlink(global.appRoot + "/public/events/" + eventImage, (err) => { -        if (err) { -          res.status(500).send(err); -          addToLog( -            "deleteEventImage", -            "error", -            "Attempt to delete event image for event " + -              req.params.eventID + -              " failed with error: " + -              err -          ); -        } -        // Image removed -        addToLog( -          "deleteEventImage", -          "success", -          "Image for event " + req.params.eventID + " deleted" -        ); -        event.image = ""; -        event -          .save() -          .then((response) => { -            res.status(200).send("Success"); -          }) -          .catch((err) => { +      fs.unlink( +        path.join(process.cwd(), "/public/events/" + eventImage), +        (err) => { +          if (err) {              res.status(500).send(err);              addToLog(                "deleteEventImage", @@ -1743,8 +1721,32 @@ router.post("/deleteimage/:eventID/:editToken", (req, res) => {                  " failed with error: " +                  err              ); -          }); -      }); +          } +          // Image removed +          addToLog( +            "deleteEventImage", +            "success", +            "Image for event " + req.params.eventID + " deleted" +          ); +          event.image = ""; +          event +            .save() +            .then((response) => { +              res.status(200).send("Success"); +            }) +            .catch((err) => { +              res.status(500).send(err); +              addToLog( +                "deleteEventImage", +                "error", +                "Attempt to delete event image for event " + +                  req.params.eventID + +                  " failed with error: " + +                  err +              ); +            }); +        } +      );      }    });  }); @@ -1768,7 +1770,7 @@ router.post("/deleteevent/:eventID/:editToken", (req, res) => {          const guidUpdateObject = crypto.randomBytes(16).toString("hex");          const jsonUpdateObject = JSON.parse(event.activityPubActor);          // first broadcast AP messages, THEN delete from DB -        ap.broadcastDeleteMessage( +        broadcastDeleteMessage(            jsonUpdateObject,            event.followers,            req.params.eventID, @@ -1790,7 +1792,7 @@ router.post("/deleteevent/:eventID/:editToken", (req, res) => {                  // Delete image                  if (eventImage) {                    fs.unlink( -                    global.appRoot + "/public/events/" + eventImage, +                    path.join(process.cwd(), "/public/events/" + eventImage),                      (err) => {                        if (err) {                          res.send(err); @@ -1943,7 +1945,7 @@ router.post("/deleteeventgroup/:eventGroupID/:editToken", (req, res) => {              // Delete image              if (eventGroupImage) {                fs.unlink( -                global.appRoot + "/public/events/" + eventGroupImage, +                path.join(process.cwd(), "/public/events/" + eventGroupImage),                  (err) => {                    if (err) {                      res.send(err); @@ -2569,7 +2571,7 @@ router.post("/post/comment/:eventID", (req, res) => {              cc: "https://www.w3.org/ns/activitystreams#Public",              content: `<p>${req.body.commentAuthor} commented: ${req.body.commentContent}.</p><p><a href="https://${domain}/${req.params.eventID}/">See the full conversation here.</a></p>`,            }; -          ap.broadcastCreateMessage( +          broadcastCreateMessage(              jsonObject,              event.followers,              req.params.eventID @@ -2681,7 +2683,7 @@ router.post("/post/reply/:eventID/:commentID", (req, res) => {              cc: "https://www.w3.org/ns/activitystreams#Public",              content: `<p>${req.body.replyAuthor} commented: ${req.body.replyContent}</p><p><a href="https://${domain}/${req.params.eventID}/">See the full conversation here.</a></p>`,            }; -          ap.broadcastCreateMessage( +          broadcastCreateMessage(              jsonObject,              event.followers,              req.params.eventID @@ -2877,7 +2879,7 @@ router.post("/activitypub/inbox", (req, res) => {          const result = verifier.verify(publicKeyBuf, signatureBuf);          if (result) {            // actually process the ActivityPub message now that it's been verified -          ap.processInbox(req, res); +          processInbox(req, res);          } else {            return res.status(401).send("Signature could not be verified.");          } @@ -2896,4 +2898,4 @@ router.use(function (req, res, next) {  addToLog("startup", "success", "Started up successfully"); -module.exports = router; +export default router; diff --git a/src/start.js b/src/start.js index a6ecfbf..ca17862 100755 --- a/src/start.js +++ b/src/start.js @@ -1,13 +1,10 @@ -require("dotenv").config(); +import mongoose from "mongoose"; +import { getConfig } from "./lib/config.js"; +import app from "./app.js"; -const path = require("path"); +const config = getConfig(); -const mongoose = require("mongoose"); - -const databaseCredentials = require("./config/database.js"); -const port = require("./config/domain.js").port; - -mongoose.connect(databaseCredentials.url, { +mongoose.connect(config.database.mongodb_url, {    useNewUrlParser: true,    useUnifiedTopology: true,  }); @@ -21,15 +18,7 @@ mongoose.connection      console.log("Connection error: ${err.message}");    }); -require("./models/Event"); -require("./models/Log"); -require("./models/EventGroup"); - -const app = require("./app.js"); - -global.appRoot = path.resolve(__dirname); - -const server = app.listen(port, () => { +const server = app.listen(config.general.port, () => {    console.log(      `Welcome to gathio! The app is now running on http://localhost:${        server.address().port  | 
