mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 11:53:49 +00:00
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:
+2
-1
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user