mirror of
https://github.com/nfonteyne/octane-website.git
synced 2026-09-03 23:24:48 +02:00
update: add calendar to app
This commit is contained in:
parent
b17715eaff
commit
08717c24bc
29 changed files with 1478 additions and 313 deletions
30
src/lib/calendarDates.js
Normal file
30
src/lib/calendarDates.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Truncates to minute precision so repeated ingests of "the same" slot land
|
||||
// on the same instant even though n8n's timestamps carry sub-minute noise
|
||||
// that differs run to run (the DB's UNIQUE(lower, upper) constraint depends
|
||||
// on this). Returns a Date (not a string) — pg accepts Date directly for a
|
||||
// timestamptz column.
|
||||
function normalizeISO(iso) {
|
||||
const d = new Date(iso);
|
||||
d.setSeconds(0, 0);
|
||||
return d;
|
||||
}
|
||||
|
||||
// Calendar date (YYYY-MM-DD) of the given instant in Europe/Paris, so e.g. a
|
||||
// 18:30 CEST slot stays on the correct local day regardless of what UTC day
|
||||
// it falls on.
|
||||
function slotDateParis(iso) {
|
||||
return new Date(iso).toLocaleDateString('en-CA', { timeZone: 'Europe/Paris' });
|
||||
}
|
||||
|
||||
const WEEKDAY_TO_NUMBER = { Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6, Sun: 7 };
|
||||
|
||||
// Monday=1..Sunday=7, from the Europe/Paris-local weekday name. Falls back
|
||||
// to the instant's own (UTC) getDay() if the locale parsing ever fails.
|
||||
function dayOfWeekParis(iso) {
|
||||
const date = new Date(iso);
|
||||
const short = date.toLocaleDateString('en-US', { timeZone: 'Europe/Paris', weekday: 'short' }).slice(0, 3);
|
||||
const dow = WEEKDAY_TO_NUMBER[short];
|
||||
return dow === undefined ? date.getDay() || 7 : dow;
|
||||
}
|
||||
|
||||
module.exports = { normalizeISO, slotDateParis, dayOfWeekParis };
|
||||
23
src/lib/calendarWebhookAuth.js
Normal file
23
src/lib/calendarWebhookAuth.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
const crypto = require('crypto');
|
||||
const config = require('../config');
|
||||
|
||||
// Protects the two endpoints n8n calls directly (server-to-server, no
|
||||
// browser session available) — a shared secret header instead of the
|
||||
// session-based auth the rest of /api uses. Constant-time compare since
|
||||
// this is a bearer-secret check.
|
||||
function calendarWebhookAuth(req, res, next) {
|
||||
const provided = req.header('X-Calendar-Webhook-Secret') || '';
|
||||
const expected = config.calendarWebhookSecret;
|
||||
|
||||
const providedBuf = Buffer.from(provided);
|
||||
const expectedBuf = Buffer.from(expected);
|
||||
const valid =
|
||||
providedBuf.length === expectedBuf.length && crypto.timingSafeEqual(providedBuf, expectedBuf);
|
||||
|
||||
if (!valid) {
|
||||
return res.status(401).json({ error: 'invalid_webhook_secret' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = calendarWebhookAuth;
|
||||
25
src/lib/calendarWorkflowState.js
Normal file
25
src/lib/calendarWorkflowState.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// In-memory only — resets on process restart, which is fine: it just tracks
|
||||
// whether the last "refresh availability" run is idle/running/done/failed,
|
||||
// not data that needs to survive a restart. Shared between the session-gated
|
||||
// routes (src/routes/calendar.js) and the n8n-facing webhook routes
|
||||
// (src/routes/calendarWebhooks.js) so both can read/update the same state
|
||||
// without a require cycle between those two route files.
|
||||
let state = { status: 'idle', triggeredAt: null, message: null, node: null };
|
||||
|
||||
function getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
function setRunning() {
|
||||
state = { status: 'running', triggeredAt: new Date().toISOString(), message: null, node: null };
|
||||
}
|
||||
|
||||
function setSuccess() {
|
||||
state = { ...state, status: 'success', message: null, node: null };
|
||||
}
|
||||
|
||||
function setError(message, node) {
|
||||
state = { ...state, status: 'error', message: message || 'Unknown error', node: node || null };
|
||||
}
|
||||
|
||||
module.exports = { getState, setRunning, setSuccess, setError };
|
||||
Loading…
Add table
Add a link
Reference in a new issue