apply eslint fixes

pull/4/head
Cameron Clough 2022-01-08 20:43:57 +00:00
parent 4f13eae80a
commit 7b43aa3eff
36 changed files with 1081 additions and 1192 deletions

View File

@ -1,61 +1,60 @@
var config = {
applicationSalt: 'RANDOM_SEED',
databaseFile: 'database.sqlite',
allowAccountRegistration: true,
httpInterface: '0.0.0.0',
httpPort: 3000,
httpsInterface: '0.0.0.0',
httpsPort: 4430,
sslKey: 'certs/retropilot.key',
sslCrt: 'certs/retropilot.crt',
applicationSalt: 'RANDOM_SEED',
canSendMail: true, // Skips sending mail, all attempted mail is logged under DEBUG
smtpHost: "localhost", // credentials for smtp server to send account registration mails. if not filled in, get the generated tokens from the server.log manually
smtpPort: 25,
smtpUser: "root",
smtpPassword: "",
smtpFrom: "no-reply@retropilot.org",
databaseFile: 'database.sqlite',
baseUrl: 'http://192.168.1.165:3000/', // base url of the retropilot server
baseUploadUrl: 'http://192.168.1.165:3000/backend/post_upload', // base url sent to devices for POSTing drives & logs
baseDriveDownloadUrl: 'http://192.168.1.165:3000/realdata/', // base download url for drive & log data
baseDriveDownloadPathMapping: '/realdata', // path mapping of above download url for expressjs, prefix with "/"
storagePath: 'realdata/', // relative or absolute ( "/..." for absolute path )
allowAccountRegistration: true,
cabanaUrl: 'http://192.168.1.165:3000/cabana/index.html',
httpInterface: '0.0.0.0',
httpPort: 3000,
deviceStorageQuotaMb: 200000,
deviceDriveExpirationDays: 30,
httpsInterface: '0.0.0.0',
httpsPort: 4430,
sslKey: 'certs/retropilot.key',
sslCrt: 'certs/retropilot.crt',
welcomeMessage: `<><><><><><><><><><><><><><><><><><><><><><><br>2021 RetroPilot`,
canSendMail: true, // Skips sending mail, all attempted mail is logged under DEBUG
smtpHost: 'localhost', // credentials for smtp server to send account registration mails. if not filled in, get the generated tokens from the server.log manually
smtpPort: 25,
smtpUser: 'root',
smtpPassword: '',
smtpFrom: 'no-reply@retropilot.org',
flags: {
useUserAdminApi: false,
baseUrl: 'http://192.168.1.165:3000/', // base url of the retropilot server
baseUploadUrl: 'http://192.168.1.165:3000/backend/post_upload', // base url sent to devices for POSTing drives & logs
baseDriveDownloadUrl: 'http://192.168.1.165:3000/realdata/', // base download url for drive & log data
baseDriveDownloadPathMapping: '/realdata', // path mapping of above download url for expressjs, prefix with "/"
storagePath: 'realdata/', // relative or absolute ( "/..." for absolute path )
cabanaUrl: 'http://192.168.1.165:3000/cabana/index.html',
deviceStorageQuotaMb: 200000,
deviceDriveExpirationDays: 30,
welcomeMessage: '<><><><><><><><><><><><><><><><><><><><><><><br>2021 RetroPilot',
flags: {
useUserAdminApi: false,
},
clientSocket: { // Used in development, remove before prod
port: 81,
host: '0.0.0.0',
},
athena: {
enabled: true, // Enables Athena service
secure: true, // Disables crypto on Websocket server - use for testing on local network, change ATHENA_HOST in openpilot to ws:// instead of wss://
api: {
ratelimit: 100, // Maxmium hits to /realtime/* per 30s
},
clientSocket: { // Used in development, remove before prod
port: 81,
host: "0.0.0.0"
socket: {
port: 4040,
heartbeatFrequency: 5000, // Higher the number = lower traffic, varies on how many devices are connected
},
athena: {
enabled: true, // Enables Athena service
secure: true, // Disables crypto on Websocket server - use for testing on local network, change ATHENA_HOST in openpilot to ws:// instead of wss://
api: {
ratelimit: 100 // Maxmium hits to /realtime/* per 30s
},
socket: {
port: 4040,
heartbeatFrequency: 5000 // Higher the number = lower traffic, varies on how many devices are connected
}
}
},
};
module.exports = config;

View File

@ -25,14 +25,13 @@ async function banAccount(ban, userId) {
let cleanBan;
if (ban === 'true' || ban === 'false') {
cleanBan = ban === 'true';
}
else {
} else {
return { success: false, status: 400, data: { bad_data: true } };
}
const update = await models_orm.models.accounts.update(
{ banned: cleanBan ? 1 : 0 },
{ where: { id: userId } }
{ where: { id: userId } },
);
const verify = await models_orm.models.accounts.findOne({ where: { id: userId } });
@ -45,5 +44,5 @@ async function banAccount(ban, userId) {
module.exports = {
banAccount,
isCurrentUserAdmin
isCurrentUserAdmin,
};

View File

@ -8,8 +8,7 @@ const config = require('../config');
async function validateJWT(token, key) {
try {
return jsonwebtoken.verify(token.replace('JWT ', ''), key, { algorithms: ['RS256'], ignoreNotBefore: true });
}
catch (exception) {
} catch (exception) {
console.log(`failed to validate JWT ${exception}`);
}
return null;
@ -18,8 +17,7 @@ async function validateJWT(token, key) {
async function readJWT(token) {
try {
return jsonwebtoken.decode(token);
}
catch (exception) {
} catch (exception) {
logger.warn(`failed to read JWT ${exception}`);
}
return null;
@ -54,7 +52,7 @@ async function changePassword(account, newPassword, oldPassword) {
const update = models_orm.models.accounts.update(
{ password: newPasswordHash },
{ where: { id: account.id } }
{ where: { id: account.id } },
);
return { success: true, msg: 'PASSWORD CHANGED', changed: true };
@ -80,8 +78,7 @@ async function getAccountFromJWT(jwt, limitData) {
try {
token = jsonwebtoken.verify(jwt, config.applicationSalt);
}
catch (err) {
} catch (err) {
return null;// {success: false, msg: 'BAD_JWT'}
}
@ -94,7 +91,7 @@ async function getAccountFromJWT(jwt, limitData) {
if (account.dataValues) {
const update = models_orm.models.accounts.update(
{ last_ping: Date.now() },
{ where: { id: account.id } }
{ where: { id: account.id } },
);
if (!account || account.banned) {
@ -113,5 +110,5 @@ module.exports = {
changePassword,
signIn,
readJWT,
getAccountFromJWT
getAccountFromJWT,
};

View File

@ -21,14 +21,12 @@ async function pairDevice(account, qr_string) {
if (qrCodeParts.length > 1) {
deviceQuery = await models_orm.models.device.findOne({ where: { serial: qrCodeParts[1] } });
pairJWT = qrCodeParts[2];
}
else {
} else {
pairJWT = qr_string;
const data = await authenticationController.readJWT(qr_string);
if (data.pair === true) {
deviceQuery = await models_orm.models.device.findOne({ where: { dongle_id: data.identity } });
}
else {
} else {
return { success: false, noPair: true };
}
}
@ -52,15 +50,15 @@ async function pairDevice(account, qr_string) {
async function pairDeviceToAccountId(dongle_id, account_id) {
const update = await models_orm.models.device.update(
{ account_id },
{ where: { dongle_id } }
{ where: { dongle_id } },
);
const check = await models_orm.models.device.findOne(
{ where: { dongle_id, account_id } }
{ where: { dongle_id, account_id } },
);
if (check.dataValues) {
return {
success: true, paired: true, dongle_id, account_id
success: true, paired: true, dongle_id, account_id,
};
}
return { success: false, paired: false };
@ -68,13 +66,13 @@ async function pairDeviceToAccountId(dongle_id, account_id) {
async function unpairDevice(account, dongleId) {
const device = await models_orm.models.device.getOne(
{ where: { account_id: account.id, dongle_id: dongleId } }
{ where: { account_id: account.id, dongle_id: dongleId } },
);
if (device && device.dataValues) {
await models_orm.models.device.update(
{ account_id: 0 },
{ where: { dongle_id: dongleId } }
{ where: { dongle_id: dongleId } },
);
return { success: true };
}
@ -83,7 +81,7 @@ async function unpairDevice(account, dongleId) {
async function setDeviceNickname(account, dongleId, nickname) {
const device = await models_orm.models.device.getOne(
{ where: { account_id: account.id, dongle_id: dongleId } }
{ where: { account_id: account.id, dongle_id: dongleId } },
);
const cleanNickname = sanitize.value(nickname, 'string');
@ -91,7 +89,7 @@ async function setDeviceNickname(account, dongleId, nickname) {
if (device && device.dataValues) {
await models_orm.models.device.update(
{ nickname: cleanNickname },
{ where: { dongle_id: dongleId } }
{ where: { dongle_id: dongleId } },
);
return { success: true, data: { nickname: cleanNickname } };
}
@ -112,7 +110,7 @@ async function getDeviceFromDongle(dongleId) {
async function setIgnoredUploads(dongleId, isIgnored) {
const update = models_orm.models.accounts.update(
{ dongle_id: dongleId },
{ where: { uploads_ignored: isIgnored } }
{ where: { uploads_ignored: isIgnored } },
);
// TODO check this change was processed..
@ -128,7 +126,7 @@ async function getAllDevicesFiltered() {
async function updateLastPing(device_id, dongle_id) {
models_orm.models.device.update(
{ last_ping: Date.now() },
{ where: { [Op.or]: [{ id: device_id }, { dongle_id }] } }
{ where: { [Op.or]: [{ id: device_id }, { dongle_id }] } },
);
}
@ -150,8 +148,8 @@ async function isUserAuthorised(account_id, dongle_id) {
return {
success: true,
data: {
authorised: true, account_id: account.id, dongle_id: device.dongle_id
}
authorised: true, account_id: account.id, dongle_id: device.dongle_id,
},
};
}
return { success: false, msg: 'not_authorised', data: { authorised: false, account_id: account.id, dongle_id: device.dongle_id } };
@ -199,15 +197,14 @@ async function getCrashlogs(dongle_id) {
let dateObj = null;
try {
dateObj = Date.parse(timeString);
}
catch (exception) {}
} catch (exception) {}
if (!dateObj) dateObj = new Date(0);
crashlogFiles.push({
name: crashlogDirectoryTree.children[i].name,
size: crashlogDirectoryTree.children[i].size,
date: dateObj,
permalink: `${config.baseDriveDownloadUrl}${dongle_id}/${dongleIdHash}/crash/${crashlogDirectoryTree.children[i].name}`
permalink: `${config.baseDriveDownloadUrl}${dongle_id}/${dongleIdHash}/crash/${crashlogDirectoryTree.children[i].name}`,
});
}
crashlogFiles.sort((a, b) => ((a.date < b.date) ? 1 : -1));
@ -228,15 +225,14 @@ async function getBootlogs(dongle_id) {
let dateObj = null;
try {
dateObj = Date.parse(timeString);
}
catch (exception) {}
} catch (exception) {}
if (!dateObj) dateObj = new Date(0);
bootlogFiles.push({
name: bootlogDirectoryTree.children[i].name,
size: bootlogDirectoryTree.children[i].size,
date: dateObj,
permalink: `${config.baseDriveDownloadUrl}${dongle_id}/${dongleIdHash}/boot/${bootlogDirectoryTree.children[i].name}`
permalink: `${config.baseDriveDownloadUrl}${dongle_id}/${dongleIdHash}/boot/${bootlogDirectoryTree.children[i].name}`,
});
}
bootlogFiles.sort((a, b) => ((a.date < b.date) ? 1 : -1));
@ -261,5 +257,5 @@ module.exports = {
// drive stuff, move maybe?
getDrives,
getBootlogs,
getCrashlogs
getCrashlogs,
};

View File

@ -39,5 +39,5 @@ function formatDate(timestampMs) {
}
module.exports = {
formatDuration, simpleStringify, formatDate
formatDuration, simpleStringify, formatDate,
};

View File

@ -9,5 +9,5 @@ module.exports = async (models, logger, models_sqli) => ({
mailing: require('./mailing')(models, logger),
users: require('./users'),
admin: require('./admin'),
devices: require('./devices')
devices: require('./devices'),
});

View File

@ -10,12 +10,12 @@ const transporter = nodemailer.createTransport(
port: config.smtpPort,
auth: {
user: config.smtpUser,
pass: config.smtpPassword
pass: config.smtpPassword,
},
logger: true,
debug: false
debug: false,
},
{ from: config.smtpFrom }
{ from: config.smtpFrom },
);
async function sendEmailVerification(token, email) {
@ -30,12 +30,11 @@ async function sendEmailVerification(token, email) {
from: config.smtpFrom,
to: email.trim(),
subject: 'RetroPilot Registration Token',
text: `Your Email Registration Token Is: "${token}"`
text: `Your Email Registration Token Is: "${token}"`,
};
error, info = await transporter.sendMail(message);
}
catch (exception) {
} catch (exception) {
logger.warn(`Email to ${email} FAILED ${exception} - ${token}`);
}
@ -52,6 +51,6 @@ module.exports = (_models, _logger) => {
logger = _logger;
return {
sendEmailVerification
sendEmailVerification,
};
};

View File

@ -12,8 +12,7 @@ function initializeStorage() {
var verifiedPath = mkDirByPathSync(config.storagePath, { isRelativeToScript: (config.storagePath.indexOf('/') !== 0) });
if (verifiedPath != null) {
logger.info(`Verified storage path ${verifiedPath}`);
}
else {
} else {
logger.error(`Unable to verify storage path '${config.storagePath}', check filesystem / permissions`);
process.exit();
}
@ -29,8 +28,7 @@ function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
const curDir = path.resolve(baseDir, parentDir, childDir);
try {
fs.mkdirSync(curDir);
}
catch (err) {
} catch (err) {
// console.debug(err);
if (err.code === 'EEXIST') { // curDir already exists!
return curDir;
@ -57,8 +55,7 @@ function writeFileSync(path, buffer, permission) {
let fileDescriptor;
try {
fileDescriptor = fs.openSync(path, 'w', permission);
}
catch (e) {
} catch (e) {
fs.chmodSync(path, permission);
fileDescriptor = fs.openSync(path, 'w', permission);
}
@ -104,8 +101,7 @@ async function updateTotalStorageUsed() {
if (verifiedPath !== null) {
try {
totalStorageUsed = execSync(`du -hs ${verifiedPath} | awk -F'\t' '{print $1;}'`).toString();
}
catch (exception) {
} catch (exception) {
totalStorageUsed = 'Unsupported Platform';
logger.debug('Unable to calculate storage used, only supported on systems with \'du\' available');
}
@ -129,6 +125,6 @@ module.exports = (_models, _logger) => {
writeFileSync,
moveUploadedFile,
updateTotalStorageUsed,
getTotalStorageUsed
getTotalStorageUsed,
};
};

View File

@ -1,4 +1 @@
function getUploadURL(driveType) {}
function getUploadURL(driveType) {}

View File

@ -25,7 +25,7 @@ async function createAccount(email, password) {
password,
created: Date.now(),
last_ping: Date.now(),
email_verify_token: emailToken
email_verify_token: emailToken,
});
const didAccountRegister = await models_orm.models.accounts.findOne({ where: { email } });
@ -38,7 +38,7 @@ async function createAccount(email, password) {
async function verifyEmailToken(token) {
if (!token) return { success: false, status: 400, data: { missingToken: true } };
const account = await models_orm.models.accounts.findOne(
{ where: { email_verify_token: token } }
{ where: { email_verify_token: token } },
);
if (account === null) return { success: false, status: 404, data: { badToken: true } };
@ -48,13 +48,13 @@ async function verifyEmailToken(token) {
const update = models_orm.models.accounts.update(
{
verified: true
verified: true,
},
{
where: {
id: account.id
}
}
id: account.id,
},
},
);
return { success: true, status: 200, data: { successfullyVerified: true } };
@ -69,5 +69,5 @@ module.exports = {
createAccount,
verifyEmailToken,
getAccountFromId,
getAllUsers
getAllUsers,
};

View File

@ -1,10 +1,10 @@
module.exports = {
apps : [{
name : "Retropilot Service",
script : "./server.js",
apps: [{
name: 'Retropilot Service',
script: './server.js',
env_development: {
NODE_ENV: "development",
}
NODE_ENV: 'development',
},
}],
}
};

View File

@ -6,41 +6,41 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
account_id: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
device_id: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
action: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
user_ip: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
device_ip: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
meta: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
created_at: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
dongle_id: {
allowNull: true,
type: DataTypes.TEXT
}
type: DataTypes.TEXT,
},
}, {
timestamps: false
timestamps: false,
});
};

View File

@ -6,34 +6,34 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
device_id: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
type: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
data: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
created_at: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
uuid: {
allowNull: false,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
resolved_at: {
allowNull: true,
type: DataTypes.INTEGER
}
type: DataTypes.INTEGER,
},
}, {
timestamps: false
timestamps: false,
});
};

View File

@ -6,34 +6,34 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
account_id: {
allowNull: false,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
device_id: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
athena: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
unpair: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
view_drives: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
created_at: {
allowNull: true,
type: DataTypes.INTEGER
}
type: DataTypes.INTEGER,
},
}, {
timestamps: false
timestamps: false,
});
};

View File

@ -14,6 +14,6 @@ module.exports = (_db) => {
getAccountFromId,
getAccountFromVerifyToken,
verifyAccountEmail,
banAccount
banAccount,
};
};

View File

@ -8,60 +8,60 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
dongle_id: {
allowNull: false,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
account_id: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
imei: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
serial: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
device_type: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
public_key: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
created: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
last_ping: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
storage_used: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
max_storage: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
ignore_uploads: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
nickname: {
allowNull: true,
type: DataTypes.TEXT
}
type: DataTypes.TEXT,
},
},
{
timestamps: false
}
timestamps: false,
},
);
};

View File

@ -1,6 +1,3 @@
let db;
function getDrives(dongleId) {
@ -8,21 +5,22 @@ function getDrives(dongleId) {
}
async function getDevice(dongleId) {
return await db.get('SELECT * FROM devices WHERE dongle_id = ?', dongleId);
return await db.get('SELECT * FROM devices WHERE dongle_id = ?', dongleId);
}
async function deviceCheckIn(dongleId) {
return await db.run(
'UPDATE devices SET last_ping = ? WHERE dongle_id = ?',
Date.now(), dongleId
);
return await db.run(
'UPDATE devices SET last_ping = ? WHERE dongle_id = ?',
Date.now(),
dongleId,
);
}
module.exports = (_db) => {
db = _db;
db = _db;
return {
getDevice,
deviceCheckIn
}
}
return {
getDevice,
deviceCheckIn,
};
};

View File

@ -8,71 +8,71 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
identifier: {
allowNull: false,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
dongle_id: {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
max_segment: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
upload_complete: {
allowedNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
duration: {
allowNull: true,
type: DataTypes.NUMBER
type: DataTypes.NUMBER,
},
distance_meters: {
allowNull: true,
type: DataTypes.NUMBER
type: DataTypes.NUMBER,
},
filesize: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
is_processed: {
allowNull: true,
type: DataTypes.BOOLEAN
type: DataTypes.BOOLEAN,
},
created: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
last_upload: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
is_preserved: {
allowNull: true,
type: DataTypes.BOOLEAN
type: DataTypes.BOOLEAN,
},
is_deleted: {
allowNull: true,
type: DataTypes.BOOLEAN
type: DataTypes.BOOLEAN,
},
drive_date: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
is_physically_removed: {
allowNull: true,
type: DataTypes.BOOLEAN
type: DataTypes.BOOLEAN,
},
metadata: {
allowNull: true,
type: DataTypes.TEXT
}
type: DataTypes.TEXT,
},
},
{
timestamps: false
}
timestamps: false,
},
);
};

View File

@ -1,55 +1,49 @@
const sqlite3 = require('sqlite3')
const {open} = require('sqlite')
const config = require('./../config');
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
const config = require('../config');
async function validateDatabase(db, logger) {
try {
db = await open({
filename: config.databaseFile,
driver: sqlite3.Database,
mode: sqlite3.OPEN_READWRITE
});
await db.get('SELECT * FROM accounts LIMIT 1')
await db.get('SELECT * FROM devices LIMIT 1')
await db.get('SELECT * FROM drives LIMIT 1')
await db.get('SELECT * FROM drive_segments LIMIT 1')
} catch (exception) {
logger.error(exception);
process.exit();
}
try {
db = await open({
filename: config.databaseFile,
driver: sqlite3.Database,
mode: sqlite3.OPEN_READWRITE,
});
await db.get('SELECT * FROM accounts LIMIT 1');
await db.get('SELECT * FROM devices LIMIT 1');
await db.get('SELECT * FROM drives LIMIT 1');
await db.get('SELECT * FROM drive_segments LIMIT 1');
} catch (exception) {
logger.error(exception);
process.exit();
}
}
module.exports = async (logger) => {
let db;
let db;
try {
db = await open({
filename: config.databaseFile,
driver: sqlite3.Database,
mode: sqlite3.OPEN_READWRITE
});
try {
db = await open({
filename: config.databaseFile,
driver: sqlite3.Database,
mode: sqlite3.OPEN_READWRITE,
});
} catch (exception) {
logger.error(exception);
process.exit();
}
} catch (exception) {
logger.error(exception);
process.exit();
}
// I'm not sure we _really_ need to wait for this, since it'll exit the application if it's invalid anyway.
// I'm not sure we _really_ need to wait for this, since it'll exit the application if it's invalid anyway.
await validateDatabase(db, logger);
await validateDatabase(db, logger);
return {
models: {
drivesModel: require('./drives')(db),
users: require('./users')(db),
return {
models: {
drivesModel: require('./drives')(db),
users: require('./users')(db),
// TODO remove access to DB queries from non models
__db: db // to be removed when db queries are removed from outside models.
}
}
}
// TODO remove access to DB queries from non models
__db: db, // to be removed when db queries are removed from outside models.
},
};
};

View File

@ -5,7 +5,7 @@ const { Sequelize } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'database.sqlite'
storage: 'database.sqlite',
});
sequelize.options.logging = () => {};
@ -16,7 +16,7 @@ const modelDefiners = [
require('./users.model'),
require('./athena_action_log.model'),
require('./athena_returned_data.model'),
require('./device_authorised_users.model')
require('./device_authorised_users.model'),
];
for (const modelDefiner of modelDefiners) {

View File

@ -1,45 +1,44 @@
let db;
async function userPing(email) {
return await db.run('UPDATE accounts SET last_ping = ? WHERE email = ?', Date.now(), email);
return await db.run('UPDATE accounts SET last_ping = ? WHERE email = ?', Date.now(), email);
}
async function getAccountFromEmail(email) {
return await db.get('SELECT * FROM accounts WHERE LOWER(email) = ?', email);
return await db.get('SELECT * FROM accounts WHERE LOWER(email) = ?', email);
}
async function getAccountFromVerifyToken(token) {
return await db.get('SELECT * FROM accounts WHERE email_verify_token = ?', token);
return await db.get('SELECT * FROM accounts WHERE email_verify_token = ?', token);
}
async function getAccountFromId(id) {
return await db.get('SELECT * FROM accounts WHERE id = ?', id);
return await db.get('SELECT * FROM accounts WHERE id = ?', id);
}
async function createUser(email, password, created, lastPing, emailToken) {
return await db.get('INSERT INTO accounts (email, password, created, last_ping, email_verify_token) VALUES (?,?,?,?,?)', email, password, created, lastPing, emailToken)
return await db.get('INSERT INTO accounts (email, password, created, last_ping, email_verify_token) VALUES (?,?,?,?,?)', email, password, created, lastPing, emailToken);
}
async function verifyAccountEmail(email, verified, newToken) {
verified = verified === true ? 1 : 0
return await db.get('UPDATE accounts SET verified=? WHERE email = ?', verified, email);
verified = verified === true ? 1 : 0;
return await db.get('UPDATE accounts SET verified=? WHERE email = ?', verified, email);
}
async function banAccount(ban, userId) {
return await db.get('UPDATE accounts SET banned=? WHERE id = ?', ban ? 1 : 0, userId)
return await db.get('UPDATE accounts SET banned=? WHERE id = ?', ban ? 1 : 0, userId);
}
module.exports = (_db) => {
db = _db;
db = _db;
return {
userPing,
getAccountFromEmail,
createUser,
getAccountFromId,
getAccountFromVerifyToken,
verifyAccountEmail,
banAccount
}
}
return {
userPing,
getAccountFromEmail,
createUser,
getAccountFromId,
getAccountFromVerifyToken,
verifyAccountEmail,
banAccount,
};
};

View File

@ -6,33 +6,33 @@ module.exports = (sequelize) => {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
email: {
allowNull: false,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
password: {
allowNull: true,
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
created: {
allowNull: true,
type: DataTypes.NUMBER
type: DataTypes.NUMBER,
},
last_ping: {
allowNull: true,
type: DataTypes.NUMBER
type: DataTypes.NUMBER,
},
'2fa_token': {
allowNull: true,
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
admin: {
allowNull: true,
type: DataTypes.INTEGER
}
type: DataTypes.INTEGER,
},
}, {
timestamps: false
timestamps: false,
});
};

View File

@ -2,128 +2,108 @@ const router = require('express').Router();
const bodyParser = require('body-parser');
const crypto = require('crypto');
const { route } = require('../../server');
const config = require('./../../config');
const deviceController = require('./../../controllers/devices')
const config = require('../../config');
const deviceController = require('../../controllers/devices');
function runAsyncWrapper(callback) {
return function (req, res, next) {
callback(req, res, next)
.catch(next)
}
return function (req, res, next) {
callback(req, res, next)
.catch(next);
};
}
let models;
let controllers;
let logger;
// probs should put middleware somewhere else
router.use(async function (req, res, next) {
const currentAdmin = await controllers.admin.isCurrentUserAdmin(true, req);
if (currentAdmin.isAdmin === false) {
return res.status(402).json({error:true, msg: 'NOT AUTHORISED', status: 403}).end();
} else {
next();
}
router.use(async (req, res, next) => {
const currentAdmin = await controllers.admin.isCurrentUserAdmin(true, req);
if (currentAdmin.isAdmin === false) {
return res.status(402).json({ error: true, msg: 'NOT AUTHORISED', status: 403 }).end();
}
next();
});
// TODO
// TODO
router.get('/user/:userId/ban/:ban', runAsyncWrapper(async (req, res) => {
const banResult = await controllers.admin.banAccount(req.params.ban, req.params.userId)
if (banResult.hasOwnProperty('success') && banResult.success === true) {
res.status(200).json(banResult);
} else {
res.status(500).json(banResult)
}
const banResult = await controllers.admin.banAccount(req.params.ban, req.params.userId);
if (banResult.hasOwnProperty('success') && banResult.success === true) {
res.status(200).json(banResult);
} else {
res.status(500).json(banResult);
}
}));
router.get('/user/:userId/get/devices', runAsyncWrapper(async (req, res) => {
if (!req.params.userId) { return req.status(400).json({error: true, msg: 'MISSING DATA', status: 400})}
if (!req.params.userId) { return req.status(400).json({ error: true, msg: 'MISSING DATA', status: 400 }); }
return res.status(200).json({success: true, data: controllers.devices.getDevices(req.params.userId)})
return res.status(200).json({ success: true, data: controllers.devices.getDevices(req.params.userId) });
}));
router.get('/user/', runAsyncWrapper(async (req, res) => {
console.warn("PROCESSED")
return res.status(200).json({success: true, data: await controllers.users.getAllUsers()})
console.warn('PROCESSED');
return res.status(200).json({ success: true, data: await controllers.users.getAllUsers() });
}));
router.get('/device/:dongle_id', runAsyncWrapper(async (req, res) => {
if (!req.params.dongle_id) { return req.status(400).json({error: true, msg: 'MISSING DATA', status: 400})}
return res.status(200).json({success: true, data: await controllers.devices.getDeviceFromDongle(getDeviceFromDongle)})
if (!req.params.dongle_id) { return req.status(400).json({ error: true, msg: 'MISSING DATA', status: 400 }); }
return res.status(200).json({ success: true, data: await controllers.devices.getDeviceFromDongle(getDeviceFromDongle) });
}));
router.get('/device/:dongle_id/pair/:user_id', runAsyncWrapper(async (req, res) => {
if (!req.params.dongle_id || !req.params.user_id) { return req.status(400).json({error: true, msg: 'MISSING DATA', status: 400})}
const pairDeviceToAccountId = await controllers.devices.pairDeviceToAccountId(req.params.dongle_id, req.params.user_id)
if (!req.params.dongle_id || !req.params.user_id) { return req.status(400).json({ error: true, msg: 'MISSING DATA', status: 400 }); }
return res.status(200).json(pairDeviceToAccountId)
const pairDeviceToAccountId = await controllers.devices.pairDeviceToAccountId(req.params.dongle_id, req.params.user_id);
return res.status(200).json(pairDeviceToAccountId);
}));
router.get('/device', runAsyncWrapper(async (req, res) => {
const filteredDevices = await controllers.devices.getAllDevicesFiltered();
console.log("fil", filteredDevices)
return res.status(200).json({success: true, data: filteredDevices})
const filteredDevices = await controllers.devices.getAllDevicesFiltered();
console.log('fil', filteredDevices);
return res.status(200).json({ success: true, data: filteredDevices });
}));
router.get('/device/:dongle_id/ignore/:ignore_uploads', runAsyncWrapper(async (req, res) => {
if (!req.params.dongle_id || !req.params.ignore_uploads) { return req.status(400).json({error: true, msg: 'MISSING DATA', status: 400})}
if (!req.params.dongle_id || !req.params.ignore_uploads) { return req.status(400).json({ error: true, msg: 'MISSING DATA', status: 400 }); }
}));
router.get('/admin/device/:dongle_id/ignore/:ignore_uploads', runAsyncWrapper(async (req, res) => {
if (!req.params.dongle_id || !req.params.ignore_uploads) { return req.status(400).json({error: true, msg: 'MISSING DATA', status: 400})}
if (!req.params.dongle_id || !req.params.ignore_uploads) { return req.status(400).json({ error: true, msg: 'MISSING DATA', status: 400 }); }
let ignore = null;
let ignore = null;
switch (req.params.ignore_uploads) {
case "true":
ignore = true
break;
case "false":
ignore = false
break;
default:
return res.json({error: true, msg: 'MISSING DATA'})
break
}
switch (req.params.ignore_uploads) {
case 'true':
ignore = true;
break;
case 'false':
ignore = false;
break;
default:
return res.json({ error: true, msg: 'MISSING DATA' });
break;
}
if (ignore === null) {return}
await controllers.devices.setIgnoredUploads(req.params.dongle_id);
return res.status(200).json({success: true});
if (ignore === null) { return; }
await controllers.devices.setIgnoredUploads(req.params.dongle_id);
return res.status(200).json({ success: true });
}));
router.get('/device/:dongle_id/athena/reboot', runAsyncWrapper(async (req, res) => {
req.athenaWebsocketTemp.rebootDevice(req.params.dongle_id)
res.send("ok");
req.athenaWebsocketTemp.rebootDevice(req.params.dongle_id);
res.send('ok');
}));
module.exports = (_models, _controllers, _logger) => {
models = _models;
controllers = _controllers;
logger = _logger;
models = _models;
controllers = _controllers;
logger = _logger;
return router;
}
return router;
};

View File

@ -18,7 +18,7 @@ let logger;
router.put('/backend/post_upload', bodyParser.raw({
inflate: true,
limit: '100000kb',
type: '*/*'
type: '*/*',
}), runAsyncWrapper(async (req, res) => {
// TODO update buffer functions project wide
var buf = new Buffer(req.body.toString('binary'), 'binary');
@ -38,23 +38,20 @@ router.put('/backend/post_upload', bodyParser.raw({
logger.error(`HTTP.PUT /backend/post_upload token mismatch (${token} vs ${req.query.token})`);
res.status(400);
res.send('Malformed request');
}
else {
} else {
logger.info('HTTP.PUT /backend/post_upload permissions checked, calling moveUploadedFile');
const moveResult = controllers.storage.moveUploadedFile(buf, directory, filename);
if (moveResult === false) {
logger.error('HTTP.PUT /backend/post_upload moveUploadedFile failed');
res.status(500);
res.send('Internal Server Error');
}
else {
} else {
logger.info(`HTTP.PUT /backend/post_upload succesfully uploaded to ${moveResult}`);
res.status(200);
res.json(['OK']);
}
}
}
else { // boot or crash upload
} else { // boot or crash upload
var filename = req.query.file;
var directory = req.query.dir;
var token = crypto.createHmac('sha256', config.applicationSalt).update(dongleId + filename + directory + ts).digest('hex');
@ -64,16 +61,14 @@ router.put('/backend/post_upload', bodyParser.raw({
logger.error(`HTTP.PUT /backend/post_upload token mismatch (${token} vs ${req.query.token})`);
res.status(400);
res.send('Malformed request');
}
else {
} else {
logger.info('HTTP.PUT /backend/post_upload permissions checked, calling moveUploadedFile');
var moveResult = controllers.storage.moveUploadedFile(buf, directory, filename);
if (moveResult === false) {
logger.error('HTTP.PUT /backend/post_upload moveUploadedFile failed');
res.status(500);
res.send('Internal Server Error');
}
else {
} else {
logger.info(`HTTP.PUT /backend/post_upload succesfully uploaded to ${moveResult}`);
res.status(200);
res.json(['OK']);
@ -120,13 +115,13 @@ router.get('/v1.1/devices/:dongleId/stats', runAsyncWrapper(async (req, res) =>
all: {
routes: 0,
distance: 0,
minutes: 0
minutes: 0,
},
week: {
routes: 0,
distance: 0,
minutes: 0
}
minutes: 0,
},
};
@ -259,8 +254,7 @@ async function upload(req, res) {
responseUrl = `${config.baseUploadUrl}?file=${filename}&dir=${directory}&dongleId=${dongleId}&ts=${ts}&token=${token}`;
logger.info(`HTTP.UPLOAD_URL matched '${uploadType}' file upload, constructed responseUrl: ${responseUrl}`);
}
else {
} else {
// "2021-04-12--01-44-25--0/qlog.bz2" for example
const subdirPosition = path.split('--', 2).join('--').length;
const filenamePosition = path.indexOf('/');
@ -325,7 +319,7 @@ async function upload(req, res) {
false,
false
false,
);
const driveSegmentResult = await models.__db.run(
@ -346,12 +340,11 @@ async function upload(req, res) {
false,
Date.now()
Date.now(),
);
logger.info(`HTTP.UPLOAD_URL created new drive #${JSON.stringify(driveResult.lastID)}`);
}
else {
} else {
const driveResult = await models.__db.run(
'UPDATE drives SET last_upload = ?, max_segment = ?, upload_complete = ?, is_processed = ? WHERE identifier = ? AND dongle_id = ?',
Date.now(),
@ -364,7 +357,7 @@ async function upload(req, res) {
driveName,
dongleId
dongleId,
);
const drive_segment = await models.__db.get('SELECT * FROM drive_segments WHERE drive_identifier = ? AND dongle_id = ? AND segment_id = ?', driveName, dongleId, segment);
@ -388,10 +381,9 @@ async function upload(req, res) {
false,
Date.now()
Date.now(),
);
}
else {
} else {
const driveSegmentResult = await models.__db.run(
'UPDATE drive_segments SET upload_complete = ?, is_stalled = ? WHERE drive_identifier = ? AND dongle_id = ? AND segment_id = ?',
false,
@ -402,7 +394,7 @@ async function upload(req, res) {
dongleId,
segment
segment,
);
}
@ -414,8 +406,7 @@ async function upload(req, res) {
if (responseUrl != null) {
res.status(200);
res.json({ url: responseUrl, headers: { 'Content-Type': 'application/octet-stream' } });
}
else {
} else {
logger.error('HTTP.UPLOAD_URL unable to match request, responding with HTTP 400');
res.status(400);
res.send('Malformed Request.');
@ -473,7 +464,7 @@ router.post('/v2/pilotauth/', bodyParser.urlencoded({ extended: true }), async (
Date.now(),
0
0,
);
const device = await models.__db.get('SELECT * FROM devices WHERE dongle_id = ?', dongleId);
@ -484,15 +475,14 @@ router.post('/v2/pilotauth/', bodyParser.urlencoded({ extended: true }), async (
return;
}
}
}
else {
} else {
const result = await models.__db.run(
'UPDATE devices SET last_ping = ?, public_key = ? WHERE dongle_id = ?',
Date.now(),
public_key,
device.dongle_id
device.dongle_id,
);
logger.info(`HTTP.V2.PILOTAUTH REACTIVATING KNOWN DEVICE (${imei1}, ${serial}) with dongle_id ${device.dongle_id}`);
@ -545,7 +535,7 @@ router.get('/useradmin/cabana_drive/:extendedRouteIdentifier', runAsyncWrapper(a
driveUrl,
name: `${drive.dongle_id}|${drive.identifier}`,
driveIdentifier: drive.identifier,
dongleId: drive.dongle_id
dongleId: drive.dongle_id,
});
}));

View File

@ -12,11 +12,10 @@ async function isAuthenticated(req, res, next) {
res.json({
success: true,
data: {
authenticated: false
}
authenticated: false,
},
});
}
else {
} else {
req.account = account;
next();
}
@ -30,16 +29,16 @@ router.get('/retropilot/0/useradmin/session', isAuthenticated, async (req, res)
success: true,
data: {
authenticated: true,
user: account.dataValues
}
user: account.dataValues,
},
});
}
return res.json({
success: true,
data: {
authenticated: false
}
authenticated: false,
},
});
});
@ -56,16 +55,15 @@ router.post('/retropilot/0/useradmin/auth', bodyParser.urlencoded({ extended: tr
data: {
authenticated: true,
jwt: signIn.jwt,
user: account.dataValues
}
user: account.dataValues,
},
});
}
else {
} else {
res.json({
success: true,
data: {
authenticated: false
}
authenticated: false,
},
});
}
});
@ -80,8 +78,7 @@ router.get('/session/get', async (req, res) => {
if (!account) {
res.json({ success: true, hasSession: false, session: {} });
}
else {
} else {
res.json({ success: true, hasSession: false, session: account });
}
});

View File

@ -12,8 +12,7 @@ async function isAuthenticated(req, res, next) {
if (account === null) {
res.json({ success: false, msg: 'NOT_AUTHENTICATED1' });
}
else {
} else {
req.account = account;
next();
}

View File

@ -20,7 +20,7 @@ const whitelistParams = {
getsiminfo: true,
getnetworktype: true,
getnetworks: true,
takesnapshot: true
takesnapshot: true,
};
router.get('/dongle/:dongle_id/connected', async (req, res) => {
@ -42,7 +42,7 @@ router.get('/dongle/:dongle_id/connected', async (req, res) => {
const deviceConnected = await req.athenaWebsocketTemp.isDeviceConnected(device.id, account.id, device.dongle_id);
return res.status(200).json({
success: true, dongle_id: device.dongle_id, data: deviceConnected
success: true, dongle_id: device.dongle_id, data: deviceConnected,
});
});
@ -69,7 +69,7 @@ router.get('/dongle/:dongle_id/send/:method/', async (req, res) => {
const data = await req.athenaWebsocketTemp.invoke(req.params.method, null, device.dongle_id, account.id);
return res.status(200).json({
success: true, dongle_id: device.dongle_id, method: req.params.method, data
success: true, dongle_id: device.dongle_id, method: req.params.method, data,
});
});
@ -108,7 +108,7 @@ router.get('/dongle/:dongle_id/temp/nav/:lat/:long', async (req, res) => {
const data = await req.athenaWebsocketTemp.invoke('setNavDestination', { latitude: req.params.lat, longitude: req.params.long }, device.dongle_id, account.id);
return res.status(200).json({
success: true, dongle_id: device.dongle_id, method: req.params.method, data
success: true, dongle_id: device.dongle_id, method: req.params.method, data,
});
});

View File

@ -7,5 +7,5 @@ module.exports = (_models, _controllers, _logger) => ({
realtime: require('./api/realtime'),
deviceApi: require('./api/devices'),
authenticationApi: require('./api/authentication')
authenticationApi: require('./api/authentication'),
});

View File

@ -24,8 +24,7 @@ router.post('/retropilot/0/useradmin/auth', bodyParser.urlencoded({ extended: tr
if (signIn.success) {
res.cookie('jwt', signIn.jwt);
res.redirect('/useradmin/overview');
}
else {
} else {
res.redirect(`/useradmin?status=${encodeURIComponent('Invalid credentials or banned account')}`);
}
}));
@ -46,14 +45,14 @@ router.get('/retropilot/0/useradmin', runAsyncWrapper(async (req, res) => {
serverStats: {
config: {
registerAllowed: config.allowAccountRegistration,
welcomeMessage: config.welcomeMessage
welcomeMessage: config.welcomeMessage,
},
accounts: accounts.num,
devices: devices.num,
drives: drives.num,
storageUsed: await controllers.storage.getTotalStorageUsed()
}
}
storageUsed: await controllers.storage.getTotalStorageUsed(),
},
},
}).status(200);
}));
@ -178,8 +177,8 @@ router.get('/retropilot/0/overview', runAsyncWrapper(async (req, res) => {
success: true,
data: {
account,
devices
}
devices,
},
}).status(200);
}));
@ -199,7 +198,7 @@ router.get('/retropilot/0/unpair_device/:dongleId', runAsyncWrapper(async (req,
const result = await models.__db.run(
'UPDATE devices SET account_id = ? WHERE dongle_id = ?',
0,
req.params.dongleId
req.params.dongleId,
);
res.json({ success: true, data: { unlink: true } });
@ -215,10 +214,9 @@ router.post('/retropilot/0/pair_device', bodyParser.urlencoded({ extended: true
if (pairDevice.success === true) {
res.json({
success: true, msg: 'Paired', status: 200, data: pairDevice
success: true, msg: 'Paired', status: 200, data: pairDevice,
});
}
else {
} else {
res.json({ success: false, msg: 'error', data: pairDevice });
}
}));
@ -233,8 +231,7 @@ router.post('/retropilot/0/password/change', bodyParser.urlencoded({ extended: t
if (pwChange.success === true) {
res.json({ success: true });
}
else {
} else {
res.json({ success: false, data: pwChange });
}
}));

235
server.js
View File

@ -6,165 +6,134 @@ const http = require('http');
const https = require('https');
const express = require('express');
const cors = require('cors');
const rateLimit = require("express-rate-limit");
const rateLimit = require('express-rate-limit');
log4js.configure({
appenders: {logfile: {type: "file", filename: "server.log"}, out: {type: 'console'} /*{type: "file", filename: "server1.log"}*/},
categories: {default: {appenders: ['out', 'logfile'], level: 'info'}},
appenders: { logfile: { type: 'file', filename: 'server.log' }, out: { type: 'console' } /* {type: "file", filename: "server1.log"} */ },
categories: { default: { appenders: ['out', 'logfile'], level: 'info' } },
});
const logger = log4js.getLogger('default');
// TODO evaluate if this is the best way to determine the root of project
global.__basedir = __dirname;
let models = require('./models/index');
let models_sqli = require('./models/index.model');
let controllers = require('./controllers');
let routers = require('./routes')
const athena = require('./websocket/athena');
const webWebsocket = require('./websocket/web');
var cookieParser = require('cookie-parser');
const webWebsocket = require('./websocket/web');
const athena = require('./websocket/athena');
let routers = require('./routes');
const models_sqli = require('./models/index.model');
let controllers = require('./controllers');
let models = require('./models/index');
const router = require('./routes/api/realtime');
let db;
// TODO
function runAsyncWrapper(callback) {
return function (req, res, next) {
callback(req, res, next)
.catch(next)
}
return function (req, res, next) {
callback(req, res, next)
.catch(next);
};
}
const app = express();
const athenaRateLimit = rateLimit({
windowMs: 30000,
max: config.athena.api.ratelimit
windowMs: 30000,
max: config.athena.api.ratelimit,
});
const web = async () => {
// TODO clean up
const _models = await models(logger);
db = _models.models.__db;
models = _models.models;
// TODO clean up
const _models = await models(logger);
db = _models.models.__db;
models = _models.models;
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "http://localhost:3000");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
const _controllers = await controllers(models, logger);
controllers = _controllers;
controllers.storage.initializeStorage();
await controllers.storage.updateTotalStorageUsed();
routers = routers(models, controllers, logger);
app.use(routers.api);
app.use(routers.useradmin);
app.use(routers.authenticationApi);
if (config.athena.enabled) {
app.use((req, res, next) => {
req.athenaWebsocketTemp = athena;
return next();
});
app.use('/admin', routers.admin);
app.use('/realtime', athenaRateLimit);
app.use('/realtime', routers.realtime);
} else {
logger.log('Athena disabled');
}
app.use(cors({ origin: 'http://localhost:3000' }));
app.use(cookieParser());
app.use('/favicon.ico', express.static('static/favicon.ico'));
app.use(config.baseDriveDownloadPathMapping, express.static(config.storagePath));
app.use(routers.deviceApi);
app.use('/.well-known', express.static('.well-known'));
app.use('/cabana', express.static('cabana/'));
app.get('/', async (req, res) => {
res.status(200);
var response = '<html style="font-family: monospace"><h2>404 Not found</h2>'
+ 'Are you looking for the <a href="/useradmin">useradmin dashboard</a>?';
res.send(response);
});
app.get('*', runAsyncWrapper(async (req, res) => {
logger.error(`HTTP.GET unhandled request: ${controllers.helpers.simpleStringify(req)}, ${controllers.helpers.simpleStringify(res)}`);
res.status(404);
res.send('Not Implemented');
}));
app.post('*', runAsyncWrapper(async (req, res) => {
logger.error(`HTTP.POST unhandled request: ${controllers.helpers.simpleStringify(req)}, ${controllers.helpers.simpleStringify(res)}`);
res.status(404);
res.send('Not Implemented');
}));
};
lockfile.lock('retropilot_server.lock', { realpath: false, stale: 30000, update: 2000 })
.then((release) => {
console.log('STARTING SERVER...');
web();
(async () => {
var privateKey = fs.readFileSync(config.sslKey, 'utf8');
var certificate = fs.readFileSync(config.sslCrt, 'utf8');
var sslCredentials = { key: privateKey, cert: certificate/* , ca: fs.readFileSync('certs/ca.crt') */ };
var httpServer = http.createServer(app);
var httpsServer = https.createServer(sslCredentials, app);
httpServer.listen(config.httpPort, config.httpInterface, () => {
logger.info(`Retropilot Server listening at http://${config.httpInterface}:${config.httpPort}`);
});
const _controllers = await controllers(models, logger);
controllers = _controllers;
controllers.storage.initializeStorage();
await controllers.storage.updateTotalStorageUsed();
routers = routers(models, controllers, logger)
app.use(routers.api);
app.use(routers.useradmin);
app.use(routers.authenticationApi)
if (config.athena.enabled) {
app.use((req, res, next) => {
req.athenaWebsocketTemp = athena;
return next();
});
app.use('/admin', routers.admin);
app.use('/realtime', athenaRateLimit);
app.use('/realtime', routers.realtime);
} else {
logger.log("Athena disabled");
}
app.use(cors({origin: 'http://localhost:3000',}));
app.use(cookieParser());
app.use('/favicon.ico', express.static('static/favicon.ico'));
app.use(config.baseDriveDownloadPathMapping, express.static(config.storagePath));
app.use(routers.deviceApi)
app.use('/.well-known', express.static('.well-known'));
app.use('/cabana', express.static('cabana/'));
app.get('/', async (req, res) => {
res.status(200);
var response = '<html style="font-family: monospace"><h2>404 Not found</h2>' +
'Are you looking for the <a href="/useradmin">useradmin dashboard</a>?';
res.send(response);
})
app.get('*', runAsyncWrapper(async (req, res) => {
logger.error("HTTP.GET unhandled request: " + controllers.helpers.simpleStringify(req) + ", " + controllers.helpers.simpleStringify(res) + "")
res.status(404);
res.send('Not Implemented');
}))
app.post('*', runAsyncWrapper(async (req, res) => {
logger.error("HTTP.POST unhandled request: " + controllers.helpers.simpleStringify(req) + ", " + controllers.helpers.simpleStringify(res) + "")
res.status(404);
res.send('Not Implemented');
}));
}
lockfile.lock('retropilot_server.lock', {realpath: false, stale: 30000, update: 2000})
.then((release) => {
console.log("STARTING SERVER...");
web();
(async () => {
var privateKey = fs.readFileSync(config.sslKey, 'utf8');
var certificate = fs.readFileSync(config.sslCrt, 'utf8');
var sslCredentials = {key: privateKey, cert: certificate/* , ca: fs.readFileSync('certs/ca.crt') */};
var httpServer = http.createServer(app);
var httpsServer = https.createServer(sslCredentials, app);
httpServer.listen(config.httpPort, config.httpInterface, () => {
logger.info(`Retropilot Server listening at http://${config.httpInterface}:${config.httpPort}`)
});
httpsServer.listen(config.httpsPort, config.httpsInterface, () => {
logger.info(`Retropilot Server listening at https://${config.httpsInterface}:${config.httpsPort}`)
});
})();
}).catch((e) => {
console.error(e)
httpsServer.listen(config.httpsPort, config.httpsInterface, () => {
logger.info(`Retropilot Server listening at https://${config.httpsInterface}:${config.httpsPort}`);
});
})();
}).catch((e) => {
console.error(e);
process.exit();
});
});
module.exports = app;

View File

@ -25,8 +25,7 @@ function invoke(command, params, dongleId, accountId, id) {
if (!id) {
uniqueID = uuid();
}
else {
} else {
uniqueID = id;
}
@ -36,7 +35,7 @@ function invoke(command, params, dongleId, accountId, id) {
device_id: websocket.device_id,
type: command,
created_at: Date.now(),
uuid: uniqueID
uuid: uniqueID,
});
websocket.send(JSON.stringify(wss.retropilotFunc.commandBuilder(command, params, uniqueID)));
@ -65,6 +64,6 @@ module.exports = (websocketServer) => {
invoke,
incoming,
deviceStatus,
realtimeCallback
realtimeCallback,
};
};

View File

@ -22,10 +22,9 @@ function __server() {
if (config.athena.secure) {
server = httpsServer.createServer({
cert: readFileSync(config.sslCrt),
key: readFileSync(config.sslKey)
key: readFileSync(config.sslKey),
});
}
else {
} else {
server = httpServer.createServer();
}
@ -91,7 +90,7 @@ async function manageConnection(ws, res) {
console.log(await models.models.athena_returned_data.update({
data: JSON.stringify(json),
resolved_at: Date.now()
resolved_at: Date.now(),
}, { where: { device_id: ws.device_id, uuid: json.id } }));
wss.retropilotFunc.actionLogger(null, null, 'ATHENA_DEVICE_MESSAGE_UNKNOWN', null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
@ -126,8 +125,7 @@ wss.retropilotFunc = {
authenticateDongle: async (ws, res, cookies) => {
try {
unsafeJwt = jsonwebtoken.decode(cookies.jwt);
}
catch (e) {
} catch (e) {
logger.info(`Athena(Websocket) - AUTHENTICATION FAILED (INVALID JWT) IP: ${ws._socket.remoteAddress}`);
wss.retropilotFunc.actionLogger(null, null, 'ATHENA_DEVICE_AUTHENTICATE_INVALID', null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
return false;
@ -139,8 +137,7 @@ wss.retropilotFunc = {
try {
verifiedJWT = jsonwebtoken.verify(cookies.jwt, device.public_key, { ignoreNotBefore: true });
}
catch (err) {
} catch (err) {
logger.info(`Athena(Websocket) - AUTHENTICATION FAILED (BAD JWT, CHECK SIGNATURE) IP: ${ws._socket.remoteAddress}`);
wss.retropilotFunc.actionLogger(null, null, 'ATHENA_DEVICE_AUTHENTICATE_INVALID', null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
return false;
@ -160,14 +157,14 @@ wss.retropilotFunc = {
},
commandBuilder: (method, params, id) => ({
method, params, jsonrpc: '2.0', id
method, params, jsonrpc: '2.0', id,
}),
actionLogger: async (account_id, device_id, action, user_ip, device_ip, meta, dongle_id) => {
models.models.athena_action_log.create({
account_id, device_id, action, user_ip, device_ip, meta, created_at: Date.now(), dongle_id
account_id, device_id, action, user_ip, device_ip, meta, created_at: Date.now(), dongle_id,
});
}
},
};

View File

@ -13,12 +13,11 @@ async function isDongleOnline(ws, msg) {
command: msg.command,
success: true,
id: msg.id || null,
data: athenaRealtime.isDeviceConnected(ws.account.id, null, msg.data.dongleId)
data: athenaRealtime.isDeviceConnected(ws.account.id, null, msg.data.dongleId),
}));
}
else {
} else {
ws.send(JSON.stringify({
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised'
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised',
}));
}
}
@ -33,12 +32,11 @@ async function rebootDongle(ws, msg) {
if (isAuthorised && isAuthorised.success === true) {
await athenaRealtime.invoke('reboot', null, msg.data.dongleId, ws.account.id, msg.id || null);
ws.send(JSON.stringify({
command: msg.command, success: true, id: msg.id || null, data: { command_issued: true }
command: msg.command, success: true, id: msg.id || null, data: { command_issued: true },
}));
}
else {
} else {
ws.send(JSON.stringify({
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised'
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised',
}));
}
}
@ -49,12 +47,11 @@ async function takeSnapshot(ws, msg) {
if (isAuthorised && isAuthorised.success === true) {
await athenaRealtime.invoke('takeSnapshot', null, msg.data.dongleId, ws.account.id, msg.id || null);
ws.send(JSON.stringify({
command: msg.command, success: true, id: msg.id || null, data: { command_issued: true }
command: msg.command, success: true, id: msg.id || null, data: { command_issued: true },
}));
}
else {
} else {
ws.send(JSON.stringify({
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised'
command: msg.command, success: false, id: msg.id || null, msg: 'not_authorised',
}));
}
}
@ -62,5 +59,5 @@ async function takeSnapshot(ws, msg) {
module.exports = {
isDongleOnline,
rebootDongle,
takeSnapshot
takeSnapshot,
};

View File

@ -35,6 +35,6 @@ module.exports = (websocket) => {
return {
getDongleOwners,
dongleStatus,
passData
passData,
};
};

View File

@ -36,7 +36,7 @@ function __server() {
function buildResponse(ws, success, msg, data) {
ws.send(JSON.stringify({
success, msg, data, timestamp: Date.now()
success, msg, data, timestamp: Date.now(),
}));
}
@ -82,7 +82,7 @@ async function manageConnection(ws, res) {
return realtimeCommands.takeSnapshot(ws, msg);
default:
return ws.send(JSON.stringify({
error: true, id: msg.id || null, msg: 'VERIFY_DATA', data: { msg }
error: true, id: msg.id || null, msg: 'VERIFY_DATA', data: { msg },
}));
}
});
@ -100,5 +100,5 @@ athenaRealtime.realtimeCallback(controls);
module.exports = {
controls,
websocketServer
websocketServer,
};

1208
worker.js

File diff suppressed because it is too large Load Diff