use luxon instead of dayjs server side too

This commit is contained in:
lesion 2023-03-28 19:02:08 +02:00
parent f5604a03bc
commit fc52107bd9
No known key found for this signature in database
GPG key ID: 352918250B012177
12 changed files with 102 additions and 80 deletions

View file

@ -1,7 +1,7 @@
const { Collection, Filter, Event, Tag, Place } = require('../models/models') const { Collection, Filter, Event, Tag, Place } = require('../models/models')
const log = require('../../log') const log = require('../../log')
const dayjs = require('dayjs') const { DateTime } = require('luxon')
const { col: Col } = require('../../helpers') const { col: Col } = require('../../helpers')
const { Op, Sequelize } = require('sequelize') const { Op, Sequelize } = require('sequelize')
@ -21,7 +21,6 @@ const collectionController = {
// return events from collection // return events from collection
async getEvents (req, res) { async getEvents (req, res) {
const format = req.params.format || 'json'
const name = req.params.name const name = req.params.name
const collection = await Collection.findOne({ where: { name } }) const collection = await Collection.findOne({ where: { name } })
@ -33,7 +32,7 @@ const collectionController = {
if (!filters.length) { if (!filters.length) {
return res.json([]) return res.json([])
} }
const start = dayjs().unix() const start = DateTime.local().toUnixInteger()
const where = { const where = {
// do not include parent recurrent event // do not include parent recurrent event
recurrent: null, recurrent: null,

View file

@ -5,7 +5,7 @@ const fs = require('fs/promises')
const { Op } = require('sequelize') const { Op } = require('sequelize')
const linkifyHtml = require('linkify-html') const linkifyHtml = require('linkify-html')
const Sequelize = require('sequelize') const Sequelize = require('sequelize')
const dayjs = require('dayjs') const { DateTime } = require('luxon')
const helpers = require('../../helpers') const helpers = require('../../helpers')
const Col = helpers.col const Col = helpers.col
const notifier = require('../../notifier') const notifier = require('../../notifier')
@ -252,7 +252,7 @@ const eventController = {
where: { where: {
parentId: null, parentId: null,
is_visible: false, is_visible: false,
start_datetime: { [Op.gt]: dayjs().unix() } start_datetime: { [Op.gt]: DateTime.local().toUnixInteger() }
}, },
order: [['start_datetime', 'ASC']], order: [['start_datetime', 'ASC']],
include: [{ model: Tag, required: false }, Place] include: [{ model: Tag, required: false }, Place]
@ -532,7 +532,7 @@ const eventController = {
* @returns * @returns
*/ */
async _select({ async _select({
start = dayjs().unix(), start = DateTime.local().toUnixInteger(),
end, end,
query, query,
tags, tags,
@ -541,7 +541,8 @@ const eventController = {
show_multidate, show_multidate,
limit, limit,
page, page,
older }) { older,
reverse }) {
const where = { const where = {
// do not include _parent_ recurrent event // do not include _parent_ recurrent event
@ -611,7 +612,7 @@ const eventController = {
attributes: { attributes: {
exclude: ['likes', 'boost', 'userId', 'is_visible', 'createdAt', 'description', 'resources', 'recurrent', 'placeId', 'image_path'] 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: [ include: [
{ {
model: Tag, model: Tag,
@ -640,7 +641,7 @@ const eventController = {
*/ */
async select(req, res) { async select(req, res) {
const settings = res.locals.settings 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 end = req.query.end
const query = req.query.query const query = req.query.query
const tags = req.query.tags 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}"`) log.debug(`Create recurrent event [${e.id}] ${e.title}"`)
// prepare the new event occurrence copying the parent's properties
const event = { const event = {
parentId: e.id, parentId: e.id,
title: e.title, title: e.title,
@ -675,49 +678,57 @@ const eventController = {
placeId: e.placeId placeId: e.placeId
} }
const recurrent = e.recurrent const recurrentDetails = e.recurrent
const start_date = dayjs.unix(e.start_datetime) const parentStartDatetime = DateTime.fromSeconds(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
cursor = cursor.hour(start_date.hour()).minute(start_date.minute()).second(0) // cursor is when start to count
if (!frequency) { return } // 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 // each week or 2
if (frequency[1] === 'w') { if (frequency[1] === 'w') {
cursor = cursor.day(start_date.day()) cursor = cursor.set({ weekday: parentStartDatetime.weekday }) //day(parentStartDatetime.day())
if (cursor.isBefore(startAt)) { if (cursor < startAt) {
cursor = cursor.add(7, 'day') cursor = cursor.plus({ days: 7 * Number(frequency[0]) })
}
if (frequency[0] === '2' && !firstOccurrence) {
cursor = cursor.add(7, 'day')
} }
} else if (frequency === '1m') { } else if (frequency === '1m') {
if (type === 'ordinal') { if (type === 'ordinal') {
cursor = cursor.date(start_date.date()) cursor = cursor.set({ day: parentStartDatetime.day })
if (cursor.isBefore(startAt)) { if (cursor< startAt) {
cursor = cursor.add(1, 'month') cursor = cursor.plus({ months: 1 })
} }
} else { // weekday } else { // weekday
// get weekday // get weekday
// get recurrent freq details // get recurrent freq details
cursor = helpers.getWeekdayN(cursor, type, start_date.day()) cursor = helpers.getWeekdayN(cursor, type, parentStartDatetime.weekday)
if (cursor.isBefore(startAt)) { if (cursor< startAt) {
cursor = cursor.add(4, 'week') cursor = cursor.plus({ months: 1 })
cursor = helpers.getWeekdayN(cursor, type, start_date.day()) cursor = helpers.getWeekdayN(cursor, type, parentStartDatetime.weekday)
} }
} }
} }
log.debug(cursor) log.debug(cursor)
event.start_datetime = cursor.unix() event.start_datetime = cursor.toUnixInteger()
event.end_datetime = e.end_datetime ? event.start_datetime + duration : null event.end_datetime = e.end_datetime ? event.start_datetime + duration : null
try { try {
const newEvent = await Event.create(event) const newEvent = await Event.create(event)
if (e.tags) {
return newEvent.addTags(e.tags) return newEvent.addTags(e.tags)
} else {
return newEvent
}
} catch (e) { } catch (e) {
console.error(event) console.error(event)
log.error('[RECURRENT EVENT]', e) log.error('[RECURRENT EVENT]', e)
@ -727,7 +738,7 @@ const eventController = {
/** /**
* Create instances of recurrent events * Create instances of recurrent events
*/ */
async _createRecurrent(start_datetime = dayjs().unix()) { async _createRecurrent(start_datetime = DateTime.local().toUnixInteger()) {
// select recurrent events and its childs // select recurrent events and its childs
const events = await Event.findAll({ const events = await Event.findAll({
where: { is_visible: true, recurrent: { [Op.ne]: null } }, where: { is_visible: true, recurrent: { [Op.ne]: null } },
@ -740,9 +751,9 @@ const eventController = {
const creations = events.map(e => { const creations = events.map(e => {
if (e.child.length) { if (e.child.length) {
if (e.child.find(c => c.is_visible)) return 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) return Promise.all(creations)

View file

@ -2,7 +2,7 @@ const { Event, Place, Tag } = require('../models/models')
const { htmlToText } = require('html-to-text') const { htmlToText } = require('html-to-text')
const { Op, literal } = require('sequelize') const { Op, literal } = require('sequelize')
const moment = require('dayjs') const { DateTime } = require('luxon')
const ics = require('ics') const ics = require('ics')
const exportController = { const exportController = {
@ -13,8 +13,13 @@ const exportController = {
const places = req.query.places const places = req.query.places
const show_recurrent = !!req.query.show_recurrent const show_recurrent = !!req.query.show_recurrent
const opt = {
zone: res.locals.settings.instance_timezone,
locale: res.locals.settings.instance_locale
}
const where = {} const where = {}
const yesterday = moment().subtract('1', 'day').unix() const yesterday = DateTime.local(opt).minus({day: 1}).toUnixInteger()
if (tags && places) { 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`) { feed (_req, res, events, title = res.locals.settings.title, link = `${res.locals.settings.baseurl}/feed/rss`) {
const settings = res.locals.settings 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.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 })
}, },
/** /**

View file

@ -88,7 +88,7 @@ const pluginController = {
try { try {
const plugin = require(pluginFile) const plugin = require(pluginFile)
const name = plugin.configuration.name const name = plugin.configuration.name
console.log(`Found plugin '${name}'`) log.info(`Found plugin '${name}'`)
pluginController.plugins.push(plugin) pluginController.plugins.push(plugin)
if (settingsController.settings['plugin_' + name]) { if (settingsController.settings['plugin_' + name]) {
const pluginSetting = settingsController.settings['plugin_' + name] const pluginSetting = settingsController.settings['plugin_' + name]

View file

@ -1,6 +1,6 @@
const Email = require('email-templates') const Email = require('email-templates')
const path = require('path') const path = require('path')
const moment = require('dayjs') const { DateTime } = require('luxon')
const settingsController = require('./controller/settings') const settingsController = require('./controller/settings')
const log = require('../log') const log = require('../log')
const { Task, TaskManager } = require('../taskManager') const { Task, TaskManager } = require('../taskManager')
@ -52,6 +52,11 @@ const mail = {
transport: settings.smtp || {} transport: settings.smtp || {}
}) })
const opt = {
zone: settings.instance_timezone,
locale
}
const msg = { const msg = {
template, template,
message: { message: {
@ -61,7 +66,7 @@ const mail = {
...locals, ...locals,
locale, locale,
config: { title: settings.title, baseurl: settings.baseurl, description: settings.description, admin_email: settings.admin_email }, 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) return email.send(msg)

View file

@ -1,13 +1,7 @@
const config = require('../../config') const config = require('../../config')
const { htmlToText } = require('html-to-text') const { htmlToText } = require('html-to-text')
const dayjs = require('dayjs') const { DateTime } = require('luxon')
const timezone = require('dayjs/plugin/timezone')
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
dayjs.extend(timezone)
// class Event extends Model {}
module.exports = (sequelize, DataTypes) => { module.exports = (sequelize, DataTypes) => {
const Event = sequelize.define('event', { const Event = sequelize.define('event', {
id: { id: {
@ -40,12 +34,18 @@ module.exports = (sequelize, DataTypes) => {
boost: { type: DataTypes.JSON, defaultValue: [] } 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 tags = this.tags && this.tags.map(t => t.tag.replace(/[ #]/g, '_'))
const plainDescription = htmlToText(this.description && this.description.replace('\n', '').slice(0, 1000)) const plainDescription = htmlToText(this.description && this.description.replace('\n', '').slice(0, 1000))
const content = ` const content = `
📍 ${this.place && this.place.name} 📍 ${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} ${plainDescription}
` `
@ -68,8 +68,8 @@ module.exports = (sequelize, DataTypes) => {
name: this.title, name: this.title,
url: `${config.baseurl}/event/${this.slug || this.id}`, url: `${config.baseurl}/event/${this.slug || this.id}`,
type: 'Event', type: 'Event',
startTime: dayjs.unix(this.start_datetime).tz().locale(locale).format(), startTime: DateTime.fromSeconds(this.start_datetime, opt).toISO(),
...( this.end_datetime ? { endTime : dayjs.unix(this.end_datetime).tz().locale(locale).format() } : {} ), ...( this.end_datetime ? { endTime : DateTime.fromSeconds(this.end_datetime, opt).toISO() } : {} ),
location: { location: {
name: this.place.name, name: this.place.name,
address: this.place.address, address: this.place.address,
@ -82,7 +82,7 @@ module.exports = (sequelize, DataTypes) => {
name: '#' + tag, name: '#' + tag,
href: `${config.baseurl}/tag/${tag}` href: `${config.baseurl}/tag/${tag}`
})), })),
published: dayjs(this.createdAt).utc().format(), published: this.createdAt,
attributedTo: `${config.baseurl}/federation/u/${username}`, attributedTo: `${config.baseurl}/federation/u/${username}`,
to: ['https://www.w3.org/ns/activitystreams#Public'], to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: [`${config.baseurl}/federation/u/${username}/followers`], cc: [`${config.baseurl}/federation/u/${username}/followers`],

View file

@ -89,9 +89,7 @@ const Helpers = {
to: ['https://www.w3.org/ns/activitystreams#Public'], to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: [...recipients[sharedInbox], `${config.baseurl}/federation/u/${settingsController.settings.instance_name}/followers`], cc: [...recipients[sharedInbox], `${config.baseurl}/federation/u/${settingsController.settings.instance_name}/followers`],
actor: `${config.baseurl}/federation/u/${settingsController.settings.instance_name}`, actor: `${config.baseurl}/federation/u/${settingsController.settings.instance_name}`,
object: event.toAP(settingsController.settings.instance_name, object: event.toAP(settingsController.settings, recipients[sharedInbox])
settingsController.settings.instance_locale,
recipients[sharedInbox])
} }
body['@context'] = [ body['@context'] = [
'https://www.w3.org/ns/activitystreams', 'https://www.w3.org/ns/activitystreams',

View file

@ -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] }) const event = await Event.findByPk(req.params.event_id, { include: [User, Tag, Place] })
if (!event) { return res.status(404).send('Not found') } 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'] = [ eventAp['@context'] = [
"https://www.w3.org/ns/activitystreams" "https://www.w3.org/ns/activitystreams"
] ]

View file

@ -3,10 +3,8 @@ const { Event, Place, APUser, Tag } = require('../api/models/models')
const escape = require('lodash/escape') const escape = require('lodash/escape')
const config = require('../config') const config = require('../config')
const log = require('../log') const log = require('../log')
const utc = require('dayjs/plugin/utc')
const dayjs = require('dayjs')
const settingsController = require('../api/controller/settings') const settingsController = require('../api/controller/settings')
dayjs.extend(utc) const { DateTime } = require('luxon')
module.exports = { module.exports = {
get (req, res) { get (req, res) {
@ -132,9 +130,9 @@ module.exports = {
type: 'Create', type: 'Create',
to: ['https://www.w3.org/ns/activitystreams#Public'], to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: [`${settings.baseurl}/federation/u/${name}/followers`], cc: [`${settings.baseurl}/federation/u/${name}/followers`],
published: dayjs(e.createdAt).utc().format(), published: e.createdAt,
actor: `${settings.baseurl}/federation/u/${name}`, actor: `${settings.baseurl}/federation/u/${name}`,
object: e.toAP(name, settings.instance_locale) object: e.toAP(settings)
})) }))
} }
}) })

View file

@ -100,9 +100,6 @@ module.exports = {
footerLinks: settings.footerLinks, footerLinks: settings.footerLinks,
about: settings.about 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() next()
}, },
@ -254,16 +251,17 @@ module.exports = {
let cursor let cursor
if (n === -1) { if (n === -1) {
cursor = date.endOf('month') cursor = date.endOf('month')
cursor = cursor.day(weekday) cursor = cursor.set({ weekday })
if (cursor.month() !== date.month()) { if (cursor.month !== date.month) {
cursor = cursor.subtract(1, 'week') cursor = cursor.minus({ days: 7 })
} }
} else { } else {
cursor = date.startOf('month') cursor = date.startOf('month')
cursor = cursor.add(cursor.day() <= date.day() ? n - 1 : n, 'week') 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) log.debug(cursor)
return cursor return cursor
}, },

View file

@ -1,12 +1,12 @@
const config = require('../server/config') const config = require('../server/config')
const db = require('./api/models/index') const db = require('./api/models/index')
const log = require('../server/log') const log = require('../server/log')
const { Settings } = require('luxon')
db.initialize() db.initialize()
const settingsController = require('./api/controller/settings') const settingsController = require('./api/controller/settings')
const initialize = { const initialize = {
// close connections/port/unix socket // close connections/port/unix socket
async shutdown (exit = true) { async shutdown (exit = true) {
@ -26,9 +26,6 @@ const initialize = {
}, },
async start () { async start () {
const dayjs = require('dayjs')
const timezone = require('dayjs/plugin/timezone')
dayjs.extend(timezone)
if (config.status == 'CONFIGURED') { if (config.status == 'CONFIGURED') {
await db.sequelize.authenticate() await db.sequelize.authenticate()
log.debug('Running migrations') log.debug('Running migrations')
@ -57,10 +54,11 @@ const initialize = {
await settingsController.load() await settingsController.load()
} }
dayjs.tz.setDefault(settingsController.settings.instance_timezone) Settings.defaultLocale = settingsController.settings.instance_locale
Settings.defaultZone = settingsController.settings.instance_timezone
let TaskManager 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 = require('../server/taskManager').TaskManager
TaskManager.start() TaskManager.start()
} }