2022-01-12 08:02:30 -07:00
import bodyParser from 'body-parser' ;
import crypto from 'crypto' ;
2022-03-22 09:14:08 -06:00
import express from 'express' ;
2022-01-12 08:02:30 -07:00
import log4js from 'log4js' ;
2022-03-22 09:14:08 -06:00
import { validateJWT } from '../controllers/authentication' ;
2022-01-12 08:02:30 -07:00
import deviceController from '../controllers/devices' ;
2022-03-22 09:14:08 -06:00
import storageController from '../controllers/storage' ;
import { getAccountFromId } from '../controllers/users' ;
2022-03-24 16:05:05 -06:00
import { getDevice } from '../middlewares/devices' ;
2022-01-09 16:19:00 -07:00
2022-03-22 09:14:08 -06:00
const logger = log4js . getLogger ( ) ;
2022-01-12 08:02:30 -07:00
const router = express . Router ( ) ;
2021-05-21 16:17:11 -06:00
function runAsyncWrapper ( callback ) {
2022-01-08 17:38:41 -07:00
return function wrapper ( req , res , next ) {
2022-01-07 18:35:55 -07:00
callback ( req , res , next )
. catch ( next ) ;
} ;
2021-05-21 16:17:11 -06:00
}
2022-03-22 07:03:17 -06:00
// TODO(cameron): clean up this mess into separate files
2021-05-21 16:17:11 -06:00
// DRIVE & BOOT/CRASH LOG FILE UPLOAD HANDLING
router . put ( '/backend/post_upload' , bodyParser . raw ( {
2022-01-07 18:35:55 -07:00
inflate : true ,
limit : '100000kb' ,
2022-01-08 13:43:57 -07:00
type : '*/*' ,
2021-05-21 16:17:11 -06:00
} ) , runAsyncWrapper ( async ( req , res ) => {
2022-01-08 17:57:15 -07:00
const buf = Buffer . from ( req . body . toString ( 'binary' ) , 'binary' ) ;
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.PUT /backend/post_upload for dongle ${ req . query . dongleId } with body length: ${ buf . length } ` ) ;
2021-05-21 16:17:11 -06:00
2022-01-08 17:57:15 -07:00
const {
dir : directory ,
dongleId ,
file : filename ,
ts ,
} = req . query ;
2022-01-07 18:35:55 -07:00
2022-01-08 17:57:15 -07:00
const isDriveFile = filename . indexOf ( 'boot' ) !== 0 && filename . indexOf ( 'crash' ) !== 0 ;
if ( isDriveFile ) {
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.PUT /backend/post_upload DRIVE upload with filename: ${ filename } , directory: ${ directory } , token: ${ req . query . token } ` ) ;
2022-01-08 17:57:15 -07:00
} else {
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.PUT /backend/post_upload BOOT or CRASH upload with filename: ${ filename } , token: ${ req . query . token } ` ) ;
2022-01-08 17:57:15 -07:00
}
2022-01-08 17:38:41 -07:00
2022-03-24 17:36:42 -06:00
const token = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( dongleId + filename + directory + ts )
. digest ( 'hex' ) ;
2022-01-08 17:57:15 -07:00
if ( token !== req . query . token ) {
logger . error ( ` HTTP.PUT /backend/post_upload token mismatch ( ${ token } vs ${ req . query . token } ) ` ) ;
return res . status ( 400 ) . send ( 'Malformed request' ) ;
2022-01-07 18:35:55 -07:00
}
2022-01-08 17:57:15 -07:00
logger . info ( 'HTTP.PUT /backend/post_upload permissions checked, calling moveUploadedFile' ) ;
2022-01-09 17:04:04 -07:00
const moveResult = storageController . moveUploadedFile ( buf , directory , filename ) ;
2022-01-08 17:57:15 -07:00
if ( ! moveResult ) {
logger . error ( 'HTTP.PUT /backend/post_upload moveUploadedFile failed' ) ;
return res . status ( 500 ) . send ( 'Internal Server Error' ) ;
}
logger . info ( ` HTTP.PUT /backend/post_upload successfully uploaded to ${ moveResult } ` ) ;
return res . status ( 200 ) . json ( [ 'OK' ] ) ;
2022-01-07 18:35:55 -07:00
} ) ) ;
2021-06-04 20:36:50 -06:00
2022-01-07 18:35:55 -07:00
// RETURN THE PAIRING STATUS
2022-03-24 16:05:05 -06:00
router . get ( '/v1.1/devices/:dongleId' , getDevice , runAsyncWrapper ( async ( req , res ) => {
2022-03-23 17:55:01 -06:00
const { authorization } = req . headers ;
2022-01-07 18:35:55 -07:00
const { dongleId } = req . params ;
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.DEVICES called for ${ dongleId } ` ) ;
2021-06-04 20:36:50 -06:00
2022-03-24 16:05:05 -06:00
const { device } = req ;
2022-01-07 18:35:55 -07:00
if ( ! device ) {
logger . info ( ` HTTP.DEVICES device ${ dongleId } not found ` ) ;
2022-03-24 09:47:47 -06:00
return res . status ( 200 ) . json ( {
is _paired : false ,
prime : false ,
prime _type : 0 ,
} ) ;
2022-01-07 18:35:55 -07:00
}
2021-06-04 20:36:50 -06:00
2022-03-23 17:55:01 -06:00
const {
account _id : accountId ,
public _key : publicKey ,
} = device ;
const decoded = publicKey
? await validateJWT ( authorization , publicKey )
2022-01-08 16:07:09 -07:00
: null ;
2021-06-04 20:36:50 -06:00
2022-03-23 17:55:01 -06:00
if ( ( ! decoded || decoded . identity !== dongleId ) ) {
logger . info ( 'HTTP.DEVICES JWT authorization failed' , {
token : authorization ,
device ,
decoded ,
} ) ;
2022-03-24 16:05:05 -06:00
return res . status ( 401 ) . send ( 'Unauthorised.' ) ;
2022-01-07 18:35:55 -07:00
}
2021-06-08 05:01:07 -06:00
2022-03-24 09:06:25 -06:00
const PrimeType = {
None : 0 ,
Magenta : 1 ,
Lite : 2 ,
} ;
const isPaired = accountId !== 0 ;
2022-03-23 17:55:01 -06:00
const response = {
2022-03-24 09:06:25 -06:00
is _paired : isPaired ,
/ *
* Whether the account is subscribed to prime . Removed in OP 0.8 . 13. Replaced by ` prime_type ` .
* /
prime : isPaired ,
/ *
* The type of prime subscription the account is subscribed to .
* /
prime _type : isPaired ? PrimeType . Lite : PrimeType . None ,
2022-03-23 17:55:01 -06:00
} ;
logger . info ( ` HTTP.DEVICES for ${ dongleId } returning: ${ JSON . stringify ( response ) } ` ) ;
2021-06-08 05:01:07 -06:00
2022-01-08 17:38:41 -07:00
return res . status ( 200 ) . json ( response ) ;
2022-01-07 18:35:55 -07:00
} ) ) ;
2021-06-04 20:36:50 -06:00
2022-01-07 18:35:55 -07:00
// RETURN STATS FOR DASHBOARD
2022-03-24 16:05:05 -06:00
router . get ( '/v1.1/devices/:dongleId/stats' , getDevice , runAsyncWrapper ( async ( req , res ) => {
2022-01-07 18:35:55 -07:00
const { dongleId } = req . params ;
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.STATS called for ${ dongleId } ` ) ;
2022-01-07 18:35:55 -07:00
const stats = {
all : {
routes : 0 ,
distance : 0 ,
2022-01-08 13:43:57 -07:00
minutes : 0 ,
2022-01-07 18:35:55 -07:00
} ,
week : {
routes : 0 ,
distance : 0 ,
2022-01-08 13:43:57 -07:00
minutes : 0 ,
} ,
2022-01-07 18:35:55 -07:00
} ;
2021-06-04 20:36:50 -06:00
2022-03-24 16:05:05 -06:00
const { device } = req ;
2022-01-07 18:35:55 -07:00
if ( ! device ) {
logger . info ( ` HTTP.STATS device ${ dongleId } not found ` ) ;
2022-03-24 16:05:05 -06:00
return res . status ( 404 ) . send ( 'Not found.' ) ;
2022-01-07 18:35:55 -07:00
}
2022-03-23 17:55:01 -06:00
const { public _key : publicKey } = device ;
const { authorization } = req . headers ;
2022-03-24 16:05:05 -06:00
if ( ! authorization ) {
logger . info ( ` HTTP.STATS JWT missing authorization, dongleId: ${ dongleId } ` ) ;
return res . status ( 401 ) . send ( 'Unauthorised.' ) ;
}
2022-01-08 16:07:09 -07:00
const decoded = device . public _key
2022-03-23 17:55:01 -06:00
? await validateJWT ( authorization , publicKey )
2022-01-08 16:07:09 -07:00
: null ;
2022-01-07 18:35:55 -07:00
2022-03-23 17:55:01 -06:00
if ( ( ! decoded || decoded . identity !== dongleId ) ) {
logger . info ( ` HTTP.STATS JWT authorization failed, token: ${ authorization } device: ${ JSON . stringify ( device ) } , decoded: ${ JSON . stringify ( decoded ) } ` ) ;
2022-03-24 16:05:05 -06:00
return res . status ( 403 ) . send ( 'Forbidden.' ) ;
2022-01-07 18:35:55 -07:00
}
2022-01-09 03:50:27 -07:00
// TODO reimplement weekly stats
2022-03-22 09:14:08 -06:00
// const statresult = await models.get('SELECT COUNT(*) as routes, ROUND(SUM(distance_meters)/1609.34) as distance, ROUND(SUM(duration)/60) as duration FROM drives WHERE dongle_id=?', device.dongle_id);
// if (statresult != null && statresult.routes != null) {
// stats.all.routes = statresult.routes;
// stats.all.distance = statresult.distance != null ? statresult.distance : 0;
// stats.all.minutes = statresult.duration != null ? statresult.duration : 0;
// }
//
// // this determines the date at 00:00:00 UTC of last monday (== beginning of the current "ISO"week)
// const d = new Date();
// const day = d.getDay();
// const diff = d.getDate() - day + (day === 0 ? -6 : 1);
// const lastMonday = new Date(d.setDate(diff));
// lastMonday.setHours(0, 0, 0, 0);
//
// const statresultweek = await models.get('SELECT COUNT(*) as routes, ROUND(SUM(distance_meters)/1609.34) as distance, ROUND(SUM(duration)/60) as duration FROM drives WHERE dongle_id=? AND drive_date >= ?', device.dongle_id, lastMonday.getTime());
// if (statresultweek != null && statresultweek.routes != null) {
// stats.week.routes = statresultweek.routes;
// stats.week.distance = statresultweek.distance != null ? statresultweek.distance : 0;
// stats.week.minutes = statresultweek.duration != null ? statresultweek.duration : 0;
// }
2022-01-07 18:35:55 -07:00
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.STATS for ${ dongleId } returning: ${ JSON . stringify ( stats ) } ` ) ;
2022-01-08 17:38:41 -07:00
return res . status ( 200 ) . json ( stats ) ;
2022-01-07 18:35:55 -07:00
} ) ) ;
2021-06-04 20:36:50 -06:00
// RETURN USERNAME & POINTS FOR DASHBOARD
2022-03-24 16:05:05 -06:00
router . get ( '/v1/devices/:dongleId/owner' , getDevice , runAsyncWrapper ( async ( req , res ) => {
2022-01-07 18:35:55 -07:00
const { dongleId } = req . params ;
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.OWNER called for ${ dongleId } ` ) ;
2021-06-04 20:36:50 -06:00
2022-03-24 16:05:05 -06:00
const { device } = req ;
2022-01-07 18:35:55 -07:00
if ( ! device ) {
logger . info ( ` HTTP.OWNER device ${ dongleId } not found ` ) ;
2022-01-08 17:38:41 -07:00
return res . status ( 200 ) . json ( { username : 'unregisteredDevice' , points : 0 } ) ;
2022-01-07 18:35:55 -07:00
}
2021-06-04 20:36:50 -06:00
2022-03-24 16:05:05 -06:00
const { authorization } = req . headers ;
if ( ! authorization ) {
logger . info ( ` HTTP.OWNER JWT missing authorization, dongleId: ${ dongleId } ` ) ;
return res . status ( 401 ) . send ( 'Unauthorised.' ) ;
}
2022-01-08 17:38:41 -07:00
const decoded = device . public _key
2022-03-24 16:05:05 -06:00
? await validateJWT ( authorization , device . public _key )
2022-01-08 17:38:41 -07:00
: null ;
2021-06-04 20:36:50 -06:00
2022-03-23 17:55:01 -06:00
if ( ( ! decoded || decoded . identity !== dongleId ) ) {
2022-03-24 16:05:05 -06:00
logger . info ( ` HTTP.OWNER JWT authorization failed, token: ${ authorization . authorization } device: ${ JSON . stringify ( device ) } , decoded: ${ JSON . stringify ( decoded ) } ` ) ;
return res . status ( 403 ) . send ( 'Forbidden.' ) ;
2022-01-07 18:35:55 -07:00
}
let owner = '' ;
2022-03-22 09:14:08 -06:00
const points = 0 ;
2022-01-07 18:35:55 -07:00
2022-03-22 09:14:08 -06:00
let account = await getAccountFromId ( device . account _id ) ;
2022-01-09 03:50:27 -07:00
if ( account != null && account . dataValues != null ) {
2022-03-22 09:14:08 -06:00
account = account . dataValues ;
2022-01-08 17:38:41 -07:00
[ owner ] = account . email . split ( '@' ) ;
2022-01-09 03:50:27 -07:00
// TODO reimplement "points"
2022-03-22 09:14:08 -06:00
// const stats = await models.all('SELECT SUM(distance_meters) as points FROM drives WHERE dongle_id IN (SELECT dongle_id FROM devices WHERE account_id=?)', account.id);
// if (stats != null && stats.points != null) {
// points = stats.points;
// }
2022-01-07 18:35:55 -07:00
}
2021-06-04 20:36:50 -06:00
2022-01-07 18:35:55 -07:00
const response = { username : owner , points } ;
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.OWNER for ${ dongleId } returning: ${ JSON . stringify ( response ) } ` ) ;
2021-06-04 20:36:50 -06:00
2022-01-08 17:38:41 -07:00
return res . status ( 200 ) . json ( response ) ;
2022-01-07 18:35:55 -07:00
} ) ) ;
2021-06-04 20:36:50 -06:00
2022-01-07 18:35:55 -07:00
async function upload ( req , res ) {
2022-01-08 17:57:15 -07:00
let { path } = req . query ;
2022-01-07 18:35:55 -07:00
const { dongleId } = req . params ;
2022-03-24 16:05:05 -06:00
2022-03-23 17:55:01 -06:00
logger . info ( ` HTTP.UPLOAD_URL called for ${ dongleId } and file ${ path } : ${ JSON . stringify ( req . headers ) } ` ) ;
2021-05-21 16:17:11 -06:00
2022-03-24 16:05:05 -06:00
const { device } = req ;
2022-01-07 18:35:55 -07:00
if ( ! device ) {
logger . info ( ` HTTP.UPLOAD_URL device ${ dongleId } not found or not linked to an account / refusing uploads ` ) ;
2022-03-23 17:55:01 -06:00
return res . status ( 404 ) . send ( 'Not Found.' ) ;
2022-01-07 18:35:55 -07:00
}
2021-05-21 16:17:11 -06:00
2022-03-24 16:05:05 -06:00
const { authorization } = req . headers ;
if ( ! authorization ) {
logger . info ( ` HTTP.UPLOAD_URL JWT missing authorization, dongleId: ${ dongleId } ` ) ;
return res . status ( 401 ) . send ( 'Unauthorised.' ) ;
}
2022-01-08 17:57:15 -07:00
const decoded = device . public _key
2022-03-24 16:05:05 -06:00
? await validateJWT ( authorization , device . public _key )
2022-03-24 07:51:39 -06:00
. catch ( ( err ) => logger . error ( err ) )
2022-01-08 17:57:15 -07:00
: null ;
2021-05-21 16:17:11 -06:00
2022-03-23 17:55:01 -06:00
if ( ( ! decoded || decoded . identity !== dongleId ) ) {
2022-03-24 16:05:05 -06:00
logger . info ( ` HTTP.UPLOAD_URL JWT authorization failed, token: ${ authorization } device: ${ JSON . stringify ( device ) } , decoded: ${ JSON . stringify ( decoded ) } ` ) ;
return res . status ( 403 ) . send ( 'Forbidden.' ) ;
2022-01-07 18:35:55 -07:00
}
2022-03-24 07:51:39 -06:00
await deviceController
. updateLastPing ( dongleId )
. catch ( ( err ) => logger . error ( err ) ) ;
2022-01-07 18:35:55 -07:00
let responseUrl = null ;
const ts = Date . now ( ) ; // we use this to make sure old URLs cannot be reused (timeout after 60min)
2021-05-21 16:17:11 -06:00
2022-03-24 17:36:42 -06:00
const dongleIdHash = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( dongleId )
. digest ( 'hex' ) ;
2021-05-21 16:17:11 -06:00
2022-01-07 18:35:55 -07:00
// boot log upload
if ( path . indexOf ( 'boot/' ) === 0 || path . indexOf ( 'crash/' ) === 0 || path . indexOf ( 'bootlog.bz2' ) > 0 ) {
if ( path . indexOf ( 'bootlog.bz2' ) > 0 ) { // pre-op 0.8 way of uploading bootlogs
// file 2020-09-30--08-09-13--0/bootlog.bz2 to something like: boot/2021-05-11--03-03-38.bz2
path = ` boot/ ${ path . split ( '--' ) [ 0 ] } -- ${ path . split ( '--' ) [ 1 ] } .bz2 ` ;
2021-05-21 16:17:11 -06:00
}
2022-01-07 18:35:55 -07:00
const filename = path . replace ( '/' , '-' ) ;
2021-05-21 16:17:11 -06:00
2022-01-07 18:35:55 -07:00
// TODO, allow multiple types
const uploadType = path . indexOf ( 'boot/' ) === 0 ? 'boot' : 'crash' ;
2021-05-21 16:17:11 -06:00
2022-01-07 18:35:55 -07:00
// "boot-2021-04-12--01-45-30.bz" for example
const directory = ` ${ dongleId } / ${ dongleIdHash } / ${ uploadType } ` ;
2021-05-21 16:17:11 -06:00
2022-03-24 17:36:42 -06:00
const token = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( dongleId + filename + directory + ts )
. digest ( 'hex' ) ;
2021-05-21 16:17:11 -06:00
2022-02-28 22:04:36 -07:00
responseUrl = ` ${ process . env . BASE _UPLOAD _URL } ?file= ${ filename } &dir= ${ directory } &dongleId= ${ dongleId } &ts= ${ ts } &token= ${ token } ` ;
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.UPLOAD_URL matched ' ${ uploadType } ' file upload, constructed responseUrl: ${ responseUrl } ` ) ;
2022-01-08 13:43:57 -07:00
} else {
2022-01-07 18:35:55 -07:00
// "2021-04-12--01-44-25--0/qlog.bz2" for example
const subdirPosition = path . split ( '--' , 2 ) . join ( '--' ) . length ;
const filenamePosition = path . indexOf ( '/' ) ;
if ( subdirPosition > 0 && filenamePosition > subdirPosition ) {
const driveName = ` ${ path . split ( '--' ) [ 0 ] } -- ${ path . split ( '--' ) [ 1 ] } ` ;
2022-01-08 17:57:15 -07:00
const segment = parseInt ( path . split ( '--' ) [ 2 ] . substr ( 0 , path . split ( '--' ) [ 2 ] . indexOf ( '/' ) ) , 10 ) ;
2022-01-07 18:35:55 -07:00
let directory = ` ${ path . split ( '--' ) [ 0 ] } -- ${ path . split ( '--' ) [ 1 ] } / ${ segment } ` ;
const filename = path . split ( '/' ) [ 1 ] ;
let validRequest = false ;
if ( ( filename === 'fcamera.hevc' || filename === 'qcamera.ts' || filename === 'dcamera.hevc' || filename === 'rlog.bz2' || filename === 'qlog.bz2' || filename === 'ecamera.hevc' )
2022-01-08 17:57:15 -07:00
&& ( ! Number . isNaN ( segment ) || ( segment > 0 && segment < 1000 ) ) ) {
2022-01-07 18:35:55 -07:00
validRequest = true ;
}
if ( ! validRequest ) {
logger . error ( ` HTTP.UPLOAD_URL invalid filename ( ${ filename } ) or invalid segment ( ${ segment } ), responding with HTTP 400 ` ) ;
2022-03-23 17:55:01 -06:00
return res . status ( 400 ) . send ( 'Malformed Request.' ) ;
2022-01-07 18:35:55 -07:00
}
2022-03-24 17:36:42 -06:00
const driveIdentifierHash = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( driveName )
. digest ( 'hex' ) ;
2022-01-07 18:35:55 -07:00
directory = ` ${ dongleId } / ${ dongleIdHash } / ${ driveIdentifierHash } / ${ directory } ` ;
2021-05-21 16:17:11 -06:00
2022-03-24 17:36:42 -06:00
const token = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( dongleId + filename + directory + ts )
. digest ( 'hex' ) ;
2022-02-28 22:04:36 -07:00
responseUrl = ` ${ process . env . BASE _UPLOAD _URL } ?file= ${ filename } &dir= ${ directory } &dongleId= ${ dongleId } &ts= ${ ts } &token= ${ token } ` ;
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.UPLOAD_URL matched 'drive' file upload, constructed responseUrl: ${ responseUrl } ` ) ;
2021-05-21 16:17:11 -06:00
2022-03-22 09:14:08 -06:00
const drive = await deviceController . getDriveFromIdentifier ( dongleId , driveName )
. catch ( ( err ) => {
logger . warn ( 'drive failed to make' , err ) ;
} ) ;
2022-01-21 16:36:48 -07:00
2022-03-22 09:14:08 -06:00
logger . info ( 'drive value' , drive ) ;
logger . info ( 'drive name:' , driveName ) ;
2022-01-21 16:36:48 -07:00
2022-03-24 17:36:42 -06:00
if ( ! drive ) {
2022-03-22 09:14:08 -06:00
logger . info ( 'CREATING NEW DRIVE' ) ;
2022-01-07 18:35:55 -07:00
// create a new drive
const timeSplit = driveName . split ( '--' ) ;
const timeString = ` ${ timeSplit [ 0 ] } ${ timeSplit [ 1 ] . replace ( /-/g , ':' ) } ` ;
2021-05-29 17:33:02 -06:00
2022-01-21 16:36:48 -07:00
const driveResult = await deviceController . updateOrCreateDrive ( dongleId , driveName , {
2022-01-09 18:07:56 -07:00
max _segment : segment ,
duration : 0 ,
distance _meters : 0 ,
filesize : 0 ,
upload _complete : false ,
is _processed : false ,
drive _date : Date . parse ( timeString ) ,
created : Date . now ( ) ,
last _upload : Date . now ( ) ,
is _preserved : false ,
is _deleted : false ,
is _physically _removed : false ,
2022-03-22 09:14:08 -06:00
} ) ;
2022-01-09 18:07:56 -07:00
await deviceController . updateOrCreateDriveSegment ( dongleId , driveName , segment , {
duration : 0 ,
distance _meters : 0 ,
upload _complete : false ,
is _processed : false ,
is _stalled : false ,
created : Date . now ( ) ,
2022-03-22 09:14:08 -06:00
} ) ;
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.UPLOAD_URL created new drive # ${ JSON . stringify ( driveResult . lastID ) } ` ) ;
2022-01-08 13:43:57 -07:00
} else {
2022-03-22 09:14:08 -06:00
logger . info ( 'UPDATING DRIVE' ) ;
2022-01-09 18:07:56 -07:00
await deviceController . updateOrCreateDrive ( dongleId , driveName , {
max _segment : Math . max ( drive . max _segment , segment ) ,
upload _complete : false ,
is _processed : false ,
last _upload : Date . now ( ) ,
2022-03-22 09:14:08 -06:00
} ) ;
2022-03-21 04:54:50 -06:00
2022-03-22 09:14:08 -06:00
await deviceController . updateOrCreateDriveSegment ( dongleId , driveName , segment , {
duration : 0 ,
distance _meters : 0 ,
upload _complete : false ,
is _processed : false ,
is _stalled : false ,
created : Date . now ( ) ,
} ) ;
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.UPLOAD_URL updated existing drive: ${ JSON . stringify ( drive ) } ` ) ;
}
2021-05-21 16:17:11 -06:00
}
2022-01-07 18:35:55 -07:00
}
2021-12-30 18:22:34 -07:00
2022-03-24 17:36:42 -06:00
if ( ! responseUrl ) {
2022-01-07 18:35:55 -07:00
logger . error ( 'HTTP.UPLOAD_URL unable to match request, responding with HTTP 400' ) ;
2022-01-08 17:57:15 -07:00
return res . status ( 400 ) . send ( 'Malformed Request.' ) ;
2022-01-07 18:35:55 -07:00
}
2022-03-24 17:36:15 -06:00
return res
. status ( 200 )
. append ( 'Content-Type' , 'application/octet-stream' )
. json ( { url : responseUrl , headers : { 'Content-Type' : 'application/octet-stream' } } ) ;
2022-01-07 18:35:55 -07:00
}
2021-12-30 18:22:34 -07:00
// DRIVE & BOOT/CRASH LOG FILE UPLOAD URL REQUEST
2022-03-24 17:36:15 -06:00
router . get ( '/v1.3/:dongleId/upload_url' , getDevice , upload ) ;
router . get ( '/v1.4/:dongleId/upload_url' , getDevice , upload ) ;
2021-05-21 16:17:11 -06:00
// DEVICE REGISTRATION OR RE-ACTIVATION
2022-01-07 18:35:55 -07:00
router . post ( '/v2/pilotauth/' , bodyParser . urlencoded ( { extended : true } ) , async ( req , res ) => {
2022-03-23 17:55:01 -06:00
/* eslint-disable no-unused-vars */
2022-03-02 19:37:57 -07:00
const {
2022-03-23 17:55:01 -06:00
imei : imei1 ,
imei2 ,
2022-03-02 19:37:57 -07:00
serial ,
public _key : publicKey ,
2022-03-22 09:14:08 -06:00
register _token : registerToken ,
2022-03-02 19:37:57 -07:00
} = req . query ;
2022-03-23 17:55:01 -06:00
/* eslint-enable no-unused-vars */
2022-01-08 17:57:15 -07:00
if (
2022-03-24 16:05:05 -06:00
! serial || serial . length < 5
|| ! publicKey || publicKey . length < 5
|| ! registerToken || registerToken . length < 5
2022-01-08 17:57:15 -07:00
) {
2022-01-07 18:35:55 -07:00
logger . error ( ` HTTP.V2.PILOTAUTH a required parameter is missing or empty ${ JSON . stringify ( req . query ) } ` ) ;
2022-01-08 17:57:15 -07:00
return res . status ( 400 ) . send ( 'Malformed Request.' ) ;
2022-01-07 18:35:55 -07:00
}
2021-05-21 16:17:11 -06:00
2022-03-22 09:14:08 -06:00
const decoded = await validateJWT ( registerToken , publicKey ) ;
2022-01-08 16:07:09 -07:00
if ( ! decoded || ! decoded . register ) {
2022-01-07 18:35:55 -07:00
logger . error ( ` HTTP.V2.PILOTAUTH JWT token is invalid ( ${ JSON . stringify ( decoded ) } ) ` ) ;
2022-03-24 16:05:05 -06:00
return res . status ( 401 ) . send ( 'Unauthorised.' ) ;
2022-01-07 18:35:55 -07:00
}
2021-05-21 16:17:11 -06:00
2022-03-22 09:14:08 -06:00
const device = await deviceController . getDeviceFromSerial ( serial ) ;
2022-03-24 16:05:05 -06:00
if ( ! device ) {
2022-01-07 18:35:55 -07:00
logger . info ( ` HTTP.V2.PILOTAUTH REGISTERING NEW DEVICE ( ${ imei1 } , ${ serial } ) ` ) ;
2022-01-08 17:57:15 -07:00
// TODO: rewrite without while (true) loop
// eslint-disable-next-line no-constant-condition
2022-01-07 18:35:55 -07:00
while ( true ) {
2022-03-24 17:36:42 -06:00
const dongleId = crypto
. randomBytes ( 4 )
. toString ( 'hex' ) ;
2022-03-22 09:14:08 -06:00
const isDongleIdTaken = await deviceController . getDeviceFromDongleId ( dongleId ) ;
2022-03-23 17:55:01 -06:00
if ( isDongleIdTaken ) {
continue ;
}
2022-01-09 18:07:56 -07:00
2022-03-23 17:55:01 -06:00
const newDevice = await deviceController . createDongle ( dongleId , 0 , imei1 , serial , publicKey ) ;
2022-01-07 18:35:55 -07:00
2022-03-23 17:55:01 -06:00
logger . info ( 'HTTP.V2.PILOTAUTH REGISTERED NEW DEVICE:' , { newDevice , registerToken } ) ;
return res . status ( 201 ) . json ( {
dongle _id : newDevice . dongle _id ,
access _token : 'DEPRECATED-BUT-REQUIRED-FOR-07' ,
} ) ;
2021-05-21 16:17:11 -06:00
}
2022-01-08 17:57:15 -07:00
}
2021-05-21 16:17:11 -06:00
2022-03-22 09:14:08 -06:00
await deviceController . updateDevice ( device . dongle _id , {
last _ping : Date . now ( ) ,
public _key : publicKey ,
} ) ;
2021-05-21 16:17:11 -06:00
2022-01-08 17:57:15 -07:00
logger . info ( ` HTTP.V2.PILOTAUTH REACTIVATING KNOWN DEVICE ( ${ imei1 } , ${ serial } ) with dongle_id ${ device . dongle _id } ` ) ;
2022-03-22 09:14:08 -06:00
return res . status ( 200 ) . json ( {
dongle _id : device . dongle _id ,
access _token : 'DEPRECATED-BUT-REQUIRED-FOR-07' ,
} ) ;
2022-01-07 18:35:55 -07:00
} ) ;
2021-05-21 16:17:11 -06:00
2022-01-07 18:35:55 -07:00
// RETRIEVES DATASET FOR OUR MODIFIED CABANA - THIS RESPONSE IS USED TO FAKE A DEMO ROUTE
router . get ( '/useradmin/cabana_drive/:extendedRouteIdentifier' , runAsyncWrapper ( async ( req , res ) => {
2022-03-23 17:55:01 -06:00
const { extendedRouteIdentifier } = req . params ;
const [ dongleId , dongleIdHashReq , driveIdentifier , driveIdentifierHashReq ] = extendedRouteIdentifier . split ( '|' ) ;
2022-01-07 18:35:55 -07:00
2022-03-24 17:34:13 -06:00
const drive = await deviceController . getDriveFromIdentifier ( dongleId , driveIdentifier ) ;
2022-01-07 18:35:55 -07:00
if ( ! drive ) {
2022-03-23 17:55:01 -06:00
return res . status ( 404 ) . json ( { status : 'drive not found' } ) ;
2022-01-07 18:35:55 -07:00
}
2021-05-21 16:17:11 -06:00
2022-03-24 17:36:42 -06:00
const dongleIdHash = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( drive . dongle _id )
. digest ( 'hex' ) ;
const driveIdentifierHash = crypto
. createHmac ( 'sha256' , process . env . APP _SALT )
. update ( drive . identifier )
. digest ( 'hex' ) ;
2022-03-24 16:05:05 -06:00
const driveUrl = ` ${ process . env . BASE _DRIVE _DOWNLOAD _URL + dongleId } / ${ dongleIdHash } / ${ driveIdentifierHash } / ${ driveIdentifier } ` ;
2021-05-21 16:17:11 -06:00
2022-01-08 16:07:09 -07:00
if ( dongleIdHash !== dongleIdHashReq || driveIdentifierHash !== driveIdentifierHashReq ) {
2022-03-23 17:55:01 -06:00
return res . status ( 400 ) . json ( { status : 'hashes not matching' } ) ;
2022-01-07 18:35:55 -07:00
}
if ( ! drive . is _processed ) {
2022-03-23 17:55:01 -06:00
return res . status ( 202 ) . json ( { status : 'drive is not processed yet' } ) ;
2022-01-07 18:35:55 -07:00
}
2022-01-08 15:00:08 -07:00
const logUrls = [ ] ;
for ( let i = 0 ; i <= drive . max _segment ; i ++ ) {
2022-01-07 18:35:55 -07:00
logUrls . push ( ` ${ driveUrl } / ${ i } /rlog.bz2 ` ) ;
}
2022-01-08 17:57:15 -07:00
return res . status ( 200 ) . json ( {
2022-01-07 18:35:55 -07:00
logUrls ,
driveUrl ,
2022-03-24 16:05:05 -06:00
name : ` ${ dongleId } | ${ driveIdentifier } ` ,
driveIdentifier ,
dongleId ,
2022-01-07 18:35:55 -07:00
} ) ;
} ) ) ;
2021-05-21 16:17:11 -06:00
2022-01-12 08:02:30 -07:00
export default router ;