From fc52107bd9b88bbba3f846e59b605b30d32d6457 Mon Sep 17 00:00:00 2001 From: lesion Date: Tue, 28 Mar 2023 19:02:08 +0200 Subject: [PATCH] use luxon instead of dayjs server side too --- plugins/time.js | 2 +- server/api/controller/collection.js | 5 +- server/api/controller/event.js | 81 ++++++++++++++++------------- server/api/controller/export.js | 21 ++++++-- server/api/controller/plugins.js | 2 +- server/api/mail.js | 9 +++- server/api/models/event.js | 24 ++++----- server/federation/helpers.js | 4 +- server/federation/index.js | 2 +- server/federation/users.js | 8 ++- server/helpers.js | 14 +++-- server/initialize.server.js | 10 ++-- 12 files changed, 102 insertions(+), 80 deletions(-) diff --git a/plugins/time.js b/plugins/time.js index 58c9825e..1097534a 100644 --- a/plugins/time.js +++ b/plugins/time.js @@ -99,7 +99,7 @@ export default ({ app, store }, inject) => { const opt = { zone: store.state.settings.instance_timezone, locale: app.i18n.locale || store.state.settings.instance_locale - } + } return DateTime.fromSeconds(timestamp, opt).toRelative() }, diff --git a/server/api/controller/collection.js b/server/api/controller/collection.js index b00493a0..cfcf0421 100644 --- a/server/api/controller/collection.js +++ b/server/api/controller/collection.js @@ -1,7 +1,7 @@ const { Collection, Filter, Event, Tag, Place } = require('../models/models') const log = require('../../log') -const dayjs = require('dayjs') +const { DateTime } = require('luxon') const { col: Col } = require('../../helpers') const { Op, Sequelize } = require('sequelize') @@ -21,7 +21,6 @@ const collectionController = { // return events from collection async getEvents (req, res) { - const format = req.params.format || 'json' const name = req.params.name const collection = await Collection.findOne({ where: { name } }) @@ -33,7 +32,7 @@ const collectionController = { if (!filters.length) { return res.json([]) } - const start = dayjs().unix() + const start = DateTime.local().toUnixInteger() const where = { // do not include parent recurrent event recurrent: null, diff --git a/server/api/controller/event.js b/server/api/controller/event.js index be3163f7..ebefac6b 100644 --- a/server/api/controller/event.js +++ b/server/api/controller/event.js @@ -5,7 +5,7 @@ const fs = require('fs/promises') const { Op } = require('sequelize') const linkifyHtml = require('linkify-html') const Sequelize = require('sequelize') -const dayjs = require('dayjs') +const { DateTime } = require('luxon') const helpers = require('../../helpers') const Col = helpers.col const notifier = require('../../notifier') @@ -252,7 +252,7 @@ const eventController = { where: { parentId: null, is_visible: false, - start_datetime: { [Op.gt]: dayjs().unix() } + start_datetime: { [Op.gt]: DateTime.local().toUnixInteger() } }, order: [['start_datetime', 'ASC']], include: [{ model: Tag, required: false }, Place] @@ -532,7 +532,7 @@ const eventController = { * @returns */ async _select({ - start = dayjs().unix(), + start = DateTime.local().toUnixInteger(), end, query, tags, @@ -541,7 +541,8 @@ const eventController = { show_multidate, limit, page, - older }) { + older, + reverse }) { const where = { // do not include _parent_ recurrent event @@ -611,7 +612,7 @@ const eventController = { attributes: { exclude: ['likes', 'boost', 'userId', 'is_visible', 'createdAt', 'description', 'resources', 'recurrent', 'placeId', 'image_path'] }, - order: [['start_datetime', older ? 'DESC' : 'ASC']], + order: [['start_datetime', reverse ? 'DESC' : 'ASC']], include: [ { model: Tag, @@ -640,7 +641,7 @@ const eventController = { */ async select(req, res) { const settings = res.locals.settings - const start = req.query.start || dayjs().unix() + const start = req.query.start || DateTime.local().toUnixInteger() const end = req.query.end const query = req.query.query const tags = req.query.tags @@ -661,10 +662,12 @@ const eventController = { }, /** - * Ensure we have the next instance of a recurrent event + * Ensure we have the next occurrence of a recurrent event */ - async _createRecurrentOccurrence(e, startAt, firstOccurrence) { + async _createRecurrentOccurrence(e, startAt = DateTime.local(), firstOccurrence = true) { log.debug(`Create recurrent event [${e.id}] ${e.title}"`) + + // prepare the new event occurrence copying the parent's properties const event = { parentId: e.id, title: e.title, @@ -675,49 +678,57 @@ const eventController = { placeId: e.placeId } - const recurrent = e.recurrent - const start_date = dayjs.unix(e.start_datetime) - let cursor = start_date > startAt ? start_date : startAt - startAt = cursor - const duration = e.end_datetime ? e.end_datetime-e.start_datetime : 0 - const frequency = recurrent.frequency - const type = recurrent.type + const recurrentDetails = e.recurrent + const parentStartDatetime = DateTime.fromSeconds(e.start_datetime) - cursor = cursor.hour(start_date.hour()).minute(start_date.minute()).second(0) - if (!frequency) { return } + // cursor is when start to count + // sets it to + let cursor = parentStartDatetime > startAt ? parentStartDatetime : startAt + startAt = cursor + + const duration = e.end_datetime ? e.end_datetime-e.start_datetime : 0 + const frequency = recurrentDetails.frequency + const type = recurrentDetails.type + if (!frequency) { + log.warn(`Recurrent event ${e.id} - ${e.title} does not have a frequency specified`) + return + } + + cursor = cursor.set({ hour: parentStartDatetime.hour, minute: parentStartDatetime.minute, second: 0 }) // each week or 2 if (frequency[1] === 'w') { - cursor = cursor.day(start_date.day()) - if (cursor.isBefore(startAt)) { - cursor = cursor.add(7, 'day') - } - if (frequency[0] === '2' && !firstOccurrence) { - cursor = cursor.add(7, 'day') + cursor = cursor.set({ weekday: parentStartDatetime.weekday }) //day(parentStartDatetime.day()) + if (cursor < startAt) { + cursor = cursor.plus({ days: 7 * Number(frequency[0]) }) } } else if (frequency === '1m') { if (type === 'ordinal') { - cursor = cursor.date(start_date.date()) + cursor = cursor.set({ day: parentStartDatetime.day }) - if (cursor.isBefore(startAt)) { - cursor = cursor.add(1, 'month') + if (cursor< startAt) { + cursor = cursor.plus({ months: 1 }) } } else { // weekday // get weekday // get recurrent freq details - cursor = helpers.getWeekdayN(cursor, type, start_date.day()) - if (cursor.isBefore(startAt)) { - cursor = cursor.add(4, 'week') - cursor = helpers.getWeekdayN(cursor, type, start_date.day()) + cursor = helpers.getWeekdayN(cursor, type, parentStartDatetime.weekday) + if (cursor< startAt) { + cursor = cursor.plus({ months: 1 }) + cursor = helpers.getWeekdayN(cursor, type, parentStartDatetime.weekday) } } } log.debug(cursor) - event.start_datetime = cursor.unix() + event.start_datetime = cursor.toUnixInteger() event.end_datetime = e.end_datetime ? event.start_datetime + duration : null try { const newEvent = await Event.create(event) - return newEvent.addTags(e.tags) + if (e.tags) { + return newEvent.addTags(e.tags) + } else { + return newEvent + } } catch (e) { console.error(event) log.error('[RECURRENT EVENT]', e) @@ -727,7 +738,7 @@ const eventController = { /** * Create instances of recurrent events */ - async _createRecurrent(start_datetime = dayjs().unix()) { + async _createRecurrent(start_datetime = DateTime.local().toUnixInteger()) { // select recurrent events and its childs const events = await Event.findAll({ where: { is_visible: true, recurrent: { [Op.ne]: null } }, @@ -740,9 +751,9 @@ const eventController = { const creations = events.map(e => { if (e.child.length) { if (e.child.find(c => c.is_visible)) return - return eventController._createRecurrentOccurrence(e, dayjs.unix(e.child[0].start_datetime + 1), false) + return eventController._createRecurrentOccurrence(e, DateTime.fromSeconds(e.child[0].start_datetime + 1), false) } - return eventController._createRecurrentOccurrence(e, dayjs(), true) + return eventController._createRecurrentOccurrence(e, DateTime.local(), true) }) return Promise.all(creations) diff --git a/server/api/controller/export.js b/server/api/controller/export.js index 08cfe1ef..caa020a6 100644 --- a/server/api/controller/export.js +++ b/server/api/controller/export.js @@ -2,7 +2,7 @@ const { Event, Place, Tag } = require('../models/models') const { htmlToText } = require('html-to-text') const { Op, literal } = require('sequelize') -const moment = require('dayjs') +const { DateTime } = require('luxon') const ics = require('ics') const exportController = { @@ -13,8 +13,13 @@ const exportController = { const places = req.query.places const show_recurrent = !!req.query.show_recurrent + const opt = { + zone: res.locals.settings.instance_timezone, + locale: res.locals.settings.instance_locale + } + const where = {} - const yesterday = moment().subtract('1', 'day').unix() + const yesterday = DateTime.local(opt).minus({day: 1}).toUnixInteger() if (tags && places) { @@ -69,8 +74,18 @@ const exportController = { feed (_req, res, events, title = res.locals.settings.title, link = `${res.locals.settings.baseurl}/feed/rss`) { const settings = res.locals.settings + + const opt = { + zone: settings.instance_timezone, + locale: settings.instance_locale + } + + function unixFormat (timestamp, format='EEEE d MMMM HH:mm') { + return DateTime.fromSeconds(timestamp, opt).toFormat(format) + } + res.type('application/rss+xml; charset=UTF-8') - res.render('feed/rss.pug', { events, settings, moment, title, link }) + res.render('feed/rss.pug', { events, settings, unixFormat, title, link }) }, /** diff --git a/server/api/controller/plugins.js b/server/api/controller/plugins.js index ea7ca532..cf904dc5 100644 --- a/server/api/controller/plugins.js +++ b/server/api/controller/plugins.js @@ -88,7 +88,7 @@ const pluginController = { try { const plugin = require(pluginFile) const name = plugin.configuration.name - console.log(`Found plugin '${name}'`) + log.info(`Found plugin '${name}'`) pluginController.plugins.push(plugin) if (settingsController.settings['plugin_' + name]) { const pluginSetting = settingsController.settings['plugin_' + name] diff --git a/server/api/mail.js b/server/api/mail.js index 5b546098..a96b39d0 100644 --- a/server/api/mail.js +++ b/server/api/mail.js @@ -1,6 +1,6 @@ const Email = require('email-templates') const path = require('path') -const moment = require('dayjs') +const { DateTime } = require('luxon') const settingsController = require('./controller/settings') const log = require('../log') const { Task, TaskManager } = require('../taskManager') @@ -52,6 +52,11 @@ const mail = { transport: settings.smtp || {} }) + const opt = { + zone: settings.instance_timezone, + locale + } + const msg = { template, message: { @@ -61,7 +66,7 @@ const mail = { ...locals, locale, config: { title: settings.title, baseurl: settings.baseurl, description: settings.description, admin_email: settings.admin_email }, - datetime: datetime => moment.unix(datetime).tz().locale(locale).format('ddd, D MMMM HH:mm') + datetime: timestamp => DateTime.fromSeconds(timestamp, opt).toFormat('EEEE, d MMMM HH:mm') } } return email.send(msg) diff --git a/server/api/models/event.js b/server/api/models/event.js index 362d9134..f29ed815 100644 --- a/server/api/models/event.js +++ b/server/api/models/event.js @@ -1,13 +1,7 @@ const config = require('../../config') const { htmlToText } = require('html-to-text') -const dayjs = require('dayjs') -const timezone = require('dayjs/plugin/timezone') -const utc = require('dayjs/plugin/utc') +const { DateTime } = require('luxon') -dayjs.extend(utc) -dayjs.extend(timezone) - -// class Event extends Model {} module.exports = (sequelize, DataTypes) => { const Event = sequelize.define('event', { id: { @@ -40,12 +34,18 @@ module.exports = (sequelize, DataTypes) => { boost: { type: DataTypes.JSON, defaultValue: [] } }) - Event.prototype.toAP = function (username, locale, to = []) { + Event.prototype.toAP = function (settings, to = []) { + + const username = settings.instance_name + const opt = { + zone: settings.instance_timezone, + locale: settings.instance_locale + } const tags = this.tags && this.tags.map(t => t.tag.replace(/[ #]/g, '_')) const plainDescription = htmlToText(this.description && this.description.replace('\n', '').slice(0, 1000)) const content = ` 📍 ${this.place && this.place.name} - 📅 ${dayjs.unix(this.start_datetime).tz().locale(locale).format('dddd, D MMMM (HH:mm)')} + 📅 ${DateTime.fromSeconds(this.start_datetime).toFormat('EEEE, d MMMM (HH:mm)')} ${plainDescription} ` @@ -68,8 +68,8 @@ module.exports = (sequelize, DataTypes) => { name: this.title, url: `${config.baseurl}/event/${this.slug || this.id}`, type: 'Event', - startTime: dayjs.unix(this.start_datetime).tz().locale(locale).format(), - ...( this.end_datetime ? { endTime : dayjs.unix(this.end_datetime).tz().locale(locale).format() } : {} ), + startTime: DateTime.fromSeconds(this.start_datetime, opt).toISO(), + ...( this.end_datetime ? { endTime : DateTime.fromSeconds(this.end_datetime, opt).toISO() } : {} ), location: { name: this.place.name, address: this.place.address, @@ -82,7 +82,7 @@ module.exports = (sequelize, DataTypes) => { name: '#' + tag, href: `${config.baseurl}/tag/${tag}` })), - published: dayjs(this.createdAt).utc().format(), + published: this.createdAt, attributedTo: `${config.baseurl}/federation/u/${username}`, to: ['https://www.w3.org/ns/activitystreams#Public'], cc: [`${config.baseurl}/federation/u/${username}/followers`], diff --git a/server/federation/helpers.js b/server/federation/helpers.js index 5454b35d..01779f20 100644 --- a/server/federation/helpers.js +++ b/server/federation/helpers.js @@ -89,9 +89,7 @@ const Helpers = { to: ['https://www.w3.org/ns/activitystreams#Public'], cc: [...recipients[sharedInbox], `${config.baseurl}/federation/u/${settingsController.settings.instance_name}/followers`], actor: `${config.baseurl}/federation/u/${settingsController.settings.instance_name}`, - object: event.toAP(settingsController.settings.instance_name, - settingsController.settings.instance_locale, - recipients[sharedInbox]) + object: event.toAP(settingsController.settings, recipients[sharedInbox]) } body['@context'] = [ 'https://www.w3.org/ns/activitystreams', diff --git a/server/federation/index.js b/server/federation/index.js index 7a27a062..92363c78 100644 --- a/server/federation/index.js +++ b/server/federation/index.js @@ -34,7 +34,7 @@ router.get('/m/:event_id', async (req, res) => { const event = await Event.findByPk(req.params.event_id, { include: [User, Tag, Place] }) if (!event) { return res.status(404).send('Not found') } - const eventAp = event.toAP(settingsController.settings.instance_name, settingsController.settings.instance_locale) + const eventAp = event.toAP(settingsController.settings) eventAp['@context'] = [ "https://www.w3.org/ns/activitystreams" ] diff --git a/server/federation/users.js b/server/federation/users.js index b8ac73f4..00c9fb2f 100644 --- a/server/federation/users.js +++ b/server/federation/users.js @@ -3,10 +3,8 @@ const { Event, Place, APUser, Tag } = require('../api/models/models') const escape = require('lodash/escape') const config = require('../config') const log = require('../log') -const utc = require('dayjs/plugin/utc') -const dayjs = require('dayjs') const settingsController = require('../api/controller/settings') -dayjs.extend(utc) +const { DateTime } = require('luxon') module.exports = { get (req, res) { @@ -132,9 +130,9 @@ module.exports = { type: 'Create', to: ['https://www.w3.org/ns/activitystreams#Public'], cc: [`${settings.baseurl}/federation/u/${name}/followers`], - published: dayjs(e.createdAt).utc().format(), + published: e.createdAt, actor: `${settings.baseurl}/federation/u/${name}`, - object: e.toAP(name, settings.instance_locale) + object: e.toAP(settings) })) } }) diff --git a/server/helpers.js b/server/helpers.js index f25286ba..cd072e8e 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -100,9 +100,6 @@ module.exports = { footerLinks: settings.footerLinks, about: settings.about } - // set user locale - // res.locals.user_locale = settingsController.user_locale[res.locals.acceptedLocale] - dayjs.tz.setDefault(res.locals.settings.instance_timezone) next() }, @@ -254,16 +251,17 @@ module.exports = { let cursor if (n === -1) { cursor = date.endOf('month') - cursor = cursor.day(weekday) - if (cursor.month() !== date.month()) { - cursor = cursor.subtract(1, 'week') + cursor = cursor.set({ weekday }) + if (cursor.month !== date.month) { + cursor = cursor.minus({ days: 7 }) } } else { cursor = date.startOf('month') cursor = cursor.add(cursor.day() <= date.day() ? n - 1 : n, 'week') - cursor = cursor.day(weekday) + cursor = cursor.plus({ days: cursor.weekday <= date.weekday ? (n-1) * 7 : n * 7}) + cursor = cursor.set({ weekday }) } - cursor = cursor.hour(date.hour()).minute(date.minute()).second(0) + cursor = cursor.set({ hour: date.hour, minute: date.minute, second: 0 }) log.debug(cursor) return cursor }, diff --git a/server/initialize.server.js b/server/initialize.server.js index 4fa75409..0cdb5618 100644 --- a/server/initialize.server.js +++ b/server/initialize.server.js @@ -1,12 +1,12 @@ const config = require('../server/config') const db = require('./api/models/index') const log = require('../server/log') +const { Settings } = require('luxon') db.initialize() const settingsController = require('./api/controller/settings') - const initialize = { // close connections/port/unix socket async shutdown (exit = true) { @@ -26,9 +26,6 @@ const initialize = { }, async start () { - const dayjs = require('dayjs') - const timezone = require('dayjs/plugin/timezone') - dayjs.extend(timezone) if (config.status == 'CONFIGURED') { await db.sequelize.authenticate() log.debug('Running migrations') @@ -57,10 +54,11 @@ const initialize = { await settingsController.load() } - dayjs.tz.setDefault(settingsController.settings.instance_timezone) + Settings.defaultLocale = settingsController.settings.instance_locale + Settings.defaultZone = settingsController.settings.instance_timezone let TaskManager - if (config.status === 'READY') {// && process.env.NODE_ENV == 'production') { + if (config.status === 'READY' && process.env.NODE_ENV != 'test') { TaskManager = require('../server/taskManager').TaskManager TaskManager.start() }