mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 08:53:51 +00:00
Feat/mac (#109)
* chore: upgrade dependencies * feat/mac: draft pipeline * feat/mac: use public folder for transient files * feat/mac: set app fullscreen * feat/mac: cmd + , toggles menu * feat/mac: update readme * refact: easier login process * refact prevent style issues with safari * refact reduce css download * style: cleanup paginator design * style: studio clock is responsive * style: fix spacing style issues with safari
This commit is contained in:
@@ -5,8 +5,8 @@ import { OSCIntegration } from './integrations/Osc.js';
|
||||
import { HTTPIntegration } from './integrations/Http.js';
|
||||
import { cleanURL } from '../utils/url.js';
|
||||
import getRandomName from '../utils/getRandomName.js';
|
||||
import { stringFromMillis } from 'ontime-utils/time.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { generateId } from '../utils/generate_id.js';
|
||||
import { stringFromMillis } from '../utils/time.js';
|
||||
|
||||
/*
|
||||
* Class EventTimer adds functions specific to APP
|
||||
@@ -764,7 +764,7 @@ export class EventTimer extends Timer {
|
||||
const numEvents = events.length;
|
||||
|
||||
// is this the first event
|
||||
let first = this.numEvents === 0;
|
||||
const first = this.numEvents === 0;
|
||||
|
||||
// set general
|
||||
this._eventlist = events;
|
||||
@@ -822,7 +822,7 @@ export class EventTimer extends Timer {
|
||||
if (e.id === this.selectedEventId) {
|
||||
// handle reload selected
|
||||
// Reload data if running
|
||||
let type = this.selectedEventId === id && this._startedAt != null ? 'reload' : 'load';
|
||||
const type = this.selectedEventId === id && this._startedAt != null ? 'reload' : 'load';
|
||||
this.loadEvent(this.selectedEventIndex, type);
|
||||
} else if (e.id === this.nextEventId) {
|
||||
// roll needs to recalculate
|
||||
@@ -1178,7 +1178,7 @@ export class EventTimer extends Timer {
|
||||
|
||||
rollLoad() {
|
||||
const now = this._getCurrentTime();
|
||||
let prevLoaded = this.selectedEventId;
|
||||
const prevLoaded = this.selectedEventId;
|
||||
|
||||
// maybe roll has already been loaded
|
||||
if (this.secondaryTimer === null) {
|
||||
@@ -1428,7 +1428,7 @@ export class EventTimer extends Timer {
|
||||
* @param {string} message
|
||||
* @param {any} [payload]
|
||||
*/
|
||||
async sendOsc(message, payload = undefined) {
|
||||
async sendOsc(message, payload) {
|
||||
// Todo: add disabled osc check
|
||||
const reply = await this.osc.send(message, payload);
|
||||
if (!reply.success) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { stringFromMillis } from '../utils/time.js';
|
||||
|
||||
/*
|
||||
* Timer implements simple countdown timer functions
|
||||
* User needs to use setup function to be able to use
|
||||
*
|
||||
*/
|
||||
|
||||
import { stringFromMillis } from 'ontime-utils/time.js';
|
||||
|
||||
export class Timer {
|
||||
constructor() {
|
||||
this.clock = null;
|
||||
@@ -62,11 +62,7 @@ export class Timer {
|
||||
if (this._startedAt != null) {
|
||||
// update current timer
|
||||
this.current =
|
||||
this._startedAt +
|
||||
this.duration +
|
||||
this._pausedTotal +
|
||||
this._pausedInterval -
|
||||
now;
|
||||
this._startedAt + this.duration + this._pausedTotal + this._pausedInterval - now;
|
||||
}
|
||||
|
||||
// enable flag
|
||||
@@ -114,10 +110,7 @@ export class Timer {
|
||||
if (this._finishedAt) return this._finishedAt;
|
||||
|
||||
return Math.max(
|
||||
this._startedAt +
|
||||
this.duration +
|
||||
this._pausedInterval +
|
||||
this._pausedTotal,
|
||||
this._startedAt + this.duration + this._pausedInterval + this._pausedTotal,
|
||||
this._startedAt
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ export const DAY_TO_MS = 86400000;
|
||||
* @param {number} end - When does the event end
|
||||
* @returns {number} normalised time
|
||||
*/
|
||||
export const normaliseEndTime = (start, end) =>
|
||||
end < start ? end + DAY_TO_MS : end;
|
||||
export const normaliseEndTime = (start, end) => (end < start ? end + DAY_TO_MS : end);
|
||||
|
||||
/**
|
||||
* @description Sorts an array of objects by given property
|
||||
@@ -34,7 +33,7 @@ export const sortArrayByProperty = (arr, property) => {
|
||||
*/
|
||||
|
||||
export const replacePlaceholder = (str, values) => {
|
||||
for (let [k, v] of Object.entries(values)) {
|
||||
for (const [k, v] of Object.entries(values)) {
|
||||
str = str.replace(k, v);
|
||||
}
|
||||
return str;
|
||||
@@ -71,10 +70,7 @@ export const getSelectionByRoll = (arr, now) => {
|
||||
|
||||
// exit early if we are past the events
|
||||
const lastEvent = orderedEvents[orderedEvents.length - 1];
|
||||
const lastNormalEnd = normaliseEndTime(
|
||||
lastEvent.timeStart,
|
||||
lastEvent.timeEnd
|
||||
);
|
||||
const lastNormalEnd = normaliseEndTime(lastEvent.timeStart, lastEvent.timeEnd);
|
||||
if (now > lastNormalEnd) {
|
||||
return {
|
||||
nowIndex,
|
||||
@@ -163,14 +159,8 @@ export const getSelectionByRoll = (arr, now) => {
|
||||
* @returns {object} object with selection variables
|
||||
*/
|
||||
export const updateRoll = (currentTimers) => {
|
||||
const {
|
||||
selectedEventId,
|
||||
current,
|
||||
_finishAt,
|
||||
clock,
|
||||
secondaryTimer,
|
||||
_secondaryTarget,
|
||||
} = currentTimers;
|
||||
const { selectedEventId, current, _finishAt, clock, secondaryTimer, _secondaryTarget } =
|
||||
currentTimers;
|
||||
|
||||
// timers
|
||||
let updatedTimer = current;
|
||||
@@ -200,8 +190,7 @@ export const updateRoll = (currentTimers) => {
|
||||
// a) we just finished an event (finished was set to true)
|
||||
// b) we need to look for events
|
||||
// this could be caused by a secondary timer or event finished
|
||||
const secondaryRunning =
|
||||
updatedSecondaryTimer <= 0 && updatedSecondaryTimer != null;
|
||||
const secondaryRunning = updatedSecondaryTimer <= 0 && updatedSecondaryTimer != null;
|
||||
|
||||
if (isFinished || secondaryRunning) {
|
||||
// look for events
|
||||
|
||||
@@ -68,10 +68,7 @@ export const initiateOSC = (config) => {
|
||||
try {
|
||||
const t = parseInt(args);
|
||||
if (isNaN(t)) {
|
||||
global.timer.error(
|
||||
'RX',
|
||||
`OSC IN: delay time not recognised ${args}`
|
||||
);
|
||||
global.timer.error('RX', `OSC IN: delay time not recognised ${args}`);
|
||||
return;
|
||||
}
|
||||
global.timer.increment(t * 1000 * 60);
|
||||
@@ -82,7 +79,7 @@ export const initiateOSC = (config) => {
|
||||
case 'goto':
|
||||
try {
|
||||
const eventIndex = parseInt(args);
|
||||
if (isNaN(eventIndex) || eventIndex <= 0 || eventIndex == null) {
|
||||
if (isNaN(eventIndex) || eventIndex <= 0) {
|
||||
global.timer.error(
|
||||
'RX',
|
||||
`OSC IN: event index not recognised or out of range ${eventIndex}`
|
||||
@@ -96,10 +93,7 @@ export const initiateOSC = (config) => {
|
||||
case 'gotoid':
|
||||
console.log('calling gotoid with', args);
|
||||
if (args == null) {
|
||||
global.timer.error(
|
||||
'RX',
|
||||
`OSC IN: event id not recognised or out of range ${args}}`
|
||||
);
|
||||
global.timer.error('RX', `OSC IN: event id not recognised or out of range ${args}}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
import { data, db } from '../app.js';
|
||||
|
||||
// utils
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import {
|
||||
block as blockDef,
|
||||
delay as delayDef,
|
||||
event as eventDef,
|
||||
} from '../models/eventsDefinition.js';
|
||||
import { generateId } from '../utils/generate_id.js';
|
||||
|
||||
const MAX_EVENTS = 99;
|
||||
|
||||
async function _insertAt(entry, index) {
|
||||
// get events
|
||||
let events = data.events;
|
||||
let count = events.length;
|
||||
let order = entry.order;
|
||||
const events = data.events;
|
||||
const count = events.length;
|
||||
const order = entry.order;
|
||||
|
||||
// Remove order field from object
|
||||
delete entry.order;
|
||||
@@ -146,7 +146,7 @@ export const eventsPut = async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
let eventId = req.body.id;
|
||||
const eventId = req.body.id;
|
||||
if (!eventId) {
|
||||
res.status(400).send(`Object malformed: id missing`);
|
||||
return;
|
||||
@@ -191,8 +191,8 @@ export const eventsReorder = async (req, res) => {
|
||||
const { index, from, to } = req.body;
|
||||
|
||||
// get events
|
||||
let events = data.events;
|
||||
let idx = events.findIndex((e) => e.id === index, from);
|
||||
const events = data.events;
|
||||
const idx = events.findIndex((e) => e.id === index, from);
|
||||
|
||||
// Check if item is at given index
|
||||
if (idx !== from) {
|
||||
@@ -233,7 +233,7 @@ export const eventsApplyDelay = async (req, res) => {
|
||||
|
||||
try {
|
||||
// get events
|
||||
let events = data.events;
|
||||
const events = data.events;
|
||||
|
||||
// AUX
|
||||
let delayIndex = null;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
|
||||
import { data, db } from '../app.js';
|
||||
import { networkInterfaces } from 'os';
|
||||
import { fileHandler } from '../utils/parser.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { generateId } from '../utils/generate_id.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"nanoid": "^3.2.0",
|
||||
"node-osc": "6.1.12",
|
||||
"node-xlsx": "^0.21.0",
|
||||
"ontime-utils": "link: ../server/utils/",
|
||||
"passport": "^0.5.2",
|
||||
"passport-local": "~1.0.0",
|
||||
"socket.io": "^4.4.1",
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { generateId } from '../generate_id.js';
|
||||
|
||||
test('generate a valid 5 digit id', () => {
|
||||
const id = generateId();
|
||||
expect(id.length).toBe(5);
|
||||
});
|
||||
|
||||
test('generate 100 with less than 110 attempts', () => {
|
||||
const ids = new Set();
|
||||
let attempts = 1;
|
||||
while (ids.size < 100) {
|
||||
ids.add(generateId());
|
||||
attempts++;
|
||||
}
|
||||
|
||||
expect(attempts).toBeLessThan(105);
|
||||
});
|
||||
|
||||
describe('generate 1000 with less than 1020 attempts', () => {
|
||||
const ids = new Set();
|
||||
let attempts = 1;
|
||||
while (ids.size < 1000) {
|
||||
ids.add(generateId());
|
||||
attempts++;
|
||||
}
|
||||
|
||||
expect(attempts).toBeLessThan(1020);
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('1234567890abcdef', 5);
|
||||
|
||||
export const generateId = () => nanoid();
|
||||
+69
-69
@@ -2,8 +2,6 @@ import fs from 'fs';
|
||||
import xlsx from 'node-xlsx';
|
||||
import { event as eventDef } from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { excelDateStringToMillis } from 'ontime-utils/time.js';
|
||||
import { deleteFile, makeString, validateDuration } from './parserUtils.js';
|
||||
import {
|
||||
parseAliases_v1,
|
||||
@@ -14,86 +12,25 @@ import {
|
||||
parseSettings_v1,
|
||||
parseUserFields_v1,
|
||||
} from './parserUtils_v1.js';
|
||||
import { excelDateStringToMillis } from './time.js';
|
||||
import { generateId } from './generate_id.js';
|
||||
|
||||
export const EXCEL_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
export const JSON_MIME = 'application/json';
|
||||
export const MAX_EVENTS = 99;
|
||||
|
||||
/**
|
||||
* @description Middleware function that checks file type and calls relevant parser
|
||||
* @param {string} file - reference to file
|
||||
* @return {object} - parse result message
|
||||
*/
|
||||
export const fileHandler = async (file) => {
|
||||
let res = {};
|
||||
|
||||
// check which file type are we dealing with
|
||||
|
||||
if (file.endsWith('.xlsx')) {
|
||||
try {
|
||||
const excelData = xlsx
|
||||
.parse(file, { cellDates: true })
|
||||
.find(
|
||||
({ name }) => name.toLowerCase() === 'ontime' || name.toLowerCase() === 'event schedule'
|
||||
);
|
||||
|
||||
// we only look at worksheets called ontime or event schedule
|
||||
if (excelData?.data) {
|
||||
const dataFromExcel = await parseExcel_v1(excelData.data);
|
||||
res.data = await parseJson_v1(dataFromExcel);
|
||||
res.message = 'success';
|
||||
} else {
|
||||
console.log('Error: No sheets found named ontime or event schedule');
|
||||
res = {
|
||||
error: true,
|
||||
message: `No sheets found named ontime or event schedule`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
}
|
||||
}
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
// if json check version
|
||||
const rawdata = fs.readFileSync(file);
|
||||
let uploadedJson = null;
|
||||
|
||||
try {
|
||||
uploadedJson = JSON.parse(rawdata);
|
||||
} catch (error) {
|
||||
return { error: true, message: 'Error parsing JSON file' };
|
||||
}
|
||||
|
||||
if (uploadedJson.settings.version === 1) {
|
||||
try {
|
||||
res.data = await parseJson_v1(uploadedJson);
|
||||
res.message = 'success';
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
}
|
||||
} else {
|
||||
res = { error: true, message: 'Error parsing file, version unknown' };
|
||||
}
|
||||
}
|
||||
|
||||
// delete file
|
||||
await deleteFile(file);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Excel array parser
|
||||
* @param {array} excelData - array with excel sheet
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseExcel_v1 = async (excelData) => {
|
||||
let eventData = {
|
||||
const eventData = {
|
||||
title: '',
|
||||
url: '',
|
||||
};
|
||||
let customUserFields = {};
|
||||
let events = [];
|
||||
const customUserFields = {};
|
||||
const events = [];
|
||||
let timeStartIndex = null;
|
||||
let timeEndIndex = null;
|
||||
let titleIndex = null;
|
||||
@@ -291,7 +228,7 @@ export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
}
|
||||
|
||||
// object containing the parsed data
|
||||
let returnData = {};
|
||||
const returnData = {};
|
||||
|
||||
// parse Events
|
||||
returnData.events = parseEvents_v1(jsonData);
|
||||
@@ -366,3 +303,66 @@ export const validateEvent_v1 = (eventArgs) => {
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Middleware function that checks file type and calls relevant parser
|
||||
* @param {string} file - reference to file
|
||||
* @return {object} - parse result message
|
||||
*/
|
||||
export const fileHandler = async (file) => {
|
||||
let res = {};
|
||||
|
||||
// check which file type are we dealing with
|
||||
|
||||
if (file.endsWith('.xlsx')) {
|
||||
try {
|
||||
const excelData = xlsx
|
||||
.parse(file, { cellDates: true })
|
||||
.find(
|
||||
({ name }) => name.toLowerCase() === 'ontime' || name.toLowerCase() === 'event schedule'
|
||||
);
|
||||
|
||||
// we only look at worksheets called ontime or event schedule
|
||||
if (excelData?.data) {
|
||||
const dataFromExcel = await parseExcel_v1(excelData.data);
|
||||
res.data = await parseJson_v1(dataFromExcel);
|
||||
res.message = 'success';
|
||||
} else {
|
||||
console.log('Error: No sheets found named ontime or event schedule');
|
||||
res = {
|
||||
error: true,
|
||||
message: `No sheets found named ontime or event schedule`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
}
|
||||
}
|
||||
|
||||
if (file.endsWith('.json')) {
|
||||
// if json check version
|
||||
const rawdata = fs.readFileSync(file);
|
||||
let uploadedJson = null;
|
||||
|
||||
try {
|
||||
uploadedJson = JSON.parse(rawdata);
|
||||
} catch (error) {
|
||||
return { error: true, message: 'Error parsing JSON file' };
|
||||
}
|
||||
|
||||
if (uploadedJson.settings.version === 1) {
|
||||
try {
|
||||
res.data = await parseJson_v1(uploadedJson);
|
||||
res.message = 'success';
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
}
|
||||
} else {
|
||||
res = { error: true, message: 'Error parsing file, version unknown' };
|
||||
}
|
||||
}
|
||||
|
||||
// delete file
|
||||
await deleteFile(file);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import fs from 'fs';
|
||||
export const makeString = (val, fallback = '') => {
|
||||
if (typeof val === 'string') return val;
|
||||
else if (val == null || val.constructor === Object) return fallback;
|
||||
else return val.toString();
|
||||
return val.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { MAX_EVENTS, validateEvent_v1 } from './parser.js';
|
||||
import { generateId } from './generate_id.js';
|
||||
|
||||
/**
|
||||
* Parse events array of an entry
|
||||
@@ -12,8 +12,8 @@ export const parseEvents_v1 = (data) => {
|
||||
let newEvents = [];
|
||||
if ('events' in data) {
|
||||
console.log('Found events definition, importing...');
|
||||
let events = [];
|
||||
let ids = [];
|
||||
const events = [];
|
||||
const ids = [];
|
||||
for (const e of data.events) {
|
||||
// cap number of events
|
||||
if (events.length >= MAX_EVENTS) {
|
||||
@@ -28,7 +28,7 @@ export const parseEvents_v1 = (data) => {
|
||||
}
|
||||
|
||||
if (e.type === 'event') {
|
||||
let event = validateEvent_v1(e);
|
||||
const event = validateEvent_v1(e);
|
||||
if (event != null) {
|
||||
events.push(event);
|
||||
ids.push(event.id);
|
||||
@@ -94,7 +94,7 @@ export const parseSettings_v1 = (data, enforce) => {
|
||||
if (s.app == null || s.version == null) {
|
||||
console.log('ERROR: unknown app version, skipping');
|
||||
} else {
|
||||
let settings = {
|
||||
const settings = {
|
||||
lock: s.lock || null,
|
||||
pinCode: s.pinCode || null,
|
||||
};
|
||||
@@ -123,12 +123,12 @@ export const parseOsc_v1 = (data, enforce) => {
|
||||
if ('osc' in data) {
|
||||
console.log('Found OSC definition, importing...');
|
||||
const s = data.osc;
|
||||
let osc = {};
|
||||
const osc = {};
|
||||
|
||||
if (s.port) osc.port = s.port;
|
||||
if (s.portOut) osc.portOut = s.portOut;
|
||||
if (s.targetIP) osc.targetIP = s.targetIP;
|
||||
if (s.enabled !== undefined) osc.enabled = s.enabled;
|
||||
if (typeof s.enabled !== 'undefined') osc.enabled = s.enabled;
|
||||
|
||||
// write to db
|
||||
newOsc = {
|
||||
@@ -149,7 +149,7 @@ export const parseOsc_v1 = (data, enforce) => {
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseHttp_v1 = (data, enforce) => {
|
||||
let newHttp = {};
|
||||
const newHttp = {};
|
||||
if ('http' in data) {
|
||||
console.log('Found HTTP definition, importing...');
|
||||
const h = data.osc;
|
||||
@@ -207,13 +207,13 @@ export const parseAliases_v1 = (data) => {
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseUserFields_v1 = (data) => {
|
||||
let newUserFields = dbModelv1.userFields;
|
||||
const newUserFields = dbModelv1.userFields;
|
||||
|
||||
if ('userFields' in data) {
|
||||
console.log('Found User Fields definition, importing...');
|
||||
// we will only be importing the fields we know, so look for that
|
||||
let fieldsFound = 0;
|
||||
for (let n in newUserFields) {
|
||||
for (const n in newUserFields) {
|
||||
if (n in data.userFields) {
|
||||
fieldsFound++;
|
||||
newUserFields[n] = data.userFields[n];
|
||||
|
||||
@@ -27,12 +27,7 @@ export const nowInMillis = () => {
|
||||
* @returns {string} String representing time 00:12:02
|
||||
*/
|
||||
|
||||
export const stringFromMillis = (
|
||||
ms,
|
||||
showSeconds = true,
|
||||
delim = ':',
|
||||
ifNull = '...'
|
||||
) => {
|
||||
export const stringFromMillis = (ms, showSeconds = true, delim = ':', ifNull = '...') => {
|
||||
if (ms == null || isNaN(ms)) return ifNull;
|
||||
const isNegative = ms < 0 ? '-' : '';
|
||||
const millis = Math.abs(ms);
|
||||
@@ -57,7 +52,7 @@ export const stringFromMillis = (
|
||||
export const excelDateStringToMillis = (excelDate) => {
|
||||
const date = new Date(excelDate);
|
||||
if (date instanceof Date && !isNaN(date)) {
|
||||
const h = date.getUTCHours();
|
||||
const h = date.getHours();
|
||||
const m = date.getMinutes();
|
||||
const s = date.getSeconds();
|
||||
|
||||
|
||||
+38
-12
@@ -1,26 +1,52 @@
|
||||
import multer from 'multer';
|
||||
import { statSync, mkdirSync } from 'fs';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { EXCEL_MIME, JSON_MIME } from './parser.js';
|
||||
|
||||
/**
|
||||
* @description Returns public path depending on os
|
||||
* @return {string|*}
|
||||
*/
|
||||
function getAppDataPath() {
|
||||
switch (process.platform) {
|
||||
case 'darwin': {
|
||||
return path.join(process.env.HOME, 'Library', 'Application Support', 'Ontime');
|
||||
}
|
||||
case 'win32': {
|
||||
return path.join(process.env.APPDATA, 'Ontime');
|
||||
}
|
||||
case 'linux': {
|
||||
return path.join(process.env.HOME, '.Ontime');
|
||||
}
|
||||
default: {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define multer storage object
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
let newDestination = 'uploads/';
|
||||
let stat = null;
|
||||
try {
|
||||
stat = statSync(newDestination);
|
||||
} catch (err) {
|
||||
mkdirSync(newDestination);
|
||||
// get platform path
|
||||
const appDataPath = getAppDataPath();
|
||||
if (appDataPath === '') {
|
||||
throw new Error('Could not resolve public folder for platform');
|
||||
}
|
||||
if (stat && !stat.isDirectory()) {
|
||||
throw new Error(
|
||||
`Directory cannot be created because an inode of a different type exists at ${newDestination}`
|
||||
);
|
||||
// append uploads folder
|
||||
const newDestination = path.join(appDataPath, 'uploads');
|
||||
|
||||
// Create directory if not exist
|
||||
if (!existsSync(newDestination)) {
|
||||
try {
|
||||
mkdirSync(newDestination);
|
||||
} catch (err) {
|
||||
throw new Error('Could not create directory');
|
||||
}
|
||||
}
|
||||
cb(null, newDestination);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
cb(null, Date.now() + '--' + file.originalname);
|
||||
cb(null, `${Date.now()}--${file.originalname}`);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* @param {string} url - URL to be checked
|
||||
* @returns {string} Sanitized url
|
||||
*/
|
||||
export const cleanURL = (url) => {
|
||||
|
||||
export const cleanURL = (url) => {
|
||||
// trim whitespaces
|
||||
let r = url.trim();
|
||||
|
||||
@@ -12,10 +11,9 @@ export const cleanURL = (url) => {
|
||||
r = r.split(' ').join('%20');
|
||||
|
||||
// contain only allowed characters
|
||||
r = r.replace(/([^\x00-\x7F]|[@\s<>\[\]{}|\\^])+/g, '')
|
||||
|
||||
r = r.replace(/([@\s<>[\]{}|\\^])+/g, '');
|
||||
// starts with http://
|
||||
if (!r.startsWith('http://')) r = `http://${r}`
|
||||
if (!r.startsWith('http://')) r = `http://${r}`;
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1103,10 +1103,6 @@ once@^1.3.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
"ontime-utils@link: ../server/utils":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
optionator@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
|
||||
|
||||
Reference in New Issue
Block a user