diff --git a/frontend/src/app.js b/frontend/src/app.js index 3f6bad9..97a5af9 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -115,7 +115,7 @@ app.get( '/clientDisplayNotifier', ( req, res ) => { app.get( '/mainNotifier', ( req, res ) => { const ipRetrieved = req.headers[ 'x-forwarded-for' ]; const ip = ipRetrieved ? ipRetrieved.split( /, / )[ 0 ] : req.connection.remoteAddress; - if ( ip === '::ffff:127.0.0.1' ) { + if ( ip === '::ffff:127.0.0.1' || ip === '::1' ) { res.writeHead( 200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', @@ -123,7 +123,7 @@ app.get( '/mainNotifier', ( req, res ) => { } ); res.status( 200 ); res.flushHeaders(); - let det = { 'type': 'basics', 'data': connectedClients }; + let det = { 'type': 'basics' }; res.write( `data: ${ JSON.stringify( det ) }\n\n` ); connectedMain = res; } else { @@ -199,22 +199,23 @@ app.post( '/statusUpdate', ( req, res ) => { } ); // STATUS UPDATE from the client display to send to main ui -const allowedMainUpdates = [ 'disconnect', 'fullScreenStatus', 'visibility' ]; -app.get( '/clientStatusUpdate/:status', ( req, res ) => { - if ( allowedTypes.includes( req.body.type ) ) { +// Send update if page is closed +const allowedMainUpdates = [ 'blur', 'visibility' ]; +app.post( '/clientStatusUpdate', ( req, res ) => { + if ( allowedMainUpdates.includes( req.body.type ) ) { const ipRetrieved = req.headers[ 'x-forwarded-for' ]; const ip = ipRetrieved ? ipRetrieved.split( /, / )[ 0 ] : req.connection.remoteAddress; - sendClientUpdate( req.body.type, req.body.data, ip ); + sendClientUpdate( req.body.type, ip ); res.send( 'ok' ); } else { res.status( 400 ).send( 'ERR_UNKNOWN_TYPE' ); } } ); -const sendClientUpdate = ( update, data, ip ) => { - for ( let main in connectedMain ) { - connectedMain[ main ].write( 'data: ' + JSON.stringify( { 'type': update, 'ip': ip, 'data': data } ) + '\n\n' ); - } +const sendClientUpdate = ( update, ip ) => { + try { + connectedMain.write( 'data: ' + JSON.stringify( { 'type': update, 'ip': ip } ) + '\n\n' ); + } catch ( err ) {} } app.get( '/indexDirs', ( req, res ) => { diff --git a/frontend/src/client/appleMusic/index.html b/frontend/src/client/appleMusic/index.html index 6f35934..32f69cb 100644 --- a/frontend/src/client/appleMusic/index.html +++ b/frontend/src/client/appleMusic/index.html @@ -11,6 +11,14 @@
+
+

WARNING!

+

A client display is being tampered with!

+

A desktop notification with a warning has already been dispatched.

+ + +
+
autorenew

Loading player...

diff --git a/frontend/src/client/appleMusic/index.js b/frontend/src/client/appleMusic/index.js index 5a4a549..5cbeeb1 100644 --- a/frontend/src/client/appleMusic/index.js +++ b/frontend/src/client/appleMusic/index.js @@ -38,6 +38,7 @@ const app = Vue.createApp( { isPreparingToPlay: false, additionalSongInfo: {}, hasFinishedInit: false, + isShowingWarning: false, // For use with playlists that are partially from apple music and // local drive @@ -290,7 +291,9 @@ const app = Vue.createApp( { } ); } ); } - this.audioPlayer.pause(); + try { + this.audioPlayer.pause(); + } catch ( err ) {} } else { this.audioPlayer.play(); this.musicKit.pause(); @@ -619,6 +622,43 @@ const app = Vue.createApp( { const result = await this.musicKit.api.music( '/v1/catalog/ch/search', queryParameters ); console.log( result ); } )(); + }, + connectToNotifier() { + let source = new EventSource( '/mainNotifier', { withCredentials: true } ); + source.onmessage = ( e ) => { + let data; + try { + data = JSON.parse( e.data ); + } catch ( err ) { + data = { 'type': e.data }; + } + if ( data.type === 'blur' ) { + this.isShowingWarning = true; + } else if ( data.type === 'visibility' ) { + this.isShowingWarning = true; + } + }; + + source.onopen = () => { + console.log( 'client notifier connected successfully' ); + }; + + let self = this; + + source.addEventListener( 'error', function( e ) { + if ( e.eventPhase == EventSource.CLOSED ) source.close(); + + if ( e.target.readyState == EventSource.CLOSED ) { + console.log( 'disconnected' ); + } + + setTimeout( () => { + self.connectToNotifier(); + }, 1000 ); + }, false ); + }, + dismissNotification() { + this.isShowingWarning = false; } }, watch: { @@ -653,6 +693,7 @@ const app = Vue.createApp( { } else { this.initMusicKit(); } + this.connectToNotifier(); fetch( '/getLocalIP' ).then( res => { if ( res.status === 200 ) { res.text().then( ip => { diff --git a/frontend/src/client/appleMusic/style.css b/frontend/src/client/appleMusic/style.css index c2b7aa3..097e646 100644 --- a/frontend/src/client/appleMusic/style.css +++ b/frontend/src/client/appleMusic/style.css @@ -312,4 +312,48 @@ body, html { .slider-inactive { cursor: default !important; +} + + +.warning { + display: flex; + justify-content: center; + align-items: center; + width: 40vw; + height: 50vh; + font-size: 2vh; + background-color: rgb(255, 0, 0); + color: white; + position: fixed; + right: 1vh; + top: 1vh; + flex-direction: column; + z-index: 100; +} + +.warning h3 { + font-size: 4vh; +} + +.warning .flash { + background-color: rgba(255, 0, 0, 0.4); + animation: flashing linear infinite 1s; + width: 100vw; + height: 100vh; + top: 0; + left: 0; + position: fixed; + z-index: -1; +} + +@keyframes flashing { + 0% { + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + } } \ No newline at end of file diff --git a/frontend/src/client/showcase.js b/frontend/src/client/showcase.js index 5058967..64cde9b 100644 --- a/frontend/src/client/showcase.js +++ b/frontend/src/client/showcase.js @@ -17,6 +17,7 @@ createApp( { micAnalyzer: null, beatDetected: false, colorThief: null, + lastDispatch: new Date().getTime() - 5000, }; }, computed: { @@ -290,23 +291,41 @@ createApp( { return flux; }, notifier() { - console.log( 'notifier enabled' ); + if ( parseInt( this.lastDispatch ) + 5000 < new Date().getTime() ) { + + } + Notification.requestPermission(); + + console.warn( '[ notifier ]: Status is now enabled \n\n-> Any leaving or tampering with the website will send a notification to the host' ); // Detect if window is currently in focus window.onblur = () => { - console.log( 'left browser or page' ); + this.sendNotification( 'blur' ); } - // Detect key events - window.addEventListener( 'keypress', keyEvent => { - console.log( keyEvent.key ); + // Detect if browser window becomes hidden (also with blur event) + document.onvisibilitychange = () => { + if ( document.visibilityState === 'hidden' ) { + this.sendNotification( 'visibility' ); + } + }; + }, + sendNotification( notification ) { + let fetchOptions = { + method: 'post', + body: JSON.stringify( { 'type': notification } ), + headers: { + 'Content-Type': 'application/json', + 'charset': 'utf-8' + }, + }; + fetch( '/clientStatusUpdate', fetchOptions ).catch( err => { + console.error( err ); } ); - // Detect if browser window becomes hidden (also with blur event) - document.addEventListener( 'visibilitychange', visibilityEvent => { - if ( document.visibilityState === 'hidden' ) { - console.log( 'left page' ); - } - } ); + new Notification( 'YOU ARE UNDER SURVEILLANCE', { + body: 'Please return to the original webpage immediately!', + requireInteraction: true, + } ) } }, mounted() {