diff --git a/camera-server/src/config/config.ts b/camera-server/src/config/config.ts index 5f75e8d4070905afcef4a9345844729d0aab2254..84ecd9df53d3936dfb407df5272e6964a17a79e9 100644 --- a/camera-server/src/config/config.ts +++ b/camera-server/src/config/config.ts @@ -4,6 +4,11 @@ import * as path from 'path'; interface Config { port: number; cameraSlots: number; +} + +// Required to access config with config[key] +// But only an object of type Config should be returned +interface IndexableConfig extends Config { // Change type once non-number values are added [key: string]: number; } @@ -26,7 +31,7 @@ if (configPath) { console.log('Got not CONFIG_PATH environment variable'); } -const config: Config = { +const indexableConfig: IndexableConfig = { port: 5000, cameraSlots: 4 }; @@ -43,11 +48,11 @@ if (fileContent) { console.log(`Reading config at ${path.resolve(configPath!)}`); // Overwrite default values with values of read config Object.keys(readConfig).forEach((key) => { - if (config.hasOwnProperty(key)) { - const expectedType = typeof config[key]; + if (indexableConfig.hasOwnProperty(key)) { + const expectedType = typeof indexableConfig[key]; const readType = typeof readConfig[key]; if (expectedType === readType) { - config[key] = readConfig[key]; + indexableConfig[key] = readConfig[key]; } else { console.log( `Error: Read config propety '${key}' is of type ${readType}, but type ${expectedType} was expected` @@ -66,6 +71,8 @@ if (fileContent) { } } +const config = indexableConfig as Config; + console.log('Using config:', config); export { config }; diff --git a/camera-server/src/io-interface/handlers/input-handlers.ts b/camera-server/src/io-interface/handlers/input-handlers.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a2a097e508549d038b488af3382e2a46134d6f1 --- /dev/null +++ b/camera-server/src/io-interface/handlers/input-handlers.ts @@ -0,0 +1,142 @@ +import { socketIO } from '../../socket-io/socket-io'; +import { cameraSlotState } from '../../state/camera-slot-state'; +import { emitRemoveFeed } from '../../socket-io/handlers/common-handlers'; + +const visibilityCommands = ['hide', 'show']; +const geometryCommands = [ + 'set_geometry_relative_to_window', + 'set_geometry_relative_to_canvas' +]; +const internalCommands = ['activate_slot', 'deactivate_slot', 'refresh_token']; + +const handleInternalCommand = ( + command: string, + slot: number, + params: string[] +) => { + const currentCameraState = cameraSlotState[slot]; + switch (command) { + case 'activate_slot': + if (currentCameraState.slotActive) { + console.log('Error: Tried to activate active slot ' + slot); + return; + } + if (params.length === 0) { + console.log( + 'Error while activating slot ' + + slot + + ' - Got no token parameter' + ); + return; + } + currentCameraState.token = params[0]; + currentCameraState.slotActive = true; + break; + case 'deactivate_slot': + if (!currentCameraState.slotActive) { + console.log('Error: Tried to deactivate inactive slot ' + slot); + return; + } + console.log('Deactivating slot ' + slot); + emitRemoveFeed(slot); + + currentCameraState.slotActive = false; + currentCameraState.token = null; + currentCameraState.feedActive = false; + currentCameraState.feedId = null; + currentCameraState.senderSocketId = null; + break; + case 'refresh_token': + if (!currentCameraState.slotActive) { + console.log( + 'Error: Tried to refresh token for inactive slot ' + slot + ); + return; + } + if (params.length === 0) { + console.log( + 'Error while refreshing token for slot ' + + slot + + ' - Got no token parameter' + ); + console.log('Keeping old token'); + return; + } + console.log('Refreshing token for slot ' + slot); + currentCameraState.token = params[0]; + break; + default: + console.log( + 'Error: handleInternalCommand got unknown command ' + command + ); + break; + } +}; + +export const handleCommand = (line: string) => { + let emitCommand = false; + + console.log('Got command from stdin:', line); + const params = line.split(' '); + + const command = params.shift(); + if (command == null) { + console.log('Error: Got malformed line with no command'); + return; + } + + const slotStr = params.shift(); + if (slotStr == null) { + console.log('Error: Got no slot to apply the command on'); + return; + } + + const slot = parseInt(slotStr); + if (isNaN(slot)) { + console.log( + 'Error: Could not parse slot ' + slotStr + ' to an integer' + ); + return; + } + if (slot < 0 || slot > cameraSlotState.length - 1) { + console.log( + `Error: Got invalid slot number ${slot}. There are ${cameraSlotState.length} camera slots.` + ); + return; + } + + console.log('command:', command); + console.log('slot:', slot); + console.log('params:', params); + + const currentCameraState = cameraSlotState[slot]; + + if (visibilityCommands.includes(command)) { + currentCameraState.visibility = { + command, + params + }; + emitCommand = true; + } else if (geometryCommands.includes(command)) { + currentCameraState.geometry = { + command, + params + }; + emitCommand = true; + } else if (internalCommands.includes(command)) { + handleInternalCommand(command, slot, params); + } else { + console.log('Command "' + command + '" is not a valid command'); + return; + } + + console.log('new cameraState:', currentCameraState); + + if (currentCameraState.feedActive && emitCommand) { + socketIO.emit('command', { + slot, + command, + params + }); + } +}; diff --git a/camera-server/src/io-interface/readline-interface.ts b/camera-server/src/io-interface/readline-interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6c6a03cf00b0f7e8d3771c9fe04ab8bf435adee --- /dev/null +++ b/camera-server/src/io-interface/readline-interface.ts @@ -0,0 +1,7 @@ +import * as readline from 'readline'; + +export const readlineInterface = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); diff --git a/camera-server/src/models/command-descriptor.ts b/camera-server/src/models/command-descriptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a65bf10defc6ff5e8ecad0aa70615e39b718a82 --- /dev/null +++ b/camera-server/src/models/command-descriptor.ts @@ -0,0 +1,4 @@ +export interface CommandDescriptor { + command: string; + params: string[]; +} diff --git a/camera-server/src/server.ts b/camera-server/src/server.ts index 16d5d1f151668ceceb73cedcb0d017a02a4dbd57..e2ec3a48bbba527993f5537d64d4bb63a107aa2c 100644 --- a/camera-server/src/server.ts +++ b/camera-server/src/server.ts @@ -1,387 +1,18 @@ -import { Server as SocketIOServer, Socket } from 'socket.io'; +import { Socket } from 'socket.io'; -import { mountCleanupLogic } from './util/cleanup'; -import { ValidationError } from './models/validation-error'; -import { CameraSlotState, CommandDescriptor } from './models/camera-slot-state'; -import { SenderSocket } from './models/sender-socket'; -import { config } from './config/config'; +import { socketIO } from './socket-io/socket-io'; +import { handleSenderInit } from './socket-io/handlers/sender-handlers'; +import { handleQueryState } from './socket-io/handlers/common-handlers'; +import { readlineInterface } from './io-interface/readline-interface'; +import { handleCommand } from './io-interface/handlers/input-handlers'; +import { registerCleanupLogic } from './util/cleanup'; -const readline = require('readline'); -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: false -}); - -const io = new SocketIOServer(config.port); - -const visibilityCommands = ['hide', 'show']; -const geometryCommands = [ - 'set_geometry_relative_to_window', - 'set_geometry_relative_to_canvas' -]; -const internalCommands = ['activate_slot', 'deactivate_slot', 'refresh_token']; - -let cameraSlotState: CameraSlotState[] = []; -for (let i = 0; i < config.cameraSlots; i++) { - cameraSlotState.push(new CameraSlotState()); -} - -const emitNewFeed = (slot: number) => { - const cameraState = cameraSlotState[slot]; - io.emit('new_feed', { - slot, - feedId: cameraState.feedId, - visibility: cameraState.visibility, - geometry: cameraState.geometry - }); -}; - -const emitRemoveFeed = (slot: number) => { - io.emit('remove_feed', { slot }); -}; - -const handleSetFeedId = ( - socket: SenderSocket, - data: null | { feedId?: string }, - fn: Function -) => { - let success = true; - let message = ''; - - try { - const slot = socket.cameraSlot; - const currentCameraState = cameraSlotState[slot]; - - if (currentCameraState.token !== socket.cameraSlotToken) { - console.log( - 'Error: Got set_feed_id event for slot ' + - slot + - ' with an old token' - ); - throw new ValidationError( - 'The provided token is not valid anymore - the feed is not transmitted' - ); - } - - if (currentCameraState.feedActive) { - console.log( - 'Error: Got set_feed_id event for slot ' + - slot + - ' which already has an active feed' - ); - throw new ValidationError( - 'There is already somebody using this slot' - ); - } - - if (data == null) { - console.log( - 'Error: Got set_feed_id event for slot ' + - slot + - ' without data' - ); - throw new ValidationError( - 'Could not get feed id because no data was provided' - ); - } - - const feedId = data.feedId; - if (feedId == null) { - console.log( - 'Error: Got set_feed_id event without a feed id on slot ' + slot - ); - throw new ValidationError('No feed id was provided'); - } - - console.log('Setting feed id of slot ' + slot + ' to ' + feedId); - message = 'Successfully set feed id - you are now using this slot'; - - currentCameraState.feedActive = true; - currentCameraState.feedId = feedId; - currentCameraState.senderSocketId = socket.id; - - emitNewFeed(slot); - } catch (e) { - if (e instanceof ValidationError) { - success = false; - message = e.message; - } else { - throw e; - } - } - - fn({ success, message }); -}; - -const handleSenderDisconnect = (socket: SenderSocket, _: string) => { - const slot = socket.cameraSlot; - if (slot != null) { - const currentCameraState = cameraSlotState[slot]; - if ( - currentCameraState.feedActive && - socket.id === currentCameraState.senderSocketId - ) { - console.log( - 'Sender on slot ' + slot + ' disconnected - Clearing slot' - ); - currentCameraState.feedActive = false; - currentCameraState.feedId = null; - currentCameraState.senderSocketId = null; - - emitRemoveFeed(slot); - } - } -}; - -const registerSenderHandlers = (socket: SenderSocket) => { - socket.on('set_feed_id', handleSetFeedId.bind(null, socket)); - socket.on('disconnect', handleSenderDisconnect.bind(null, socket)); -}; - -const handleSenderInit = ( - socket: SenderSocket, - data: null | { slot?: string; token?: string }, - fn: Function -) => { - let success = true; - let message = ''; - try { - if (data == null) { - console.log('Error: Got socket connection without data'); - throw new ValidationError('No data provided'); - } - - const slotStr = data.slot; - if (slotStr == null) { - console.log('Error: Got socket connection without a slot'); - throw new ValidationError('No slot provided'); - } - - const slot = parseInt(slotStr); - if (isNaN(slot)) { - console.log( - 'Error: Got socket connection with slot ' + - slotStr + - ' that cannot be parsed to a number' - ); - throw new ValidationError( - 'Slot ' + slotStr + ' cannot be parsed to number' - ); - } - if (slot < 0 || slot > cameraSlotState.length - 1) { - console.log( - 'Error: Got socket connection with slot ' + - slot + - ' which is not in the list of slots' - ); - throw new ValidationError( - 'Slot ' + slot + ' is not in the list of slots' - ); - } - - const slotState = cameraSlotState[slot]; - if (!slotState.slotActive) { - console.log( - 'Error: Got socket connection for inactive slot ' + slot - ); - throw new ValidationError('Slot ' + slot + ' is not active'); - } - - const token = data.token; - if (token == null) { - console.log('Error: Got socket connection without token'); - throw new ValidationError('No token provided'); - } - if (slotState.token !== token) { - console.log( - 'Error: Got socket connecion with wrong token ' + - token + - ' for slot ' + - slot - ); - throw new ValidationError('Invalid token'); - } - - console.log('Got sender socket connection on slot ' + slot); - - message = 'Socket authenticated'; - socket.cameraSlot = slot; - socket.cameraSlotToken = token; - - registerSenderHandlers(socket); - } catch (e) { - if (e instanceof ValidationError) { - success = false; - message = e.message; - } else { - throw e; - } - } - - fn({ success, message }); -}; - -const handleQueryState = (fn: Function) => { - console.log('Got state query from socket'); - let response: { - [index: number]: { - feedId: string | null; - visibility: CommandDescriptor; - geometry: CommandDescriptor; - }; - } = {}; - for (let i = 0; i < cameraSlotState.length; i++) { - const cameraState = cameraSlotState[i]; - if (cameraState.feedActive) { - response[i] = { - feedId: cameraState.feedId, - visibility: cameraState.visibility, - geometry: cameraState.geometry - }; - } - } - fn(response); -}; - -io.on('connection', (socket: Socket) => { +socketIO.on('connection', (socket: Socket) => { socket.on('query_state', handleQueryState); socket.on('sender_init', handleSenderInit.bind(null, socket)); }); -const handleInternalCommand = ( - command: string, - slot: number, - params: string[] -) => { - const currentCameraState = cameraSlotState[slot]; - switch (command) { - case 'activate_slot': - if (currentCameraState.slotActive) { - console.log('Error: Tried to activate active slot ' + slot); - return; - } - if (params.length === 0) { - console.log( - 'Error while activating slot ' + - slot + - ' - Got no token parameter' - ); - return; - } - currentCameraState.token = params[0]; - currentCameraState.slotActive = true; - break; - case 'deactivate_slot': - if (!currentCameraState.slotActive) { - console.log('Error: Tried to deactivate inactive slot ' + slot); - return; - } - console.log('Deactivating slot ' + slot); - emitRemoveFeed(slot); - - currentCameraState.slotActive = false; - currentCameraState.token = null; - currentCameraState.feedActive = false; - currentCameraState.feedId = null; - currentCameraState.senderSocketId = null; - break; - case 'refresh_token': - if (!currentCameraState.slotActive) { - console.log( - 'Error: Tried to refresh token for inactive slot ' + slot - ); - return; - } - if (params.length === 0) { - console.log( - 'Error while refreshing token for slot ' + - slot + - ' - Got no token parameter' - ); - console.log('Keeping old token'); - return; - } - console.log('Refreshing token for slot ' + slot); - currentCameraState.token = params[0]; - break; - default: - console.log( - 'Error: handleInternalCommand got unknown command ' + command - ); - break; - } -}; - -const handleCommand = (line: string) => { - let emitCommand = false; - - console.log('Got command from stdin:', line); - const params = line.split(' '); - - const command = params.shift(); - if (command == null) { - console.log('Error: Got malformed line with no command'); - return; - } - - const slotStr = params.shift(); - if (slotStr == null) { - console.log('Error: Got no slot to apply the command on'); - return; - } - - const slot = parseInt(slotStr); - if (isNaN(slot)) { - console.log( - 'Error: Could not parse slot ' + slotStr + ' to an integer' - ); - return; - } - if (slot < 0 || slot > cameraSlotState.length - 1) { - console.log( - `Error: Got invalid slot number ${slot}. There are ${cameraSlotState.length} camera slots.` - ); - return; - } - - console.log('command:', command); - console.log('slot:', slot); - console.log('params:', params); - - const currentCameraState = cameraSlotState[slot]; - - if (visibilityCommands.includes(command)) { - currentCameraState.visibility = { - command, - params - }; - emitCommand = true; - } else if (geometryCommands.includes(command)) { - currentCameraState.geometry = { - command, - params - }; - emitCommand = true; - } else if (internalCommands.includes(command)) { - handleInternalCommand(command, slot, params); - } else { - console.log('Command "' + command + '" is not a valid command'); - return; - } - - console.log('new cameraState:', currentCameraState); - - if (currentCameraState.feedActive && emitCommand) { - io.emit('command', { - slot, - command, - params - }); - } -}; - -rl.on('line', handleCommand); +readlineInterface.on('line', handleCommand); -mountCleanupLogic(io); +registerCleanupLogic(); diff --git a/camera-server/src/socket-io/handlers/common-handlers.ts b/camera-server/src/socket-io/handlers/common-handlers.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3b56691f50c5f2502530aaa2afca3a841b9470b --- /dev/null +++ b/camera-server/src/socket-io/handlers/common-handlers.ts @@ -0,0 +1,39 @@ +import { socketIO } from '../socket-io'; +import { cameraSlotState } from '../../state/camera-slot-state'; +import { CommandDescriptor } from '../../models/command-descriptor'; + +export const emitNewFeed = (slot: number) => { + const cameraState = cameraSlotState[slot]; + socketIO.emit('new_feed', { + slot, + feedId: cameraState.feedId, + visibility: cameraState.visibility, + geometry: cameraState.geometry + }); +}; + +export const emitRemoveFeed = (slot: number) => { + socketIO.emit('remove_feed', { slot }); +}; + +export const handleQueryState = (fn: Function) => { + console.log('Got state query from socket'); + let response: { + [index: number]: { + feedId: string | null; + visibility: CommandDescriptor; + geometry: CommandDescriptor; + }; + } = {}; + for (let i = 0; i < cameraSlotState.length; i++) { + const cameraState = cameraSlotState[i]; + if (cameraState.feedActive) { + response[i] = { + feedId: cameraState.feedId, + visibility: cameraState.visibility, + geometry: cameraState.geometry + }; + } + } + fn(response); +}; diff --git a/camera-server/src/socket-io/handlers/sender-handlers.ts b/camera-server/src/socket-io/handlers/sender-handlers.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae4b7fb0afdc4fe40ab6b2b0002087d539d82332 --- /dev/null +++ b/camera-server/src/socket-io/handlers/sender-handlers.ts @@ -0,0 +1,185 @@ +import { cameraSlotState } from '../../state/camera-slot-state'; +import { emitNewFeed, emitRemoveFeed } from './common-handlers'; +import { SenderSocket } from '../../models/sender-socket'; +import { ValidationError } from '../../models/validation-error'; + +const handleSetFeedId = ( + socket: SenderSocket, + data: null | { feedId?: string }, + fn: Function +) => { + let success = true; + let message = ''; + + try { + const slot = socket.cameraSlot; + const currentSlotState = cameraSlotState[slot]; + + if (currentSlotState.token !== socket.cameraSlotToken) { + console.log( + 'Error: Got set_feed_id event for slot ' + + slot + + ' with an old token' + ); + throw new ValidationError( + 'The provided token is not valid anymore - the feed is not transmitted' + ); + } + + if (currentSlotState.feedActive) { + console.log( + 'Error: Got set_feed_id event for slot ' + + slot + + ' which already has an active feed' + ); + throw new ValidationError( + 'There is already somebody using this slot' + ); + } + + if (data == null) { + console.log( + 'Error: Got set_feed_id event for slot ' + + slot + + ' without data' + ); + throw new ValidationError( + 'Could not get feed id because no data was provided' + ); + } + + const feedId = data.feedId; + if (feedId == null) { + console.log( + 'Error: Got set_feed_id event without a feed id on slot ' + slot + ); + throw new ValidationError('No feed id was provided'); + } + + console.log('Setting feed id of slot ' + slot + ' to ' + feedId); + message = 'Successfully set feed id - you are now using this slot'; + + currentSlotState.feedActive = true; + currentSlotState.feedId = feedId; + currentSlotState.senderSocketId = socket.id; + + emitNewFeed(slot); + } catch (e) { + if (e instanceof ValidationError) { + success = false; + message = e.message; + } else { + throw e; + } + } + + fn({ success, message }); +}; + +const handleSenderDisconnect = (socket: SenderSocket, _: string) => { + const slot = socket.cameraSlot; + if (slot != null) { + const currentSlotState = cameraSlotState[slot]; + if ( + currentSlotState.feedActive && + socket.id === currentSlotState.senderSocketId + ) { + console.log( + 'Sender on slot ' + slot + ' disconnected - Clearing slot' + ); + currentSlotState.feedActive = false; + currentSlotState.feedId = null; + currentSlotState.senderSocketId = null; + + emitRemoveFeed(slot); + } + } +}; + +const registerSenderHandlers = (socket: SenderSocket) => { + socket.on('set_feed_id', handleSetFeedId.bind(null, socket)); + socket.on('disconnect', handleSenderDisconnect.bind(null, socket)); +}; + +export const handleSenderInit = ( + socket: SenderSocket, + data: null | { slot?: string; token?: string }, + fn: Function +) => { + let success = true; + let message = ''; + try { + if (data == null) { + console.log('Error: Got socket connection without data'); + throw new ValidationError('No data provided'); + } + + const slotStr = data.slot; + if (slotStr == null) { + console.log('Error: Got socket connection without a slot'); + throw new ValidationError('No slot provided'); + } + + const slot = parseInt(slotStr); + if (isNaN(slot)) { + console.log( + 'Error: Got socket connection with slot ' + + slotStr + + ' that cannot be parsed to a number' + ); + throw new ValidationError( + 'Slot ' + slotStr + ' cannot be parsed to number' + ); + } + if (slot < 0 || slot > cameraSlotState.length - 1) { + console.log( + 'Error: Got socket connection with slot ' + + slot + + ' which is not in the list of slots' + ); + throw new ValidationError( + 'Slot ' + slot + ' is not in the list of slots' + ); + } + + const slotState = cameraSlotState[slot]; + if (!slotState.slotActive) { + console.log( + 'Error: Got socket connection for inactive slot ' + slot + ); + throw new ValidationError('Slot ' + slot + ' is not active'); + } + + const token = data.token; + if (token == null) { + console.log('Error: Got socket connection without token'); + throw new ValidationError('No token provided'); + } + if (slotState.token !== token) { + console.log( + 'Error: Got socket connecion with wrong token ' + + token + + ' for slot ' + + slot + ); + throw new ValidationError('Invalid token'); + } + + console.log('Got sender socket connection on slot ' + slot); + + message = 'Socket authenticated'; + socket.cameraSlot = slot; + socket.cameraSlotToken = token; + + registerSenderHandlers(socket); + } catch (e) { + if (e instanceof ValidationError) { + success = false; + message = e.message; + } else { + throw e; + } + } + + fn({ success, message }); +}; diff --git a/camera-server/src/socket-io/socket-io.ts b/camera-server/src/socket-io/socket-io.ts new file mode 100644 index 0000000000000000000000000000000000000000..7de123197b9efd6e62b246bb15035d7de0dfe47d --- /dev/null +++ b/camera-server/src/socket-io/socket-io.ts @@ -0,0 +1,5 @@ +import { Server as SocketIOServer } from 'socket.io'; + +import { config } from '../config/config'; + +export const socketIO = new SocketIOServer(config.port); diff --git a/camera-server/src/models/camera-slot-state.ts b/camera-server/src/state/camera-slot-state.ts similarity index 56% rename from camera-server/src/models/camera-slot-state.ts rename to camera-server/src/state/camera-slot-state.ts index 9795178209fa54457e1a91e81e2eb35b41b6628e..d4501752be17ea1dd36d5c8287a885903ebb4d5b 100644 --- a/camera-server/src/models/camera-slot-state.ts +++ b/camera-server/src/state/camera-slot-state.ts @@ -1,11 +1,9 @@ -export interface CommandDescriptor { - command: string; - params: string[]; -} +import { config } from '../config/config'; +import { CommandDescriptor } from '../models/command-descriptor'; type NullableString = string | null; -export class CameraSlotState { +class SingleCameraSlotState { slotActive = false; token: NullableString = null; feedActive = false; @@ -19,4 +17,12 @@ export class CameraSlotState { command: 'set_geometry_relative_to_canvas', params: ['rb', '0', '0', '200', '200'] }; -} \ No newline at end of file +} + +const cameraSlotState: SingleCameraSlotState[] = []; + +for (let i = 0; i < config.cameraSlots; i++) { + cameraSlotState.push(new SingleCameraSlotState()); +} + +export { cameraSlotState }; diff --git a/camera-server/src/util/cleanup.ts b/camera-server/src/util/cleanup.ts index 7154e5caced184f47cd6a775a69bd85093b6cc26..5cc867a4fff2a245f63c13f0d2357f968917c951 100644 --- a/camera-server/src/util/cleanup.ts +++ b/camera-server/src/util/cleanup.ts @@ -1,35 +1,35 @@ -import { Server as SocketIOServer } from 'socket.io'; +import { socketIO } from '../socket-io/socket-io'; interface ExitHandlerOptions { cleanup?: boolean; exit?: boolean; } -export const mountCleanupLogic = (io: SocketIOServer) => { - const cleanup = () => { - console.log('cleanup'); - io.emit('remove_all_feeds'); - }; +const cleanup = () => { + console.log('cleanup'); + socketIO.emit('remove_all_feeds'); +}; - const exitHandler = (options: ExitHandlerOptions, exitCode: number) => { - if (options.cleanup) cleanup(); - if (exitCode || exitCode === 0) console.log(exitCode); - if (options.exit) process.exit(); - } +const exitHandler = (options: ExitHandlerOptions, exitCode: number) => { + if (options.cleanup) cleanup(); + if (exitCode || exitCode === 0) console.log(exitCode); + if (options.exit) process.exit(); +}; +export const registerCleanupLogic = () => { // do something when app is closing - process.on('exit', exitHandler.bind(null, { cleanup:true })); + process.on('exit', exitHandler.bind(null, { cleanup: true })); // catches ctrl+c event - process.on('SIGINT', exitHandler.bind(null, { exit:true })); + process.on('SIGINT', exitHandler.bind(null, { exit: true })); // catches "kill pid" (for example: nodemon restart) - process.on('SIGUSR1', exitHandler.bind(null, { exit:true })); - process.on('SIGUSR2', exitHandler.bind(null, { exit:true })); + process.on('SIGUSR1', exitHandler.bind(null, { exit: true })); + process.on('SIGUSR2', exitHandler.bind(null, { exit: true })); // catches uncaught exceptions - process.on('uncaughtException', exitHandler.bind(null, { exit:true })); + process.on('uncaughtException', exitHandler.bind(null, { exit: true })); // catches termination - process.on('SIGTERM', exitHandler.bind(null, { exit:true })); -} \ No newline at end of file + process.on('SIGTERM', exitHandler.bind(null, { exit: true })); +};