auth enhancements amongst other things
This commit is contained in:
parent
bb9769e917
commit
85c3799fca
21 changed files with 336 additions and 363 deletions
|
|
@ -3,13 +3,13 @@
|
|||
"use strict";
|
||||
|
||||
import { ipcMain } from "electron";
|
||||
import { backgroundMitt } from './emitter';
|
||||
import { backgroundMitt } from '@/modules/emitter';
|
||||
|
||||
const portAudio = require('naudiodon');
|
||||
|
||||
// Audio in and out stream objects.
|
||||
let ai: typeof portAudio.AudioIO | null = null;
|
||||
let ao: typeof portAudio.AudioIO | null = null;
|
||||
let ai: typeof portAudio.AudioIO | Boolean = false;
|
||||
let ao: typeof portAudio.AudioIO | Boolean = false;
|
||||
|
||||
// Whether activly recording.
|
||||
let record = false;
|
||||
|
|
@ -26,9 +26,15 @@ const audioOptions = {
|
|||
closeOnError: false,
|
||||
}
|
||||
|
||||
// Toggles record to true to begin capturing chunks.
|
||||
const onRecordingStart = (_event: any, _payload: any) => {
|
||||
console.log("AUDIO: Beginning audio capture.")
|
||||
record = true;
|
||||
}
|
||||
|
||||
// Returns recorded audio to frontend and sets record to false.
|
||||
const onRecordingEnd = async (_event: any, payload: any) => {
|
||||
console.log("AUDIO:Sending audio to browser.")
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
|
|
@ -59,7 +65,7 @@ export function initAudioIO(): void {
|
|||
|
||||
// If recording, we capture the data.
|
||||
if (record) {
|
||||
console.log('Recording...')
|
||||
console.log('AUDIO:Recording...')
|
||||
audioContainer.input += chunk;
|
||||
}
|
||||
|
||||
|
|
@ -84,11 +90,11 @@ export function initAudioIO(): void {
|
|||
// Listen to record.
|
||||
console.log("AUDIO:Adding recording listeners.")
|
||||
|
||||
ipcMain.removeAllListeners('start-recording');
|
||||
ipcMain.on('start-recording', () => record = true);
|
||||
ipcMain.removeAllListeners("start-recording");
|
||||
ipcMain.on("start-recording", onRecordingStart);
|
||||
|
||||
ipcMain.removeHandler('stop-recording');
|
||||
ipcMain.handle('stop-recording', onRecordingEnd);
|
||||
ipcMain.removeHandler("stop-recording");
|
||||
ipcMain.handle("stop-recording", onRecordingEnd);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -110,10 +116,13 @@ function bufSplit(buf: Buffer, len: number): Array<Buffer> {
|
|||
}
|
||||
|
||||
// Audio playback.
|
||||
export function play(input: Buffer): void {
|
||||
let i = 0;
|
||||
export function play(input: string): void {
|
||||
|
||||
const audio = bufSplit(input, 8192);
|
||||
// Format the audio.
|
||||
const audio = bufSplit(
|
||||
Buffer.from(input as string, 'hex'),
|
||||
8192
|
||||
);
|
||||
|
||||
// Called on end of write.
|
||||
const callback = () => {
|
||||
|
|
@ -131,6 +140,7 @@ export function play(input: Buffer): void {
|
|||
function write() {
|
||||
let chunk: Buffer;
|
||||
let ok = true;
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
chunk = audio[i];
|
||||
|
|
@ -157,42 +167,13 @@ export function play(input: Buffer): void {
|
|||
// Get's called on window close.
|
||||
export function stopStream() {
|
||||
console.log("AUDIO:Stopping audio stream.")
|
||||
if(ai != null) {
|
||||
ai.quit();
|
||||
ai = null;
|
||||
if (ai) {
|
||||
ai.quit()
|
||||
}
|
||||
if (ao != null) {
|
||||
ao.quit();
|
||||
ao = null;
|
||||
if (ao) {
|
||||
ao.quit()
|
||||
}
|
||||
console.log("AUDIO:Audio closed.")
|
||||
}
|
||||
|
||||
|
||||
// // Returns recorded audio to frontend.
|
||||
// const getAudio = async (_event: any, payload: any) => {
|
||||
|
||||
// return new Promise((resolve, reject) => {
|
||||
|
||||
// // Handle auth response from server.
|
||||
// backgroundMitt.once('auth-res', (res: string) => {
|
||||
|
||||
// if (res == "locked") {
|
||||
// reject(res);
|
||||
// } else {
|
||||
// console.log('Success!');
|
||||
// auth = true;
|
||||
// resolve(res);
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// if (typeof payload !== "string") {
|
||||
// payload = JSON.stringify(payload)
|
||||
// }
|
||||
|
||||
// // Send token to backend
|
||||
// socket.send(payload);
|
||||
|
||||
// });
|
||||
// };
|
||||
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import { backgroundMitt } from "./emitter";
|
||||
import { renderMessage } from "@/modules/message";
|
||||
import { Profile, StandardMessage, Annotation } from "@/types/message/index";
|
||||
import { play } from "./audio";
|
||||
|
||||
|
||||
export const handleAuthMessage = (message: string) => {
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'auth-response',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
export const handleProfileMessage = (message: Profile) => {
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'update-profile',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
export const handleStandardMessage = (m: StandardMessage) => {
|
||||
|
||||
// Create a render message object.
|
||||
const message = renderMessage(
|
||||
m.content.text, m.content.audio, m.context, m.modifier);
|
||||
|
||||
// Play audio if any.
|
||||
if (message.content.audio) {
|
||||
const audioBytes = Buffer.from(m.content.audio as string, 'hex');
|
||||
m.content.audio = true;
|
||||
play(audioBytes);
|
||||
}
|
||||
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'render-message',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
export const handleAnnotation = (message: Annotation) => {
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'render-message',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
37
src/background/helpers.ts
Normal file
37
src/background/helpers.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import fs from 'fs';
|
||||
import { backgroundMitt } from "@/modules/emitter";
|
||||
import { SessionState } from "@/types/message";
|
||||
|
||||
|
||||
export const ipcEmit = (channel: string, payload: any) => {
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: channel,
|
||||
message: payload
|
||||
});
|
||||
}
|
||||
|
||||
export const loadState = (fileName: string): SessionState => {
|
||||
let state: SessionState;
|
||||
|
||||
try {
|
||||
state = JSON.parse(fs.readFileSync(fileName).toString());
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
state = {
|
||||
key: false,
|
||||
newMessages: []
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
||||
}
|
||||
|
||||
export const saveState = (fileName: string, state: SessionState) => {
|
||||
fs.writeFile(fileName, JSON.stringify(state), (err) => {
|
||||
if (err) {
|
||||
console.log("Error when saving state.")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -37,15 +37,10 @@ export function initApp(dev: boolean): void {
|
|||
main()
|
||||
});
|
||||
|
||||
// Quit app on window closed.
|
||||
app.on("window-all-closed", () => {
|
||||
console.log("MAIN:Quitting app.")
|
||||
app.quit()
|
||||
app.on("before-quit", () => {
|
||||
});
|
||||
|
||||
// Shutdown audio streams peacefully.
|
||||
app.on("before-quit", () => {
|
||||
stopStream()
|
||||
app.on("window-all-closed", () => {
|
||||
});
|
||||
|
||||
// When user clicks app icon (re-open)
|
||||
|
|
|
|||
|
|
@ -1,62 +1,131 @@
|
|||
/*
|
||||
* Creates a websocket session with Crimata Servers.
|
||||
*
|
||||
* Connects to Servers and attempts token authentication. Will send the result
|
||||
* of the authentication to the window. It will then serve as a communication
|
||||
* interface between the window and the servers. It will automatically try to
|
||||
* reconnect on websocket disconnect.
|
||||
* Connects to Servers and attempts key authentication. Server will respond
|
||||
* with key and user profile. We send the profile to the browser. We also
|
||||
* resend this information on new broser window. We then serve as a
|
||||
* communication interface between the window and the servers. It will
|
||||
* automatically try to reconnect on websocket disconnect.
|
||||
*/
|
||||
|
||||
import { backgroundMitt } from './emitter';
|
||||
import { ipcMain, IpcMainEvent } from "electron";
|
||||
import { backgroundMitt } from "@/modules/emitter";
|
||||
import { ipcEmit, loadState, saveState } from './helpers';
|
||||
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from "electron";
|
||||
|
||||
import useWebSockets from "@/modules/ws";
|
||||
import useWebSockets from "@/modules/websockets";
|
||||
|
||||
import { handleAuthMessage, handleStandardMessage, handleProfileMessage, handleAnnotation } from "./handlers";
|
||||
import { play } from "./audio";
|
||||
import { renderMessage } from "@/modules/message";
|
||||
import { AuthProtocol, SessionState, Profile } from "@/types/message";
|
||||
|
||||
// Key for key-based auth.
|
||||
let key: string;
|
||||
let win = true;
|
||||
|
||||
// Info saved to json on quit (key, newMessages).
|
||||
let state: SessionState;
|
||||
|
||||
// Profile of current user.
|
||||
let profile: Profile | boolean;
|
||||
|
||||
// Called when server sends auth message.
|
||||
const updateState = (res: AuthProtocol) => {
|
||||
console.log("SESS:Auth message received: \n" +
|
||||
` key: ${res.key}\n` +
|
||||
` alias: ${res.profile}`)
|
||||
|
||||
if (state) {
|
||||
|
||||
// Update key.
|
||||
state.key = res.key;
|
||||
|
||||
// Update the user profile.
|
||||
profile = res.profile;
|
||||
|
||||
// Send upated profile to frontend.
|
||||
console.log("SESS:Sending updated user profile to browser.")
|
||||
ipcEmit("update-state", {
|
||||
profile: profile,
|
||||
newMessages: state.newMessages
|
||||
})
|
||||
|
||||
// Save the updated state to json.
|
||||
console.log("SESS:Saving session state.")
|
||||
saveState("session.json", state)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Send state on new window.
|
||||
const onNewBrowserWindow = (_event: IpcMainInvokeEvent, _payload: any) => {
|
||||
console.log("SESS:New window, sending profile.")
|
||||
if (profile) {
|
||||
ipcEmit("update-state", {
|
||||
profile: profile,
|
||||
newMessages: state.newMessages
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Calls appropriate endpoint for a server message.
|
||||
const onMessage = (data: string) => {
|
||||
const message = JSON.parse(data)
|
||||
let message = JSON.parse(data)
|
||||
|
||||
// AuthProtocol message.
|
||||
if (message.hasOwnProperty("key")) {
|
||||
handleAuthMessage(message)
|
||||
updateState(message)
|
||||
}
|
||||
|
||||
// Standard message.
|
||||
else if (message.content) {
|
||||
handleStandardMessage(message)
|
||||
}
|
||||
|
||||
else if (message.first) {
|
||||
handleProfileMessage(message)
|
||||
// Convert to render message
|
||||
message = renderMessage(
|
||||
message.content.text,
|
||||
message.content.audio,
|
||||
message.context,
|
||||
message.modifier
|
||||
);
|
||||
|
||||
if (win) {
|
||||
console.log("SESS:Emitting standard message.")
|
||||
if (message.audio) {
|
||||
play(message.audio)
|
||||
}
|
||||
ipcEmit("render-message", message)
|
||||
}
|
||||
|
||||
else {
|
||||
console.log("SESS:No window: saving message.")
|
||||
state.newMessages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
handleAnnotation(message)
|
||||
if (win) {
|
||||
ipcEmit("render-message", message)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Attempt token authentication onOpen.
|
||||
// When socket connects, we update state.
|
||||
const onOpen = () => {
|
||||
if (key) {
|
||||
console.log(`SESS:Sending key: ${state.key}`)
|
||||
|
||||
if (state) {
|
||||
sendMessage({
|
||||
"key": key,
|
||||
"key": state.key,
|
||||
"usr": false,
|
||||
"pwd": false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Websockets module.
|
||||
const { createSocket, sendMessage } = useWebSockets(onMessage, onOpen);
|
||||
|
||||
// Handle messages from window/client.
|
||||
const onMessageFromWindow = (_event: IpcMainEvent, payload: any) => {
|
||||
const onClientMessage = (_event: IpcMainEvent, payload: any) => {
|
||||
console.log("New client message")
|
||||
|
||||
const success = sendMessage(payload)
|
||||
|
|
@ -68,17 +137,30 @@ const onMessageFromWindow = (_event: IpcMainEvent, payload: any) => {
|
|||
}
|
||||
|
||||
// Call this to initialize session with Crimata servers.
|
||||
export const initSession = (key: string) => {
|
||||
export const initSession = () => {
|
||||
console.log("SESS:Creating new session.")
|
||||
|
||||
// Set the key.
|
||||
key = key;
|
||||
// Load Json or createState.
|
||||
state = loadState("session.json")
|
||||
|
||||
console.log("SESS:State loaded: \n" +
|
||||
` key: ${state.key}\n` +
|
||||
` new: ${state.newMessages}`)
|
||||
|
||||
// Open socket connection.
|
||||
createSocket()
|
||||
|
||||
// Attack browser window init listener.
|
||||
ipcMain.removeAllListeners("app-mounted")
|
||||
ipcMain.on("app-mounted", onNewBrowserWindow);
|
||||
|
||||
// Attach listeners for frontend.
|
||||
ipcMain.removeAllListeners("client-message")
|
||||
ipcMain.on("client-message", onMessageFromWindow);
|
||||
ipcMain.on("client-message", onClientMessage);
|
||||
|
||||
|
||||
}
|
||||
// Keep win up-to-date.
|
||||
backgroundMitt.on('window-active', (state: boolean) => {
|
||||
win = state;
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { BrowserWindow, ipcMain } from "electron";
|
||||
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
||||
import { backgroundMitt } from './emitter';
|
||||
import { backgroundMitt } from '@/modules/emitter';
|
||||
import { RenderMessage } from "@/types/message/index";
|
||||
import * as path from "path";
|
||||
import fs from 'fs';
|
||||
|
|
@ -50,8 +50,9 @@ const saveWindowState = () => {
|
|||
);
|
||||
|
||||
fs.writeFile('windowState.json', state, (err) => {
|
||||
if (err) throw err;
|
||||
return;
|
||||
if (err) {
|
||||
console.log("BW:Error saving window state.")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -64,12 +65,12 @@ const onWindowMount = (): void => {
|
|||
backgroundMitt.emit('window-active', true);
|
||||
|
||||
// Handle win nav-bar event.
|
||||
ipcMain.removeHandler("nav-bar") // avoid setting duplicate handlers
|
||||
ipcMain.handle("nav-bar", onNavBar);
|
||||
ipcMain.removeAllListeners("nav-bar") // avoid setting duplicate handlers
|
||||
ipcMain.on("nav-bar", onNavBar);
|
||||
|
||||
// Gateway for messages to the frontend.
|
||||
backgroundMitt.removeAllListeners('ipc-renderer')
|
||||
backgroundMitt.on('ipc-renderer', renderMessage);
|
||||
backgroundMitt.removeAllListeners("ipc-renderer")
|
||||
backgroundMitt.on("ipc-renderer", renderMessage);
|
||||
|
||||
console.log("BW:Listeners created.")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue