Feat/table (#105)

* feat/table add react-table
* feat/table add protected table route
* feat/table upgrade dependencies
* feat/table support parsing of new properties

Co-authored-by: DeepSource Bot <bot@deepsource.io>
This commit is contained in:
Carlos Valente
2022-04-09 22:05:56 +02:00
committed by GitHub
parent 8841a02754
commit 67ddcd8c68
146 changed files with 7423 additions and 5780 deletions
+2 -1
View File
@@ -28,7 +28,8 @@ import express from 'express';
import http from 'http';
import cors from 'cors';
import { dbModelv1 as dbModel } from './models/dataModel.js';
import { parseJson_v1 as parseJson, validateFile } from './utils/parser.js';
import { parseJson_v1 as parseJson } from './utils/parser.js';
import { validateFile } from './utils/parserUtils.js';
import ua from 'universal-analytics';
// validate JSON before attempting read
+7 -4
View File
@@ -9,6 +9,8 @@ import {
event as eventDef,
} from '../models/eventsDefinition.js';
const MAX_EVENTS = 99;
async function _insertAt(entry, index) {
// get events
let events = data.events;
@@ -92,6 +94,11 @@ export const eventsPost = async (req, res) => {
return;
}
if (data.events.length > MAX_EVENTS) {
const error = `ERROR: Reached limit number of ${MAX_EVENTS} events`;
res.status(400).send(error);
}
// ensure structure
let newEvent = {};
const id = generateId();
@@ -275,8 +282,6 @@ export const eventsApplyDelay = async (req, res) => {
res.sendStatus(201);
} catch (error) {
console.log('debug:', error);
res.status(400).send(error);
}
};
@@ -299,8 +304,6 @@ export const eventsDelete = async (req, res) => {
res.sendStatus(201);
} catch (error) {
console.log('debug:', error);
res.status(400).send(error);
}
};
+47 -5
View File
@@ -59,18 +59,31 @@ const upload = async (file, req, res) => {
if (result?.error) {
res.status(400).send({ message: result.message });
} else if (result.message === 'success') {
if (result.data != null) {
if (result.data?.events != null) {
// explicitly write objects
if (typeof result.data !== 'undefined') {
if (typeof result.data?.events !== 'undefined') {
data.events = result.data.events;
global.timer.setupWithEventList(result.data?.events);
}
if (result.data?.event != null) {
if (typeof result.data?.event !== 'undefined') {
data.event = result.data.event;
}
if (result.data?.settings != null) {
if (typeof result.data?.settings !== 'undefined') {
data.settings = result.data.settings;
}
db.write();
if (typeof result.data?.osc !== 'undefined') {
data.osc = result.data.osc;
}
if (typeof result.data?.http !== 'undefined') {
data.http = result.data.http;
}
if (typeof result.data?.aliases !== 'undefined') {
data.aliases = result.data.aliases;
}
if (typeof result.data?.userFields !== 'undefined') {
data.userFields = result.data.userFields;
}
await db.write();
}
res.sendStatus(200);
} else {
@@ -163,6 +176,35 @@ export const postAliases = async (req, res) => {
}
};
// Create controller for GET request to '/ontime/userfields'
// Returns -
export const getUserFields = async (req, res) => {
// send userFields array
res.status(200).send(data.userFields);
};
// Create controller for POST request to '/ontime/userfields'
// Returns ACK message
export const postUserFields = async (req, res) => {
if (!req.body) {
res.status(400).send('No object found in request');
return;
}
try {
const newUserFields = { ...data.userFields };
for (const field in newUserFields) {
if (typeof req.body[field] !== 'undefined') {
newUserFields[field] = req.body[field];
}
}
data.userFields = newUserFields;
await db.write();
res.sendStatus(200);
} catch (error) {
res.status(400).send(error);
}
};
// Create controller for POST request to '/ontime/settings'
// Returns -
export const getSettings = async (req, res) => {
+12
View File
@@ -15,6 +15,18 @@ export const dbModelv1 = {
pinCode: null,
},
aliases: [],
userFields: {
user0: 'user0',
user1: 'user1',
user2: 'user2',
user3: 'user3',
user4: 'user4',
user5: 'user5',
user6: 'user6',
user7: 'user7',
user8: 'user8',
user9: 'user9',
},
osc: {
port: 8888,
portOut: 9999,
+13
View File
@@ -5,7 +5,20 @@ export const event = {
note: '',
timeStart: 0,
timeEnd: 0,
timeType: 'start-end',
duration: 0,
isPublic: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: 'event',
revision: 0,
};
+7 -7
View File
@@ -2,17 +2,17 @@
"name": "ontime-server",
"type": "module",
"dependencies": {
"body-parser": "~1.19.0",
"dotenv": "^10.0.0",
"express": "~4.17.1",
"body-parser": "1.19.1",
"dotenv": "^16.0.0",
"express": "^4.17.2",
"express-session": "~1.17.1",
"lowdb": "3.0.0",
"multer": "^1.4.4",
"nanoid": "^3.1.30",
"node-osc": "6.1.11",
"node-xlsx": "^0.17.2",
"nanoid": "^3.2.0",
"node-osc": "6.1.12",
"node-xlsx": "^0.21.0",
"ontime-utils": "link: ../server/utils/",
"passport": "~0.4.1",
"passport": "^0.5.2",
"passport-local": "~1.0.0",
"socket.io": "^4.4.1",
"universal-analytics": "^0.4.23"
@@ -57,6 +57,17 @@ describe('When a GET request request is sent', () => {
});
});
test('GET /ontime/aliases returns a valid object', async () => {
await supertest(server)
.get('/ontime/userFields')
.expect(200)
.then((response) => {
expect(response.text.includes('<!doctype html>')).toBe(false);
expect(response.body).toBeDefined();
expect(typeof response.body).toBe('object');
});
});
test('GET /ontime/settings returns a valid object', async () => {
await supertest(server)
.get('/ontime/settings')
+8
View File
@@ -16,6 +16,8 @@ import {
getAliases,
postAliases,
poll,
getUserFields,
postUserFields,
} from '../controllers/ontimeController.js';
// create route between controller and '/ontime/sync' endpoint
@@ -39,6 +41,12 @@ router.get('/aliases', getAliases);
// create route between controller and '/ontime/aliases' endpoint
router.post('/aliases', postAliases);
// create route between controller and '/ontime/aliases' endpoint
router.get('/userfields', getUserFields);
// create route between controller and '/ontime/aliases' endpoint
router.post('/userfields', postUserFields);
// create route between controller and '/ontime/info' endpoint
router.get('/info', getInfo);
+305 -36
View File
@@ -1,12 +1,8 @@
import jest from 'jest-mock';
import {
makeString,
parseAliases_v1,
parseExcel_v1,
parseJson_v1,
validateEvent_v1,
} from '../parser.js';
import { dbModelv1 as dbModel } from '../../models/dataModel.js';
import { dbModelv1, dbModelv1 as dbModel } from '../../models/dataModel.js';
import { parseExcel_v1, parseJson_v1, validateEvent_v1 } from '../parser.js';
import { makeString, validateDuration } from '../parserUtils.js';
import { parseAliases_v1, parseUserFields_v1 } from '../parserUtils_v1.js';
describe('test json parser with valid def', () => {
const testData = {
@@ -18,10 +14,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 31500000,
timeEnd: 32400000,
timeType: 'start-end',
duration: 32400000 - 31500000,
isPublic: false,
colour: '',
type: 'event',
revision: 0,
id: '4b31',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
{
title: 'Good Morning',
@@ -30,10 +39,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 32400000,
timeEnd: 36000000,
timeType: 'start-end',
duration: 36000000 - 32400000,
isPublic: true,
colour: '',
type: 'event',
revision: 0,
id: 'f24d',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
{
title: 'Stage 2 setup',
@@ -42,10 +64,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 32400000,
timeEnd: 37200000,
timeType: 'start-end',
duration: 37200000 - 32400000,
isPublic: false,
colour: '',
type: 'event',
revision: 0,
id: 'bbc5',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
{
title: 'Working Procedures',
@@ -54,7 +89,10 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 37200000,
timeEnd: 39000000,
timeType: 'start-end',
duration: 39000000 - 37200000,
isPublic: true,
colour: '',
type: 'event',
revision: 0,
id: '5b3e',
@@ -66,10 +104,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 39600000,
timeEnd: 45000000,
timeType: 'start-end',
duration: 37200000 - 32400000,
isPublic: false,
colour: '',
type: 'event',
revision: 0,
id: '8e2c',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
{
title: 'A day being carlos',
@@ -78,10 +129,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 46800000,
timeEnd: 50400000,
timeType: 'start-end',
duration: 37200000 - 32400000,
isPublic: true,
colour: '',
type: 'event',
revision: 0,
id: '08e9',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
{
title: 'Hamburgers and Cheese',
@@ -90,10 +154,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 54000000,
timeEnd: 57600000,
timeType: 'start-end',
duration: 37200000 - 32400000,
isPublic: true,
colour: '',
type: 'event',
revision: 0,
id: 'e25a',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
},
],
event: {
@@ -128,10 +205,23 @@ describe('test json parser with valid def', () => {
note: '',
timeStart: 31500000,
timeEnd: 32400000,
timeType: 'start-end',
duration: 32400000 - 31500000,
isPublic: false,
colour: '',
type: 'event',
revision: 0,
id: '4b31',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
};
expect(first).toStrictEqual(expected);
});
@@ -192,9 +282,7 @@ describe('test parser edge cases', () => {
};
const parseResponse = await parseJson_v1(testData);
expect(console.log).toHaveBeenCalledWith(
'ERROR: ID collision on import, skipping'
);
expect(console.log).toHaveBeenCalledWith('ERROR: ID collision on import, skipping');
expect(parseResponse?.events.length).toBe(1);
});
@@ -214,10 +302,7 @@ describe('test parser edge cases', () => {
};
const parseResponse = await parseJson_v1(testData);
expect(console.log).toHaveBeenCalledWith(
'ERROR: undefined event type, skipping'
);
expect(console.log).toHaveBeenCalledWith('ERROR: undefined event type, skipping');
expect(parseResponse?.events.length).toBe(0);
});
@@ -230,9 +315,7 @@ describe('test parser edge cases', () => {
};
await parseJson_v1(testData);
expect(console.log).toHaveBeenCalledWith(
'ERROR: unknown app version, skipping'
);
expect(console.log).toHaveBeenCalledWith('ERROR: unknown app version, skipping');
});
});
@@ -356,6 +439,17 @@ describe('test event validator', () => {
revision: expect.any(Number),
type: expect.any(String),
id: expect.any(String),
colour: expect.any(String),
user0: expect.any(String),
user1: expect.any(String),
user2: expect.any(String),
user3: expect.any(String),
user4: expect.any(String),
user5: expect.any(String),
user6: expect.any(String),
user7: expect.any(String),
user8: expect.any(String),
user9: expect.any(String),
})
);
});
@@ -441,6 +535,17 @@ describe('test parseExcel function', () => {
'Event Subtitle',
'Is Public? (x)',
'Notes',
'User0:test0',
'User1:test1',
'User2:test2',
'User3:test3',
'User4:test4',
'User5:test5',
'User6:test6',
'user7:test7',
'user8:test8',
'user9:test9',
'Colour',
],
[
'1899-12-30T07:00:00.000Z',
@@ -450,6 +555,21 @@ describe('test parseExcel function', () => {
'Getting things started',
'x',
'Ballyhoo',
'',
'',
'',
'',
'a0',
'a1',
'a2',
'a3',
'a4',
'a5',
'a6',
'a7',
'a8',
'a9',
'red',
],
[
'1899-12-30T08:00:00.000Z',
@@ -459,6 +579,21 @@ describe('test parseExcel function', () => {
'Derailing early',
'',
'Rainbow chase',
'',
'',
'',
'',
'b0',
'',
'',
'',
'',
'b5',
'',
'',
'',
'',
'#F00',
],
[],
];
@@ -472,6 +607,17 @@ describe('test parseExcel function', () => {
subtitle: 'Getting things started',
isPublic: true,
note: 'Ballyhoo',
user0: 'a0',
user1: 'a1',
user2: 'a2',
user3: 'a3',
user4: 'a4',
user5: 'a5',
user6: 'a6',
user7: 'a7',
user8: 'a8',
user9: 'a9',
colour: 'red',
type: 'event',
},
{
@@ -482,6 +628,9 @@ describe('test parseExcel function', () => {
subtitle: 'Derailing early',
isPublic: false,
note: 'Rainbow chase',
user0: 'b0',
user5: 'b5',
colour: '#F00',
type: 'event',
},
];
@@ -499,25 +648,145 @@ describe('test parseExcel function', () => {
});
describe('test aliases import', () => {
it('imports a well defined alias', () => {});
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
aliases: [
{
enabled: false,
alias: 'testalias',
pathAndParams: 'testpathAndParams',
it('imports a well defined alias', () => {
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
],
};
aliases: [
{
enabled: false,
alias: 'testalias',
pathAndParams: 'testpathAndParams',
},
],
};
const parsed = parseAliases_v1(testData, true);
expect(parsed.length).toBe(1);
const parsed = parseAliases_v1(testData);
expect(parsed.length).toBe(1);
// generates missing id
expect(parsed[0].id).toBeDefined();
// generates missing id
expect(parsed[0].id).toBeDefined();
});
});
describe('test userFields import', () => {
const model = dbModelv1.userFields;
it('imports a fully defined user fields', () => {
const testUserFields = {
user0: 'test0',
user1: 'test1',
user2: 'test2',
user3: 'test3',
user4: 'test4',
user5: 'test5',
user6: 'test6',
user7: 'test7',
user8: 'test8',
user9: 'test9',
};
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
userFields: testUserFields,
};
const parsed = parseUserFields_v1(testData);
expect(parsed).toStrictEqual(testUserFields);
});
it('imports a partially defined user fields', () => {
const testUserFields = {
user0: 'test0',
user1: 'test1',
user7: 'test7',
user8: 'test8',
user9: 'test9',
};
const expected = {
...model,
...testUserFields,
};
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
userFields: testUserFields,
};
const parsed = parseUserFields_v1(testData);
expect(parsed).toStrictEqual(expected);
});
it('handles missing user fields', () => {
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
};
const parsed = parseUserFields_v1(testData);
expect(parsed).toStrictEqual(model);
expect(parsed).toStrictEqual(model);
});
it('ignores badly defined fields', () => {
const testData = {
events: [],
settings: {
app: 'ontime',
version: 1,
},
userFields: {
notThis: 'this shouldng be accepted',
orThis: 'this neither',
},
};
const parsed = parseUserFields_v1(testData);
expect(parsed).toStrictEqual(model);
});
});
describe('test validateDuration()', () => {
describe('handles valid inputs', () => {
const valid = [
{ test: 'zero values', timeStart: 0, timeEnd: 0 },
{ test: 'end after start', timeStart: 0, timeEnd: 1 },
];
valid.forEach((t) => {
it(t.test, () => {
const d = validateDuration(t.timeStart, t.timeEnd);
expect(d).toBe(t.timeEnd - t.timeStart);
});
});
});
describe('handles edge cases', () => {
// edge cases
const testData = [
{ test: 'negative 0', timeStart: -0, timeEnd: -0, expected: 0 },
{ test: 'end before start', timeStart: 2, timeEnd: 1, expected: 0 },
];
testData.forEach((t) => {
it(t.test, () => {
const d = validateDuration(t.timeStart, t.timeEnd);
expect(d).toBe(t.expected);
});
});
});
});
+125 -258
View File
@@ -1,18 +1,23 @@
import fs from 'fs';
import xlsx from 'node-xlsx';
import {
block as blockDef,
delay as delayDef,
event as eventDef,
} from '../models/eventsDefinition.js';
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,
parseEvent_v1,
parseEvents_v1,
parseHttp_v1,
parseOsc_v1,
parseSettings_v1,
parseUserFields_v1,
} from './parserUtils_v1.js';
export const EXCEL_MIME =
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
export const EXCEL_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
export const JSON_MIME = 'application/json';
export const ALLOWED_TYPES = ['JSON', 'EXCEL'];
export const MAX_EVENTS = 99;
/**
* @description Middleware function that checks file type and calls relevant parser
@@ -29,9 +34,7 @@ export const fileHandler = async (file) => {
const excelData = xlsx
.parse(file, { cellDates: true })
.find(
({ name }) =>
name.toLowerCase() === 'ontime' ||
name.toLowerCase() === 'event schedule'
({ name }) => name.toLowerCase() === 'ontime' || name.toLowerCase() === 'event schedule'
);
// we only look at worksheets called ontime or event schedule
@@ -76,7 +79,6 @@ export const fileHandler = async (file) => {
// delete file
await deleteFile(file);
return res;
};
@@ -90,7 +92,7 @@ export const parseExcel_v1 = async (excelData) => {
title: '',
url: '',
};
let customUserFields = {};
let events = [];
let timeStartIndex = null;
let timeEndIndex = null;
@@ -99,6 +101,17 @@ export const parseExcel_v1 = async (excelData) => {
let subtitleIndex = null;
let isPublicIndex = null;
let notesIndex = null;
let colourIndex = null;
let user0Index = null;
let user1Index = null;
let user2Index = null;
let user3Index = null;
let user4Index = null;
let user5Index = null;
let user6Index = null;
let user7Index = null;
let user8Index = null;
let user9Index = null;
excelData
.filter((e) => e.length > 0)
@@ -127,14 +140,41 @@ export const parseExcel_v1 = async (excelData) => {
event.subtitle = column;
} else if (j === isPublicIndex) {
// whether column is not empty
event.isPublic = column !== '';
let c = column;
if (typeof column === 'string') {
c = column.replace(/\s+/g, '');
}
event.isPublic = c !== '';
} else if (j === notesIndex) {
event.note = column;
} else if (j === colourIndex) {
event.colour = column;
} else if (j === user0Index) {
event.user0 = column;
} else if (j === user1Index) {
event.user1 = column;
} else if (j === user2Index) {
event.user2 = column;
} else if (j === user3Index) {
event.user3 = column;
} else if (j === user4Index) {
event.user4 = column;
} else if (j === user5Index) {
event.user5 = column;
} else if (j === user6Index) {
event.user6 = column;
} else if (j === user7Index) {
event.user7 = column;
} else if (j === user8Index) {
event.user8 = column;
} else if (j === user9Index) {
event.user9 = column;
} else {
if (typeof column === 'string') {
const col = column.toLowerCase();
// look for keywords
// need to make sure it is a string first
switch (column.toLowerCase()) {
switch (col) {
case 'event name':
eventTitleNext = true;
break;
@@ -171,12 +211,56 @@ export const parseExcel_v1 = async (excelData) => {
case 'notes':
notesIndex = j;
break;
case 'colour':
case 'color':
colourIndex = j;
break;
default:
// look for user defined
if (col.startsWith('user')) {
const index = column.charAt(4);
// name is the bit after the :
const [, name] = column.split(':');
if (typeof name !== 'undefined') {
if (index === '0') {
customUserFields.user0 = name;
user0Index = j;
} else if (index === '1') {
customUserFields.user1 = name;
user1Index = j;
} else if (index === '2') {
customUserFields.user2 = name;
user2Index = j;
} else if (index === '3') {
customUserFields.user3 = name;
user3Index = j;
} else if (index === '4') {
customUserFields.user4 = name;
user4Index = j;
} else if (index === '5') {
customUserFields.user5 = name;
user5Index = j;
} else if (index === '6') {
customUserFields.user6 = name;
user6Index = j;
} else if (index === '7') {
customUserFields.user7 = name;
user7Index = j;
} else if (index === '8') {
customUserFields.user8 = name;
user8Index = j;
} else if (index === '9') {
customUserFields.user9 = name;
user9Index = j;
}
}
}
break;
}
}
}
});
if (Object.keys(event).length > 0) {
// if any data was found, push to array
// take care of it in the next step
@@ -190,6 +274,7 @@ export const parseExcel_v1 = async (excelData) => {
app: 'ontime',
version: 1,
},
userFields: { ...dbModelv1.userFields, ...customUserFields },
};
};
@@ -220,22 +305,12 @@ export const parseJson_v1 = async (jsonData, enforce = false) => {
returnData.http = parseHttp_v1(jsonData, enforce);
// Import Aliases if any
returnData.aliases = parseAliases_v1(jsonData);
// Import user fields if any
returnData.userFields = parseUserFields_v1(jsonData);
return returnData;
};
/**
* @description Ensures variable is string, it skips object types
* @param {any} val - variable to convert
* @param {string} [fallback=''] - fallback value
* @returns {string} - value as string or fallback if not possible
*/
export const makeString = (val, fallback = '') => {
if (typeof val === 'string') return val;
else if (val == null || val.constructor === Object) return fallback;
else return val.toString();
};
/**
* @description Enforces formatting for events
* @param {object} eventArgs - attributes of event
@@ -254,26 +329,36 @@ export const validateEvent_v1 = (eventArgs) => {
const e = eventArgs;
const d = eventDef;
const start =
e.timeStart != null && typeof e.timeStart === 'number' ? e.timeStart : d.timeStart;
const end = e.timeEnd != null && typeof e.timeEnd === 'number' ? e.timeEnd : d.timeEnd;
event = {
...d,
title: makeString(e.title, d.title),
subtitle: makeString(e.subtitle, d.subtitle),
presenter: makeString(e.presenter, d.presenter),
timeStart: start,
timeEnd: end,
timeType: 'start-end',
duration: validateDuration(start, end),
isPublic: e.isPublic != null && typeof e.isPublic === 'boolean' ? e.isPublic : d.isPublic,
note: makeString(e.note, d.note),
timeStart:
e.timeStart != null && typeof e.timeStart === 'number'
? e.timeStart
: d.timeStart,
timeEnd:
e.timeEnd != null && typeof e.timeEnd === 'number'
? e.timeEnd
: d.timeEnd,
isPublic:
e.isPublic != null && typeof e.isPublic === 'boolean'
? e.isPublic
: d.isPublic,
user0: makeString(e.user0, d.user0),
user1: makeString(e.user1, d.user1),
user2: makeString(e.user2, d.user2),
user3: makeString(e.user3, d.user3),
user4: makeString(e.user4, d.user4),
user5: makeString(e.user5, d.user5),
user6: makeString(e.user6, d.user6),
user7: makeString(e.user7, d.user7),
user8: makeString(e.user8, d.user8),
user9: makeString(e.user9, d.user9),
// deciding not to validate colour
// this adds flexibility to the user to write hex codes, rgb,
// but also colour names like blue and red
// CSS.supports is only available in frontend
colour: makeString(e.colour, d.colour),
id,
type: 'event',
};
@@ -281,221 +366,3 @@ export const validateEvent_v1 = (eventArgs) => {
return event;
};
/**
* @description Delete file from system
* @param {string} file - reference to file
*/
const deleteFile = async (file) => {
// delete a file
fs.unlink(file, (err) => {
if (err) {
console.log(err);
}
});
};
/**
* @description Delete file from system
* @param {string} file - reference to file
* @returns {boolean} - whether file is valid JSON
*/
export const validateFile = (file) => {
try {
JSON.parse(fs.readFileSync(file));
return true;
} catch (err) {
return false;
}
};
/**
* Parse events array of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseEvents_v1 = (data) => {
let newEvents = [];
if ('events' in data) {
console.log('Found events definition, importing...');
let events = [];
let ids = [];
for (const e of data.events) {
// double check unique ids
if (ids.indexOf(e?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
if (e.type === 'event') {
let event = validateEvent_v1(e);
if (event != null) {
events.push(event);
ids.push(event.id);
}
} else if (e.type === 'delay') {
events.push({
...delayDef,
duration: e.duration,
id: e.id || generateId(),
});
} else if (e.type === 'block') {
events.push({ ...blockDef, id: e.id || generateId() });
} else {
console.log('ERROR: undefined event type, skipping');
}
}
// write to db
newEvents = events;
console.log(`Uploaded file with ${events.length} entries`);
}
return newEvents;
};
/**
* Parse event portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseEvent_v1 = (data, enforce) => {
let newEvent = {};
if ('event' in data) {
console.log('Found event data, importing...');
const e = data.event;
// filter known properties and write to db
newEvent = {
...dbModelv1.event,
title: e.title || dbModelv1.event.title,
url: e.url || dbModelv1.event.url,
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
endMessage: e.endMessage || dbModelv1.event.endMessage,
};
} else if (enforce) {
newEvent = dbModelv1.event;
console.log(`Created event object in db`);
}
return newEvent;
};
/**
* Parse settings portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseSettings_v1 = (data, enforce) => {
let newSettings = {};
if ('settings' in data) {
console.log('Found settings definition, importing...');
const s = data.settings;
// skip if file definition is missing
if (s.app == null || s.version == null) {
console.log('ERROR: unknown app version, skipping');
} else {
let settings = {
lock: s.lock || null,
pinCode: s.pinCode || null,
};
// write to db
newSettings = {
...dbModelv1.settings,
...settings,
};
}
} else if (enforce) {
newSettings = dbModelv1.settings;
console.log(`Created settings object in db`);
}
return newSettings;
};
/**
* Parse osc portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseOsc_v1 = (data, enforce) => {
let newOsc = {};
if ('osc' in data) {
console.log('Found OSC definition, importing...');
const s = data.osc;
let 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;
// write to db
newOsc = {
...dbModelv1.osc,
...osc,
};
} else if (enforce) {
newOsc = dbModelv1.osc;
console.log(`Created OSC object in db`);
}
return newOsc;
};
/**
* Parse Http portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseHttp_v1 = (data, enforce) => {
let newHttp = {};
if ('http' in data) {
console.log('Found HTTP definition, importing...');
const h = data.osc;
let http = {};
if (h.user) http.user = h.user;
if (h.pwd) http.pwd = h.pwd;
// write to db
newHttp.http = {
...dbModelv1.http,
...http,
};
} else if (enforce) {
newHttp.http = dbModelv1.http;
console.log(`Created http object in db`);
}
return newHttp;
};
/**
* Parse aliases portion of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseAliases_v1 = (data) => {
let newAliases = [];
if ('aliases' in data) {
console.log('Found Aliases definition, importing...');
const ids = [];
for (const a of data.aliases) {
// double check unique ids
if (ids.indexOf(a?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
const newAlias = {
id: a.id || generateId(),
enabled: a.enabled || false,
alias: a.alias || '',
pathAndParams: a.pathAndParams || '',
};
ids.push(newAlias.id);
newAliases.push(newAlias);
}
console.log(`Uploaded ${newAliases?.length || 0} alias(es)`);
}
return newAliases;
};
+52
View File
@@ -0,0 +1,52 @@
import fs from 'fs';
/**
* @description Ensures variable is string, it skips object types
* @param {any} val - variable to convert
* @param {string} [fallback=''] - fallback value
* @returns {string} - value as string or fallback if not possible
*/
export const makeString = (val, fallback = '') => {
if (typeof val === 'string') return val;
else if (val == null || val.constructor === Object) return fallback;
else return val.toString();
};
/**
* @description validates a duration value against options
* @param {number} timeStart
* @param {number} timeEnd
* @returns {number}
*/
export const validateDuration = (timeStart, timeEnd) => {
// Todo: this would go into a switch statement when expanded
// Durations must be positive
return Math.max(timeEnd - timeStart, 0);
};
/**
* @description Delete file from system
* @param {string} file - reference to file
*/
export const deleteFile = async (file) => {
// delete a file
fs.unlink(file, (err) => {
if (err) {
console.log(err);
}
});
};
/**
* @description Delete file from system
* @param {string} file - reference to file
* @returns {boolean} - whether file is valid JSON
*/
export const validateFile = (file) => {
try {
JSON.parse(fs.readFileSync(file, 'utf-8'));
return true;
} catch (err) {
return false;
}
};
+225
View File
@@ -0,0 +1,225 @@
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';
/**
* Parse events array of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseEvents_v1 = (data) => {
let newEvents = [];
if ('events' in data) {
console.log('Found events definition, importing...');
let events = [];
let ids = [];
for (const e of data.events) {
// cap number of events
if (events.length >= MAX_EVENTS) {
console.log(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
break;
}
// double check unique ids
if (ids.indexOf(e?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
if (e.type === 'event') {
let event = validateEvent_v1(e);
if (event != null) {
events.push(event);
ids.push(event.id);
}
} else if (e.type === 'delay') {
events.push({
...delayDef,
duration: e.duration,
id: e.id || generateId(),
});
} else if (e.type === 'block') {
events.push({ ...blockDef, id: e.id || generateId() });
} else {
console.log('ERROR: undefined event type, skipping');
}
}
// write to db
newEvents = events;
console.log(`Uploaded file with ${events.length} entries`);
}
return newEvents;
};
/**
* Parse event portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseEvent_v1 = (data, enforce) => {
let newEvent = {};
if ('event' in data) {
console.log('Found event data, importing...');
const e = data.event;
// filter known properties and write to db
newEvent = {
...dbModelv1.event,
title: e.title || dbModelv1.event.title,
url: e.url || dbModelv1.event.url,
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
endMessage: e.endMessage || dbModelv1.event.endMessage,
};
} else if (enforce) {
newEvent = dbModelv1.event;
console.log(`Created event object in db`);
}
return newEvent;
};
/**
* Parse settings portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseSettings_v1 = (data, enforce) => {
let newSettings = {};
if ('settings' in data) {
console.log('Found settings definition, importing...');
const s = data.settings;
// skip if file definition is missing
if (s.app == null || s.version == null) {
console.log('ERROR: unknown app version, skipping');
} else {
let settings = {
lock: s.lock || null,
pinCode: s.pinCode || null,
};
// write to db
newSettings = {
...dbModelv1.settings,
...settings,
};
}
} else if (enforce) {
newSettings = dbModelv1.settings;
console.log(`Created settings object in db`);
}
return newSettings;
};
/**
* Parse osc portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseOsc_v1 = (data, enforce) => {
let newOsc = {};
if ('osc' in data) {
console.log('Found OSC definition, importing...');
const s = data.osc;
let 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;
// write to db
newOsc = {
...dbModelv1.osc,
...osc,
};
} else if (enforce) {
newOsc = dbModelv1.osc;
console.log(`Created OSC object in db`);
}
return newOsc;
};
/**
* Parse Http portion of an entry
* @param {object} data - data object
* @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data
*/
export const parseHttp_v1 = (data, enforce) => {
let newHttp = {};
if ('http' in data) {
console.log('Found HTTP definition, importing...');
const h = data.osc;
let http = {};
if (h.user) http.user = h.user;
if (h.pwd) http.pwd = h.pwd;
// write to db
newHttp.http = {
...dbModelv1.http,
...http,
};
} else if (enforce) {
newHttp.http = dbModelv1.http;
console.log(`Created http object in db`);
}
return newHttp;
};
/**
* Parse aliases portion of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseAliases_v1 = (data) => {
let newAliases = [];
if ('aliases' in data) {
console.log('Found Aliases definition, importing...');
const ids = [];
for (const a of data.aliases) {
// double check unique ids
if (ids.indexOf(a?.id) !== -1) {
console.log('ERROR: ID collision on import, skipping');
continue;
}
const newAlias = {
id: a.id || generateId(),
enabled: a.enabled || false,
alias: a.alias || '',
pathAndParams: a.pathAndParams || '',
};
ids.push(newAlias.id);
newAliases.push(newAlias);
}
console.log(`Uploaded ${newAliases?.length || 0} alias(es)`);
}
return newAliases;
};
/**
* Parse userFields entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parseUserFields_v1 = (data) => {
let 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) {
if (n in data.userFields) {
fieldsFound++;
newUserFields[n] = data.userFields[n];
}
}
console.log(`Uploaded ${fieldsFound} user fields`);
}
return { ...dbModelv1.userFields, ...newUserFields };
};
+104 -139
View File
@@ -2,13 +2,6 @@
# yarn lockfile v1
"@babel/runtime@^7.15.4":
version "7.16.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
dependencies:
regenerator-runtime "^0.13.4"
"@eslint/eslintrc@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318"
@@ -187,21 +180,21 @@ binpack@~0:
resolved "https://registry.yarnpkg.com/binpack/-/binpack-0.1.0.tgz#bd3d0974c3f2a0446e17df4f60b55a72a205a97e"
integrity sha1-vT0JdMPyoERuF99PYLVacqIFqX4=
body-parser@1.19.0, body-parser@~1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
body-parser@1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4"
integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==
dependencies:
bytes "3.1.0"
bytes "3.1.1"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.7.2"
http-errors "1.8.1"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.7.0"
raw-body "2.4.0"
type-is "~1.6.17"
qs "6.9.6"
raw-body "2.4.2"
type-is "~1.6.18"
brace-expansion@^1.1.7:
version "1.1.11"
@@ -211,7 +204,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
buffer-from@^1.0.0, buffer-from@^1.1.2:
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
@@ -224,10 +217,10 @@ busboy@^0.2.11:
dicer "0.2.5"
readable-stream "1.1.x"
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
bytes@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a"
integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==
callsites@^3.0.0:
version "3.1.0"
@@ -300,12 +293,12 @@ concat-stream@^1.5.2:
readable-stream "^2.2.2"
typedarray "^0.0.6"
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.1.2"
safe-buffer "5.2.1"
content-type@~1.0.4:
version "1.0.4"
@@ -317,11 +310,6 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookie@0.4.1, cookie@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
@@ -430,10 +418,10 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
dotenv@^16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411"
integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==
ecc-jsbn@~0.1.1:
version "0.1.2"
@@ -619,17 +607,17 @@ express-session@~1.17.1:
safe-buffer "5.2.1"
uid-safe "~2.1.5"
express@~4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
express@^4.17.2:
version "4.17.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3"
integrity sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
body-parser "1.19.0"
content-disposition "0.5.3"
body-parser "1.19.1"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.4.0"
cookie "0.4.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
@@ -643,13 +631,13 @@ express@~4.17.1:
on-finished "~2.3.0"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.5"
qs "6.7.0"
proxy-addr "~2.0.7"
qs "6.9.6"
range-parser "~1.2.1"
safe-buffer "5.1.2"
send "0.17.1"
serve-static "1.14.1"
setprototypeof "1.1.1"
safe-buffer "5.2.1"
send "0.17.2"
serve-static "1.14.2"
setprototypeof "1.2.0"
statuses "~1.5.0"
type-is "~1.6.18"
utils-merge "1.0.1"
@@ -808,27 +796,16 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@~1.7.2:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
http-errors@1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.1.1"
setprototypeof "1.2.0"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
toidentifier "1.0.1"
http-signature@~1.2.0:
version "1.2.0"
@@ -877,11 +854,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
@@ -1049,16 +1021,16 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
multer@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c"
@@ -1073,10 +1045,10 @@ multer@^1.4.4:
type-is "^1.6.4"
xtend "^4.0.0"
nanoid@^3.1.30:
version "3.1.30"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
nanoid@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
natural-compare@^1.4.0:
version "1.4.0"
@@ -1088,21 +1060,19 @@ negotiator@0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
node-osc@6.1.11:
version "6.1.11"
resolved "https://registry.yarnpkg.com/node-osc/-/node-osc-6.1.11.tgz#c1d0700cb74e7fe141f4f99531318b8615e38d2f"
integrity sha512-bwFV/UKBBaSNK5pLYeA8Qx0GVc49fKRjmfMwMoypfPwb7RdhyRccW+tTNHcwr/OJ2b01uVKpnTPurSNs4fWnvw==
node-osc@6.1.12:
version "6.1.12"
resolved "https://registry.yarnpkg.com/node-osc/-/node-osc-6.1.12.tgz#dff8fe8d02a5eead21a10b826cb41b700c251720"
integrity sha512-GZLGwM7r9CyciAoRAsa8WULtUjPCDfmdgURw8R8rrff9wxWWnGJU/IVnHebc6CfqYr42NrgqY02MDxvhx/3Dwg==
dependencies:
osc-min "^1.1.1"
node-xlsx@^0.17.2:
version "0.17.2"
resolved "https://registry.yarnpkg.com/node-xlsx/-/node-xlsx-0.17.2.tgz#286215afa63e096d5317a7cb0e5d599cfa1f7b62"
integrity sha512-j92dGS8KvGPi6YpYovHrR9zWiyDONx7DiGhl1SjM+vzxAh3do6hmerFCyN+hRuK7QhwHdwzfpYxZm+hKA/uErA==
node-xlsx@^0.21.0:
version "0.21.0"
resolved "https://registry.yarnpkg.com/node-xlsx/-/node-xlsx-0.21.0.tgz#085d69869d7fa7ed1e6d2a13eee6aa6dce7867a1"
integrity sha512-MB+KcNCuRzwjgr17scpKiVTPd4Vbj3V+7QwKpqACGyJzhvC67xCQUbw2vYEIKtNfMfcLxgB2q2kEuRS8rmak9g==
dependencies:
"@babel/runtime" "^7.15.4"
buffer-from "^1.1.2"
xlsx "^0.17.2"
xlsx "^0.17.4"
oauth-sign@~0.9.0:
version "0.9.0"
@@ -1180,10 +1150,10 @@ passport-strategy@1.x.x:
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
passport@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270"
integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==
passport@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/passport/-/passport-0.5.2.tgz#0cb38dd8a71552c8390dfa6a9a6f7f3909954bcf"
integrity sha512-w9n/Ot5I7orGD4y+7V3EFJCQEznE5RxHamUxcqLT2QoJY0f2JdN8GyHonYFvN0Vz+L6lUJfVhrk2aZz2LbuREw==
dependencies:
passport-strategy "1.x.x"
pause "0.0.1"
@@ -1243,7 +1213,7 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proxy-addr@~2.0.5:
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
@@ -1261,10 +1231,10 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@6.9.6:
version "6.9.6"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee"
integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==
qs@~6.5.2:
version "6.5.2"
@@ -1281,13 +1251,13 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
raw-body@2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32"
integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
bytes "3.1.1"
http-errors "1.8.1"
iconv-lite "0.4.24"
unpipe "1.0.0"
@@ -1314,11 +1284,6 @@ readable-stream@^2.2.2:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@@ -1362,16 +1327,16 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@@ -1384,10 +1349,10 @@ semver@^7.2.1:
dependencies:
lru-cache "^6.0.0"
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
send@0.17.2:
version "0.17.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==
dependencies:
debug "2.6.9"
depd "~1.1.2"
@@ -1396,27 +1361,27 @@ send@0.17.1:
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "~1.7.2"
http-errors "1.8.1"
mime "1.6.0"
ms "2.1.1"
ms "2.1.3"
on-finished "~2.3.0"
range-parser "~1.2.1"
statuses "~1.5.0"
serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
serve-static@1.14.2:
version "1.14.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa"
integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.1"
send "0.17.2"
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shebang-command@^2.0.0:
version "2.0.0"
@@ -1529,10 +1494,10 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tough-cookie@~2.5.0:
version "2.5.0"
@@ -1566,7 +1531,7 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18:
type-is@^1.6.4, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@@ -1673,10 +1638,10 @@ ws@~8.2.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
xlsx@^0.17.2:
version "0.17.4"
resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.17.4.tgz#dc3e3a0954c835f4d0fdd643645db6f4ac3f28f2"
integrity sha512-9aKt8g9ZLP0CUdBX8L5xnoMDFwSiLI997eQnDThCaqQMYB9AEBIRzblSSNN/ICMGLYIHUO3VKaItcedZJ3ijIg==
xlsx@^0.17.4:
version "0.17.5"
resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.17.5.tgz#78b788fcfc0773d126cdcd7ea069cb7527c1ce81"
integrity sha512-lXNU0TuYsvElzvtI6O7WIVb9Zar1XYw7Xb3VAx2wn8N/n0whBYrCnHMxtFyIiUU1Wjf09WzmLALDfBO5PqTb1g==
dependencies:
adler-32 "~1.2.0"
cfb "^1.1.4"