From fe954f296050ad8bef363d58fa5b66c4260fecf6 Mon Sep 17 00:00:00 2001 From: riqo Date: Sat, 22 May 2021 15:02:49 -0500 Subject: [PATCH 1/4] remove auth from sesssion --- src/App.vue | 4 +++- src/background/init.ts | 22 ++++++++++++++++-- src/background/session.ts | 43 ++++++++++++++++++------------------ src/components/messenger.vue | 15 ++++++------- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/App.vue b/src/App.vue index 320b62f..86d1007 100644 --- a/src/App.vue +++ b/src/App.vue @@ -41,6 +41,7 @@ import Messenger from "@/components/messenger.vue"; import Login from "@/components/login.vue"; export default defineComponent({ + components: { Splash, Messenger, @@ -72,7 +73,8 @@ export default defineComponent({ } // Set profile and newMessages. - profile.value = payload.message.profile; + // profile.value = payload.message.profile; + profile.value = true; newMessages.value = payload.message.newMessages; ready.value = true; diff --git a/src/background/init.ts b/src/background/init.ts index a17716c..5150d9f 100644 --- a/src/background/init.ts +++ b/src/background/init.ts @@ -3,7 +3,7 @@ import { app, dialog } from "electron"; import { createWindow } from './window'; -import { initSession } from './session'; +import { initSession, updateState } from './session'; import { initAudioIO, stopStream } from './audio'; import { backgroundMitt } from '@/modules/emitter'; @@ -15,7 +15,7 @@ backgroundMitt.on('window-active', (state: boolean) => { }); // Auto updating. -const { autoUpdater } = require('electron-updater') +const { autoUpdater } = require('electron-updater'); autoUpdater.requestHeaders = { 'PRIVATE-TOKEN': 'mvvgWYwWnot4bisiQMh_' } autoUpdater.on('update-available', (info: any) => { @@ -38,6 +38,13 @@ autoUpdater.on('update-downloaded', (info: any) => { }) + +interface Profile { + crimataId: string; + alias: string; + initials: string; +} + // Run when electron app is initialized. async function main() { console.log("MAIN:Initializing Electron App.") @@ -45,6 +52,17 @@ async function main() { // Must wait til window is created. await createWindow(); + // authenticate against business backend + const auth = async (crimataId: string): Promise => { + updateState({key: "", profile: { + crimataId: "", + alias: "", + initials: "" + }}); + return null + }; + auth(''); + // Instantiate socket session with crimata-platorm. initSession(); diff --git a/src/background/session.ts b/src/background/session.ts index 35773c8..0a8b91d 100644 --- a/src/background/session.ts +++ b/src/background/session.ts @@ -1,10 +1,10 @@ /* * Creates a websocket session with Crimata Servers. - * - * 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 + * + * 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. */ @@ -16,7 +16,7 @@ import useWebSockets from "@/modules/websockets"; import { play } from "./audio"; import { renderMessage } from "@/modules/message"; -import { AuthProtocol, SessionState, Profile } from "@/types"; +import { AuthProtocol, SessionState, Profile } from "@/types"; let win = true; @@ -27,7 +27,7 @@ let state: SessionState; let profile: Profile | boolean; // Called when server sends auth message. -const updateState = (res: AuthProtocol) => { +export const updateState = (res: AuthProtocol) => { console.log("SESS:Auth message received: \n" + ` key: ${res.key}\n` + ` alias: ${res.profile}`) @@ -71,12 +71,10 @@ const onMessage = (data: string) => { let message = JSON.parse(data) // AuthProtocol message. - if (message.hasOwnProperty("key")) { - updateState(message) - } + updateState(message) // Standard message. - else if (message.content) { + if (message.content) { // Convert to render message message = renderMessage( @@ -93,7 +91,7 @@ const onMessage = (data: string) => { } ipcEmit("render-message", message) } - + else { console.log("SESS:No window: saving message.") state.newMessages.push(message); @@ -110,14 +108,14 @@ const onMessage = (data: string) => { // When socket connects, we update state. const onOpen = () => { - console.log(`SESS:Sending key: ${state.key}`) + // console.log(`SESS:Sending key: ${state.key}`) if (state) { - sendMessage({ - "key": state.key, - "usr": false, - "pwd": false - }) + // sendMessage({ + // "key": state.key, + // "usr": false, + // "pwd": false + // }) } } @@ -128,12 +126,15 @@ const { createSocket, sendMessage } = useWebSockets(onMessage, onOpen); const onClientMessage = (_event: IpcMainEvent, payload: any) => { console.log("New client message") - const success = sendMessage(payload) + if (payload.hasOwnProperty("key")) { + return + } + const success = sendMessage(payload); if (!success) { - console.log("Unable to send message: ", payload) + console.log("Unable to send message: ", payload); } - + } // Call this to initialize session with Crimata servers. diff --git a/src/components/messenger.vue b/src/components/messenger.vue index edc50a1..f5512a9 100644 --- a/src/components/messenger.vue +++ b/src/components/messenger.vue @@ -6,9 +6,9 @@
-
@@ -48,25 +48,24 @@ export default defineComponent({ // Load the message history. if (crimataId === window.localStorage.getItem("last_usr")) { - prepMessageView(props.newMessages) + prepMessageView(props.newMessages); } else { - messages.value.clear() + messages.value.clear(); } // New content listeners. emitter.on('self-message', (message) => updateMessageView(message)); window.ipcRenderer.on("render-message", (_e: any, payload: any) => { - updateMessageView(payload.message) + updateMessageView(payload.message); }); // Save the usr for next time. - window.localStorage.setItem("last_usr", crimataId) + window.localStorage.setItem("last_usr", crimataId); }); onUnmounted(() => { window.ipcRenderer.removeAllListeners("render-message"); - window.ipcRenderer.removeAllListeners("annotate-message"); emitter.all.clear(); }); From ea3fc85371d9c6d29f398b60eca74f5b9101f2bf Mon Sep 17 00:00:00 2001 From: riqo Date: Tue, 25 May 2021 09:01:28 -0500 Subject: [PATCH 2/4] add user-auth and init-session events --- package.json | 2 + src/App.vue | 45 ++++++++++++---- src/background/init.ts | 62 +++++++++++++++-------- src/background/session.ts | 56 ++++++++------------ src/{modules => background}/websockets.ts | 29 ++++++++--- src/background/window.ts | 4 -- src/modules/auth.ts | 34 +++++++++++++ yarn.lock | 19 +++++++ 8 files changed, 174 insertions(+), 77 deletions(-) rename src/{modules => background}/websockets.ts (70%) create mode 100644 src/modules/auth.ts diff --git a/package.json b/package.json index 961035f..369eac4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/uuid": "^8.3.0", "@types/ws": "^7.2.7", "animejs": "^3.2.0", + "axios": "^0.21.1", "core-js": "^3.6.5", "electron-is-dev": "^2.0.0", "electron-updater": "^4.3.8", @@ -36,6 +37,7 @@ "ws": "^7.3.1" }, "devDependencies": { + "@types/axios": "^0.14.0", "@types/electron-devtools-installer": "^2.2.0", "@types/jest": "^24.0.19", "@typescript-eslint/eslint-plugin": "^2.33.0", diff --git a/src/App.vue b/src/App.vue index 86d1007..af89d48 100644 --- a/src/App.vue +++ b/src/App.vue @@ -35,11 +35,13 @@ import { defineComponent, onMounted, onUnmounted, ref } from "vue"; import { IpcRendererEvent } from "electron"; import { useIpc } from "@/modules/ipc"; +import { useAuth } from "@/modules/auth" import Splash from "@/components/splash.vue"; import Messenger from "@/components/messenger.vue"; import Login from "@/components/login.vue"; + export default defineComponent({ components: { @@ -49,7 +51,10 @@ export default defineComponent({ }, setup() { - const { post } = useIpc(); + + const { post, invoke } = useIpc(); + + const { user } = useAuth(); // Whether browser has received user info yet. const ready = ref(false); @@ -58,18 +63,19 @@ export default defineComponent({ const profile = ref(false); // New messages that browser missed while closed. - const newMessages = ref([]) + const newMessages = ref([]); // Receive updated information about the session. const updateState = (_event: IpcRendererEvent, payload: any) => { + console.log('YAYAYAYAYA'); console.log("APP:Received updated profile and new messages: \n" + ` profile: ${payload.message.profile}\n` + - ` new: ${payload.message.newMessages}`) + ` new: ${payload.message.newMessages}`); if (payload.message.profile) { - console.log(`APP:Logged-in, showing Messenger View.`) + console.log(`APP:Logged-in, showing Messenger View.`); } else { - console.log(`APP:Logged-out, showing Login View.`) + console.log(`APP:Logged-out, showing Login View.`); } // Set profile and newMessages. @@ -81,8 +87,8 @@ export default defineComponent({ } - onMounted(() => { - console.log("APP:mounted.") + onMounted(async () => { + console.log("APP:mounted."); window.ipcRenderer.on("update-state", updateState) window.ipcRenderer.on("update_available", () => { console.log('testing auto update'); @@ -91,11 +97,32 @@ export default defineComponent({ window.ipcRenderer.on("update_downloaded", () => { console.log('testing update download'); }) - post("app-mounted", "") + + post("app-mounted", ""); + + if(user.value.key) { + try { + const res = await invoke('user-auth', JSON.stringify(user.value)); + profile.value = res; + } catch(e) { + profile.value = false; + console.log('[AUTH]', e); + } finally { + ready.value = true; + if (profile.value) { + // start session + post("init-session", user.value.email); + } + } + } else { + profile.value = false; + ready.value = true; + } + }); onUnmounted(() => { - window.ipcRenderer.removeAllListeners("update-state") + window.ipcRenderer.removeAllListeners("update-state"); }) return { diff --git a/src/background/init.ts b/src/background/init.ts index 5150d9f..be260e7 100644 --- a/src/background/init.ts +++ b/src/background/init.ts @@ -1,11 +1,12 @@ "use strict"; -import { app, dialog } from "electron"; +import { app, dialog, ipcMain, IpcMainInvokeEvent } from "electron"; import { createWindow } from './window'; -import { initSession, updateState } from './session'; +import { initSession, onAppMounted } from './session'; import { initAudioIO, stopStream } from './audio'; import { backgroundMitt } from '@/modules/emitter'; +import { Profile } from "@/types"; let win: boolean; @@ -38,36 +39,53 @@ autoUpdater.on('update-downloaded', (info: any) => { }) - -interface Profile { - crimataId: string; - alias: string; - initials: string; +interface AccountAuth { + email: string; + password?: string; + key: string | null; } -// Run when electron app is initialized. -async function main() { - console.log("MAIN:Initializing Electron App.") +const onUserAuth = async (_event: any, payload: AccountAuth) +:Promise => ( + new Promise((resolve, reject) => { - // Must wait til window is created. - await createWindow(); + // check for login + if (payload.password) { + // await user login - // authenticate against business backend - const auth = async (crimataId: string): Promise => { - updateState({key: "", profile: { - crimataId: "", - alias: "", - initials: "" - }}); - return null - }; - auth(''); + } else { + // authenticate token + } + }) +) +const onSessionInit = (_event: IpcMainInvokeEvent, payload: string) => { // Instantiate socket session with crimata-platorm. initSession(); // Begin audio stream. initAudioIO(); +} + + + +// Run when electron app is initialized. +async function main() { + + console.log("MAIN:Initializing Electron App."); + + // Attack browser window init listener. + ipcMain.removeAllListeners("app-mounted"); + ipcMain.on("app-mounted", onAppMounted); + + ipcMain.removeHandler("user-auth"); + ipcMain.handle("user-auth", onUserAuth); + + ipcMain.removeAllListeners("init-session"); + ipcMain.on("init-session", onSessionInit); + + // Must wait til window is created. + await createWindow(); } diff --git a/src/background/session.ts b/src/background/session.ts index 0a8b91d..8128a67 100644 --- a/src/background/session.ts +++ b/src/background/session.ts @@ -12,7 +12,7 @@ import { backgroundMitt } from "@/modules/emitter"; import { ipcEmit, loadState, saveToJson } from './helpers'; import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from "electron"; -import useWebSockets from "@/modules/websockets"; +import useWebSockets from "./websockets"; import { play } from "./audio"; import { renderMessage } from "@/modules/message"; @@ -56,22 +56,23 @@ export const updateState = (res: AuthProtocol) => { } // Send state on new window. -const onNewBrowserWindow = (_event: IpcMainInvokeEvent, _payload: any) => { +export const onAppMounted = (_event: IpcMainInvokeEvent, _payload: any) => { + + profile = true; + console.log('testing!!!', profile) if (typeof profile !== 'undefined') { - console.log("SESS:Sending user profile to browser.") + console.log("SESS:Sending user profile to browser."); ipcEmit("update-state", { profile: profile, - newMessages: state.newMessages - }) + newMessages: [], + }); } } // Calls appropriate endpoint for a server message. const onMessage = (data: string) => { - let message = JSON.parse(data) - - // AuthProtocol message. - updateState(message) + let message = JSON.parse(data); + console.log('received new message', message); // Standard message. if (message.content) { @@ -106,57 +107,42 @@ const onMessage = (data: string) => { }; -// When socket connects, we update state. -const onOpen = () => { - // console.log(`SESS:Sending key: ${state.key}`) - - if (state) { - // sendMessage({ - // "key": state.key, - // "usr": false, - // "pwd": false - // }) - } -} // Websockets module. -const { createSocket, sendMessage } = useWebSockets(onMessage, onOpen); +const { createSocket, send } = useWebSockets(onMessage); // Handle messages from window/client. -const onClientMessage = (_event: IpcMainEvent, payload: any) => { +const onClientMessage = async (_event: IpcMainEvent, payload: any):Promise => { console.log("New client message") if (payload.hasOwnProperty("key")) { return } - const success = sendMessage(payload); - - if (!success) { + try { + send(payload) + } catch(e) { console.log("Unable to send message: ", payload); } } + // Call this to initialize session with Crimata servers. export const initSession = () => { console.log("SESS:Creating new session.") // Load Json or createState. - state = loadState("session.json") - - console.log("SESS:State loaded: \n" + - ` key: ${state.key}\n` + - ` new: ${state.newMessages}`) + state = loadState("session.json"); // Open socket connection. - createSocket() + createSocket(); // Attack browser window init listener. - ipcMain.removeAllListeners("app-mounted") - ipcMain.on("app-mounted", onNewBrowserWindow); + // ipcMain.removeAllListeners("app-mounted"); + // ipcMain.on("app-mounted", onAppMounted); // Attach listeners for frontend. - ipcMain.removeAllListeners("client-message") + ipcMain.removeAllListeners("client-message"); ipcMain.on("client-message", onClientMessage); // Keep win up-to-date. diff --git a/src/modules/websockets.ts b/src/background/websockets.ts similarity index 70% rename from src/modules/websockets.ts rename to src/background/websockets.ts index 9af0448..a6ae482 100644 --- a/src/modules/websockets.ts +++ b/src/background/websockets.ts @@ -1,13 +1,16 @@ "use strict"; import WebSocket from 'ws'; -import { ipcMain } from "electron"; let socket: WebSocket; + // Run every time we want to connect to backend. -export default function useWebSockets(receiveCallback: (s: string) => any, openCallback: () => any) { +export default function useWebSockets( + receiveCallback: (s: string) => void, + openCallback?: () => void +) { // Returns bool (sucess or fail). const sendMessage = (data: any) => { @@ -24,11 +27,22 @@ export default function useWebSockets(receiveCallback: (s: string) => any, openC } - const onOpen = (event: WebSocket.OpenEvent) => { + const send = async (data: Record):Promise => ( + new Promise((resolve, reject) => { + if (socket.readyState !== 1) { + reject(false); + } + socket.send(JSON.stringify(data)) + resolve(true); + + })) + + + const onOpen = (_event: WebSocket.OpenEvent) => { console.log("WS:Connected to WS Server!"); - openCallback() + if (openCallback) openCallback(); } @@ -40,7 +54,7 @@ export default function useWebSockets(receiveCallback: (s: string) => any, openC } - const onClose = (event: WebSocket.CloseEvent) => { + const onClose = (_event: WebSocket.CloseEvent) => { console.log("WS:Socket closed normally.") } @@ -66,7 +80,8 @@ export default function useWebSockets(receiveCallback: (s: string) => any, openC return { createSocket, - sendMessage + sendMessage, + send } -} \ No newline at end of file +} diff --git a/src/background/window.ts b/src/background/window.ts index e312572..162b3e1 100644 --- a/src/background/window.ts +++ b/src/background/window.ts @@ -57,7 +57,6 @@ const saveWindowState = () => { // Do this on window mount. const onWindowMount = (): void => { - console.log("BW:Adding window listeners. ") // Must tell initApp that window exists. backgroundMitt.emit('window-active', true); @@ -70,12 +69,10 @@ const onWindowMount = (): void => { backgroundMitt.removeAllListeners("ipc-renderer") backgroundMitt.on("ipc-renderer", renderMessage); - console.log("BW:Listeners created.") } // Do this on window dismount (close). const onWindowDismount = (): void => { - console.log("BW:Window closed.") win = null; backgroundMitt.emit('window-active', false); } @@ -89,7 +86,6 @@ export async function createWindow(): Promise { // Load the saved window state. winState = loadWinState("window.json") - console.log(`BW:Creating window [${winState.width}, ${winState.height}].`) // Define the browser window. win = new BrowserWindow({ diff --git a/src/modules/auth.ts b/src/modules/auth.ts new file mode 100644 index 0000000..bba8bdd --- /dev/null +++ b/src/modules/auth.ts @@ -0,0 +1,34 @@ +import { ref, Ref} from "vue"; + +interface UserState { + email: string | null; + password?: string; + key: string | null; +} + +const user:Ref = ref({ + email: null, + key: "testing" +}); + +const CRIMATA_KEY = "CRIMATA_KEY"; + +const raw = window.localStorage.getItem(CRIMATA_KEY); + +if (raw) { + user.value = JSON.parse(raw); +} + +export const useAuth = () => { + + const setUser = (token: UserState) => { + window.localStorage.setItem(CRIMATA_KEY, JSON.stringify(token)); + user.value = token; + } + + return { + setUser, + user + } + +} diff --git a/yarn.lock b/yarn.lock index 8dc3fba..6d0785b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1279,6 +1279,13 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/axios@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" + integrity sha1-7CMA++fX3d1+udOr+HmZlkyvzkY= + dependencies: + axios "*" + "@types/babel__core@^7.1.0": version "7.1.12" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d" @@ -2733,6 +2740,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@*, axios@^0.21.1: + version "0.21.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -5871,6 +5885,11 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== +follow-redirects@^1.10.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" + integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" From cc2babdadc9835848d6a1294e41ef44a42d86103 Mon Sep 17 00:00:00 2001 From: riqo Date: Fri, 28 May 2021 09:03:49 -0500 Subject: [PATCH 3/4] add electron store --- package.json | 1 + src/App.vue | 41 ++++++------- src/api/account.ts | 36 ++++++++++++ src/background/init.ts | 34 +++-------- src/background/ipc/account.ts | 105 ++++++++++++++++++++++++++++++++++ src/background/ipc/index.ts | 0 src/background/session.ts | 55 +++--------------- src/background/store.ts | 16 ++++++ src/background/websockets.ts | 2 +- src/components/login.vue | 21 +++++-- src/components/settings.vue | 19 ++++-- src/modules/auth.ts | 35 ++++-------- src/modules/http.ts | 52 +++++++++++++++++ src/types.ts | 10 ++-- yarn.lock | 99 +++++++++++++++++++++++++++++++- 15 files changed, 391 insertions(+), 135 deletions(-) create mode 100644 src/api/account.ts create mode 100644 src/background/ipc/account.ts create mode 100644 src/background/ipc/index.ts create mode 100644 src/background/store.ts create mode 100644 src/modules/http.ts diff --git a/package.json b/package.json index 369eac4..ec3ff75 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "axios": "^0.21.1", "core-js": "^3.6.5", "electron-is-dev": "^2.0.0", + "electron-store": "^8.0.0", "electron-updater": "^4.3.8", "mitt": "^2.1.0", "naudiodon": "^2.3.2", diff --git a/src/App.vue b/src/App.vue index af89d48..71eb7c3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -35,7 +35,7 @@ import { defineComponent, onMounted, onUnmounted, ref } from "vue"; import { IpcRendererEvent } from "electron"; import { useIpc } from "@/modules/ipc"; -import { useAuth } from "@/modules/auth" +import { useProfile } from "@/modules/auth" import Splash from "@/components/splash.vue"; import Messenger from "@/components/messenger.vue"; @@ -54,13 +54,13 @@ export default defineComponent({ const { post, invoke } = useIpc(); - const { user } = useAuth(); + const { profile, setProfile, clearProfile } = useProfile(); // Whether browser has received user info yet. const ready = ref(false); // Information about current user. - const profile = ref(false); + // const profile = ref(false); // New messages that browser missed while closed. const newMessages = ref([]); @@ -80,7 +80,7 @@ export default defineComponent({ // Set profile and newMessages. // profile.value = payload.message.profile; - profile.value = true; + // profile.value = true; newMessages.value = payload.message.newMessages; ready.value = true; @@ -100,23 +100,20 @@ export default defineComponent({ post("app-mounted", ""); - if(user.value.key) { - try { - const res = await invoke('user-auth', JSON.stringify(user.value)); - profile.value = res; - } catch(e) { - profile.value = false; - console.log('[AUTH]', e); - } finally { - ready.value = true; - if (profile.value) { - // start session - post("init-session", user.value.email); - } - } - } else { - profile.value = false; + try { + + const res = await invoke('user-profile', ""); + console.log('auth response', res); + setProfile(res); + } catch(e) { + console.log('[AUTH]', e); + } finally { ready.value = true; + if (profile.value.crimataId) { + console.log('TESTING') + // start session + post("init-session", JSON.stringify(profile.value.crimataId)); + } } }); @@ -127,9 +124,9 @@ export default defineComponent({ return { ready, - profile, post, - newMessages + newMessages, + profile } } }) diff --git a/src/api/account.ts b/src/api/account.ts new file mode 100644 index 0000000..de57837 --- /dev/null +++ b/src/api/account.ts @@ -0,0 +1,36 @@ + +import { useHttp } from "@/modules/http"; +import axios from "axios"; + +const { post } = useHttp(); + +export const submit = + async (email: string, password: string) => ( + + await post('/account/login', { + email, + password + }) + +) + + +export const logout = + async () : Promise => (await post('/account/logout')); + + + +export const fetchProfile = async(email: string, token: string) => ( + + await axios({ + url: "http://127.0.0.1:3010/api/account/profile", + headers: { + Cookie: `jwt=${token}` + }, + method: 'GET', + data: { + email, + } + }) +) + diff --git a/src/background/init.ts b/src/background/init.ts index be260e7..21f9c45 100644 --- a/src/background/init.ts +++ b/src/background/init.ts @@ -6,7 +6,7 @@ import { createWindow } from './window'; import { initSession, onAppMounted } from './session'; import { initAudioIO, stopStream } from './audio'; import { backgroundMitt } from '@/modules/emitter'; -import { Profile } from "@/types"; +import { initAccountListener } from "@/background/ipc/account"; let win: boolean; @@ -39,38 +39,18 @@ autoUpdater.on('update-downloaded', (info: any) => { }) -interface AccountAuth { - email: string; - password?: string; - key: string | null; -} -const onUserAuth = async (_event: any, payload: AccountAuth) -:Promise => ( - new Promise((resolve, reject) => { - - // check for login - if (payload.password) { - // await user login - - } else { - // authenticate token - } - }) -) - -const onSessionInit = (_event: IpcMainInvokeEvent, payload: string) => { +const onSessionInit = (_event: IpcMainInvokeEvent, cid: string) => { // Instantiate socket session with crimata-platorm. - initSession(); + initSession(cid); // Begin audio stream. initAudioIO(); } - // Run when electron app is initialized. -async function main() { +async function main(): Promise { console.log("MAIN:Initializing Electron App."); @@ -78,12 +58,12 @@ async function main() { ipcMain.removeAllListeners("app-mounted"); ipcMain.on("app-mounted", onAppMounted); - ipcMain.removeHandler("user-auth"); - ipcMain.handle("user-auth", onUserAuth); - ipcMain.removeAllListeners("init-session"); ipcMain.on("init-session", onSessionInit); + // handler user auth, login, and logout asynchrounously + initAccountListener(); + // Must wait til window is created. await createWindow(); diff --git a/src/background/ipc/account.ts b/src/background/ipc/account.ts new file mode 100644 index 0000000..932c2b2 --- /dev/null +++ b/src/background/ipc/account.ts @@ -0,0 +1,105 @@ + +"use strict"; + +import { Profile } from "@/types"; +import { submit, fetchProfile, logout } from "@/api/account"; +import { ipcMain } from "electron"; +import { store } from "@/background/store"; + + +const parseAuthRes = (authRes: any) => { + const token = authRes.headers['set-cookie'][0].split(";")[0].split("=")[1] as string; + const profile = authRes.data as Profile; + return { + token, + profile + } +}; + + +const onProfile = async (_event: any, _payload: string) => ( + + new Promise(async (resolve, reject) => { + + // get jwt token and crimataId from store + const token = store.get('key'); + const crimataId = store.get('crimataId'); + + // authenticate and fetch profile + try { + + const res = await fetchProfile( + crimataId, + token + ); + + const parsed = parseAuthRes(res); + resolve(parsed.profile); + + } catch(e) { + reject(new Error('Failed to fetch profile.')); + } + }) +) + + +const onLogin = async (_event: any, payload: string) => ( + + new Promise(async (resolve, reject) => { + + const account = JSON.parse(payload); + + if ( account.password && account.email ) { + try { + const res = await submit(account.email, account.password); + const parsed = parseAuthRes(res); + + // save jwt token and profile + store.set('key', parsed.token); + store.set('crimataId', parsed.profile.crimataId); + + // return profile to renderer + resolve(parsed.profile); + + } catch(e) { + console.log('[API]', e.response); + reject(new Error('Failed to authenticate')); + } + } + }) +) + +const onLogout = async (_event: any, _payload: string) => ( + + new Promise(async (resolve, reject) => { + + try { + // post logout to backend + await logout(); + + // remove key and crimataId + store.delete('key'); + store.delete('crimataId'); + + // TODO: kill crimata platform session + + resolve(null); + } catch(e) { + reject(new Error('Failed to logout. Please try again.')); + } + }) +) + + +export const initAccountListener = () => { + + ipcMain.removeHandler("user-profile"); + ipcMain.handle("user-profile", onProfile); + + ipcMain.removeHandler("user-login"); + ipcMain.handle("user-login", onLogin); + + ipcMain.removeHandler("user-logout"); + ipcMain.handle("user-logout", onLogout); + +} diff --git a/src/background/ipc/index.ts b/src/background/ipc/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/background/session.ts b/src/background/session.ts index 8128a67..a619f33 100644 --- a/src/background/session.ts +++ b/src/background/session.ts @@ -16,57 +16,20 @@ import useWebSockets from "./websockets"; import { play } from "./audio"; import { renderMessage } from "@/modules/message"; -import { AuthProtocol, SessionState, Profile } from "@/types"; +import { SessionState } from "@/types"; 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. -export 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.") - saveToJson("session.json", state) - - } - -} - // Send state on new window. export const onAppMounted = (_event: IpcMainInvokeEvent, _payload: any) => { - profile = true; - console.log('testing!!!', profile) - if (typeof profile !== 'undefined') { - console.log("SESS:Sending user profile to browser."); - ipcEmit("update-state", { - profile: profile, - newMessages: [], - }); - } + ipcEmit("update-state", { + newMessages: state.newMessages, + }); + } // Calls appropriate endpoint for a server message. @@ -112,7 +75,7 @@ const onMessage = (data: string) => { const { createSocket, send } = useWebSockets(onMessage); // Handle messages from window/client. -const onClientMessage = async (_event: IpcMainEvent, payload: any):Promise => { +const onClientMessage = async (_event: IpcMainEvent, payload: any): Promise => { console.log("New client message") if (payload.hasOwnProperty("key")) { @@ -128,7 +91,7 @@ const onClientMessage = async (_event: IpcMainEvent, payload: any):Promise // Call this to initialize session with Crimata servers. -export const initSession = () => { +export const initSession = (cid: string) => { console.log("SESS:Creating new session.") // Load Json or createState. @@ -137,10 +100,6 @@ export const initSession = () => { // Open socket connection. createSocket(); - // Attack browser window init listener. - // ipcMain.removeAllListeners("app-mounted"); - // ipcMain.on("app-mounted", onAppMounted); - // Attach listeners for frontend. ipcMain.removeAllListeners("client-message"); ipcMain.on("client-message", onClientMessage); diff --git a/src/background/store.ts b/src/background/store.ts new file mode 100644 index 0000000..16f8409 --- /dev/null +++ b/src/background/store.ts @@ -0,0 +1,16 @@ + +const Store = require('electron-store'); + +const schema = { + key: { + type: 'string', + }, + crimataId: { + type: 'string' + } +}; + +export const store = new Store({ + schema, + encryptionKey: "super user test" +}); diff --git a/src/background/websockets.ts b/src/background/websockets.ts index a6ae482..dfbd9cb 100644 --- a/src/background/websockets.ts +++ b/src/background/websockets.ts @@ -27,7 +27,7 @@ export default function useWebSockets( } - const send = async (data: Record):Promise => ( + const send = async (data: Record): Promise => ( new Promise((resolve, reject) => { if (socket.readyState !== 1) { reject(false); diff --git a/src/components/login.vue b/src/components/login.vue index 521517c..cedce14 100644 --- a/src/components/login.vue +++ b/src/components/login.vue @@ -25,7 +25,7 @@ - + @@ -37,21 +37,32 @@ import { defineComponent, ref } from "vue"; import { useIpc } from "@/modules/ipc"; import { authRequest } from '@/modules/message'; +import { useProfile } from '@/modules/auth'; export default defineComponent({ name: "Login", setup() { - const { post } = useIpc(); + const { post, invoke } = useIpc(); + const { setProfile } = useProfile(); const usr = ref(""); const pwd = ref(""); // Submit login credentials to the backend. - const submitForm = () => { - console.log(`Submitting login form: ${usr.value}, ${pwd.value}`) - post("client-message", authRequest(false, usr.value, pwd.value)) + const submitForm = async () => { + try { + const res = await invoke('user-login', JSON.stringify({ + email: usr.value, + password: pwd.value + })); + setProfile(res); + } catch(e) { + + } + // console.log(`Submitting login form: ${usr.value}, ${pwd.value}`) + // post("client-message", authRequest(false, usr.value, pwd.value)) } return { diff --git a/src/components/settings.vue b/src/components/settings.vue index 51347b2..9632b97 100644 --- a/src/components/settings.vue +++ b/src/components/settings.vue @@ -30,6 +30,7 @@ import { defineComponent, ref } from "vue"; import { useIpc } from "@/modules/ipc"; import { logoutRequest } from '@/modules/message'; + import { useProfile } from "@/modules/auth" export default defineComponent({ name: "Settings", @@ -37,7 +38,9 @@ setup() { const toggleSettings = ref(false); - const { post } = useIpc(); + const { post, invoke } = useIpc(); + + const { clearProfile } = useProfile(); // Listen for escape key to close settings. const onEscape = (e: any) => { @@ -54,9 +57,17 @@ } // We ask server to log us out. - const onLogout = () => { - console.log("Submitting logout request.") - post("client-message", logoutRequest()) + const onLogout = async () => { + + console.log("Submitting logout request."); + + try { + clearProfile(); + await invoke("user-logout", ""); + } catch(e) { + console.log('error') + } + } return { diff --git a/src/modules/auth.ts b/src/modules/auth.ts index bba8bdd..5fb40c1 100644 --- a/src/modules/auth.ts +++ b/src/modules/auth.ts @@ -1,34 +1,23 @@ -import { ref, Ref} from "vue"; +import { ref } from "vue"; +import { Profile } from "@/types"; -interface UserState { - email: string | null; - password?: string; - key: string | null; -} -const user:Ref = ref({ - email: null, - key: "testing" -}); +const profile = ref(); -const CRIMATA_KEY = "CRIMATA_KEY"; +export const useProfile = () => { -const raw = window.localStorage.getItem(CRIMATA_KEY); + const setProfile = (payload: Profile) => { + profile.value = payload; + } -if (raw) { - user.value = JSON.parse(raw); -} - -export const useAuth = () => { - - const setUser = (token: UserState) => { - window.localStorage.setItem(CRIMATA_KEY, JSON.stringify(token)); - user.value = token; + const clearProfile = () => { + profile.value = null; } return { - setUser, - user + setProfile, + clearProfile, + profile } } diff --git a/src/modules/http.ts b/src/modules/http.ts new file mode 100644 index 0000000..7ec423d --- /dev/null +++ b/src/modules/http.ts @@ -0,0 +1,52 @@ + +import axios, { AxiosRequestConfig } from 'axios'; + +const baseURL = 'http://127.0.0.1:3010/api'; + + +interface Request { + endpoint: string; + query?: Record; + config?: Record; +} + + +const makeQuery = (reqQuery: Record) => { + + let result = ''; + + result = '?' + Object.entries(reqQuery) + .map(([ key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + .join('&') + + return result; +}; + + +export const useHttp = () => { + + const api = axios.create({ + baseURL, + withCredentials: true, + }); + + + const post = async (endpoint: string, payload?: Record): Promise => ( + await api.post(endpoint, payload) + ) + + + const get = async (req: Request) => { + + if (req.query) { + req.endpoint += makeQuery(req.query); + } + + const res = await api.get(req.endpoint, req.config); + return res; + }; + + return { + get, post + } +} diff --git a/src/types.ts b/src/types.ts index 37b18ac..2e35fc5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,7 +19,7 @@ export interface ClientMessage { } export interface ClientRequest { - intent: string; + intent: string; params: object; epic: string | boolean; confidence: number; @@ -50,8 +50,10 @@ export interface Profile { } export interface AuthProtocol { - key: boolean | string; - profile: boolean | Profile; + token: null | string; + profile: null | Profile; + password?: string; + email?: string; } export interface LogoutRequest { @@ -71,4 +73,4 @@ export interface StandardMessage { }; context: string; modifier: string; -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 6d0785b..e79f19f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2355,6 +2355,13 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.0.tgz#96eaf83e38d32108b66d82a9cb0cfa24886cdfeb" + integrity sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -2370,6 +2377,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.3, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.1.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" + integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -2717,6 +2734,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomically@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe" + integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w== + autoprefixer@^9.8.6: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -3960,6 +3982,22 @@ condense-newlines@^0.2.1: is-whitespace "^0.3.0" kind-of "^3.0.2" +conf@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/conf/-/conf-10.0.1.tgz#038093e5cbddc0e59bc14f63382c4ce732a4781d" + integrity sha512-QClEoNcruwBL84QgMEPHibL3ERxWIrRKhbjJKG1VsFBadm5QpS0jsu4QjY/maxUvhyAKXeyrs+ws+lC6PajnEg== + dependencies: + ajv "^8.1.0" + ajv-formats "^2.0.2" + atomically "^1.7.0" + debounce-fn "^4.0.0" + dot-prop "^6.0.1" + env-paths "^2.2.1" + json-schema-typed "^7.0.3" + onetime "^5.1.2" + pkg-up "^3.1.0" + semver "^7.3.5" + config-chain@^1.1.11, config-chain@^1.1.12: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -4482,6 +4520,13 @@ deasync@^0.1.15: bindings "^1.5.0" node-addon-api "^1.7.1" +debounce-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7" + integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ== + dependencies: + mimic-fn "^3.0.0" + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4841,6 +4886,13 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" @@ -4999,6 +5051,14 @@ electron-publish@22.9.1: lazy-val "^1.0.4" mime "^2.4.6" +electron-store@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-8.0.0.tgz#81a4e687958e2dae1c5c84cc099a8148be776337" + integrity sha512-ZgRPUZkfrrjWSqxZeaxu7lEvmYf6tgl49dLMqxXGnEmliSiwv3u4rJPG+mH3fBQP9PBqgSh4TCuxHZImMMUgWg== + dependencies: + conf "^10.0.0" + type-fest "^1.0.2" + electron-to-chromium@^1.3.585: version "1.3.589" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.589.tgz#bd26183ed8697dde6ac19acbc16a3bf33b1f8220" @@ -5105,6 +5165,11 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -8062,6 +8127,16 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" + integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -8777,6 +8852,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -9841,6 +9921,13 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + please-upgrade-node@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" @@ -10784,6 +10871,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -11135,7 +11227,7 @@ semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^7.3.4: +semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -12407,6 +12499,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^1.0.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.1.3.tgz#ea1a602e98e5a968a56a289886a52f04c686fc81" + integrity sha512-CsiQeFMR1jZEq8R+H59qe+bBevnjoV5N2WZTTdlyqxeoODQOOepN2+msQOywcieDq5sBjabKzTn3U+sfHZlMdw== + type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" From 479bafdde59297d60f6d7ca70035eb9b746ac0bb Mon Sep 17 00:00:00 2001 From: Enrique Hernandez Date: Sun, 30 May 2021 11:06:10 -0400 Subject: [PATCH 4/4] add useIpc hook --- .gitignore | 1 + package.json | 1 + src/api/account.ts | 2 +- src/background/audio.ts | 32 +++++------------- src/background/init.ts | 29 ++++------------- src/background/ipc/account.ts | 13 +++++--- src/background/ipc/audio.ts | 39 ++++++++++++++++++++++ src/background/ipc/index.ts | 17 ++++++++++ src/background/ipc/session.ts | 61 +++++++++++++++++++++++++++++++++++ src/background/session.ts | 34 ++++++++----------- src/modules/http.ts | 2 +- yarn.lock | 5 +++ 12 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 src/background/ipc/audio.ts create mode 100644 src/background/ipc/session.ts diff --git a/.gitignore b/.gitignore index 46762a2..20128fc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ crash.log # local env files .env.local .env.*.local +.env # Log files npm-debug.log* diff --git a/package.json b/package.json index ec3ff75..04ce2ec 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "animejs": "^3.2.0", "axios": "^0.21.1", "core-js": "^3.6.5", + "dotenv": "^10.0.0", "electron-is-dev": "^2.0.0", "electron-store": "^8.0.0", "electron-updater": "^4.3.8", diff --git a/src/api/account.ts b/src/api/account.ts index de57837..0fd1497 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -23,7 +23,7 @@ export const logout = export const fetchProfile = async(email: string, token: string) => ( await axios({ - url: "http://127.0.0.1:3010/api/account/profile", + url: "http://127.0.0.1:3000/api/account/profile", headers: { Cookie: `jwt=${token}` }, diff --git a/src/background/audio.ts b/src/background/audio.ts index 0a98206..fa6efb2 100644 --- a/src/background/audio.ts +++ b/src/background/audio.ts @@ -2,9 +2,7 @@ "use strict"; -import { ipcMain } from "electron"; import { backgroundMitt } from '@/modules/emitter'; - const portAudio = require('naudiodon'); // Audio in and out stream objects. @@ -26,27 +24,22 @@ 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; -} +export const toggleRecord = (): void => { record = !record }; -// 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) => { +export const fetchAudioInput = (): Promise => ( + + new Promise((resolve, reject) => { try { resolve(audioContainer.input); - record = false; + toggleRecord(); } catch (e) { - reject() + reject(new Error('Failed to fetch the audio.')) } - }); -}; + }) +) // Main audio function run by run.ts module. @@ -86,15 +79,6 @@ export function initAudioIO(): void { ao.start(); } - - // Listen to record. - console.log("AUDIO:Adding recording listeners.") - - ipcMain.removeAllListeners("start-recording"); - ipcMain.on("start-recording", onRecordingStart); - - ipcMain.removeHandler("stop-recording"); - ipcMain.handle("stop-recording", onRecordingEnd); } diff --git a/src/background/init.ts b/src/background/init.ts index 21f9c45..0936e81 100644 --- a/src/background/init.ts +++ b/src/background/init.ts @@ -1,12 +1,12 @@ "use strict"; -import { app, dialog, ipcMain, IpcMainInvokeEvent } from "electron"; +import { app, dialog } from "electron"; import { createWindow } from './window'; -import { initSession, onAppMounted } from './session'; -import { initAudioIO, stopStream } from './audio'; +import { stopStream } from './audio'; import { backgroundMitt } from '@/modules/emitter'; -import { initAccountListener } from "@/background/ipc/account"; +import useIpc from "@/background/ipc/index"; +const { autoUpdater } = require('electron-updater'); let win: boolean; @@ -16,7 +16,6 @@ backgroundMitt.on('window-active', (state: boolean) => { }); // Auto updating. -const { autoUpdater } = require('electron-updater'); autoUpdater.requestHeaders = { 'PRIVATE-TOKEN': 'mvvgWYwWnot4bisiQMh_' } autoUpdater.on('update-available', (info: any) => { @@ -40,29 +39,13 @@ autoUpdater.on('update-downloaded', (info: any) => { }) -const onSessionInit = (_event: IpcMainInvokeEvent, cid: string) => { - // Instantiate socket session with crimata-platorm. - initSession(cid); - - // Begin audio stream. - initAudioIO(); -} - // Run when electron app is initialized. async function main(): Promise { console.log("MAIN:Initializing Electron App."); - // Attack browser window init listener. - ipcMain.removeAllListeners("app-mounted"); - ipcMain.on("app-mounted", onAppMounted); - - ipcMain.removeAllListeners("init-session"); - ipcMain.on("init-session", onSessionInit); - - // handler user auth, login, and logout asynchrounously - initAccountListener(); + useIpc(); // Must wait til window is created. await createWindow(); @@ -74,7 +57,7 @@ export function initApp(dev: boolean): void { // On initial startup. app.on("ready", () => { - autoUpdater.checkForUpdates() + // autoUpdater.checkForUpdates() main() }); diff --git a/src/background/ipc/account.ts b/src/background/ipc/account.ts index 932c2b2..e6e9795 100644 --- a/src/background/ipc/account.ts +++ b/src/background/ipc/account.ts @@ -3,7 +3,7 @@ import { Profile } from "@/types"; import { submit, fetchProfile, logout } from "@/api/account"; -import { ipcMain } from "electron"; +import { ipcMain, IpcMainInvokeEvent } from "electron"; import { store } from "@/background/store"; @@ -17,9 +17,10 @@ const parseAuthRes = (authRes: any) => { }; -const onProfile = async (_event: any, _payload: string) => ( +const onProfile = async (_event: IpcMainInvokeEvent, _payload: string) => ( new Promise(async (resolve, reject) => { + console.log('[IPC]: user-profile'); // get jwt token and crimataId from store const token = store.get('key'); @@ -43,9 +44,10 @@ const onProfile = async (_event: any, _payload: string) => ( ) -const onLogin = async (_event: any, payload: string) => ( +const onLogin = async (_event: IpcMainInvokeEvent, payload: string) => ( new Promise(async (resolve, reject) => { + console.log('[IPC]: user-login'); const account = JSON.parse(payload); @@ -69,9 +71,10 @@ const onLogin = async (_event: any, payload: string) => ( }) ) -const onLogout = async (_event: any, _payload: string) => ( +const onLogout = async (_event: IpcMainInvokeEvent, _payload: string) => ( new Promise(async (resolve, reject) => { + console.log('[IPC]: user-logout'); try { // post logout to backend @@ -91,7 +94,7 @@ const onLogout = async (_event: any, _payload: string) => ( ) -export const initAccountListener = () => { +export default function useAccountListeners(): void { ipcMain.removeHandler("user-profile"); ipcMain.handle("user-profile", onProfile); diff --git a/src/background/ipc/audio.ts b/src/background/ipc/audio.ts new file mode 100644 index 0000000..e6a63d0 --- /dev/null +++ b/src/background/ipc/audio.ts @@ -0,0 +1,39 @@ + +"use strict"; + +import { ipcMain, IpcMainInvokeEvent, IpcMainEvent } from "electron"; +import { fetchAudioInput, toggleRecord } from "@/background/audio"; + + +// Toggles record to true to begin capturing chunks. +const onRecordingStart = ( + _event: IpcMainEvent, + _payload: null): void => { + + console.log('[IPC]: start-recording'); + + toggleRecord(); +}; + + +// Returns recorded audio to frontend and sets record to false. +const onRecordingStop = async ( + _event: IpcMainInvokeEvent, + _payload: null +): Promise => { + + console.log('[IPC]: stop-recording'); + + return await fetchAudioInput() +}; + + +export default function useAudioListeners(): void { + + ipcMain.removeAllListeners("start-recording"); + ipcMain.on("start-recording", onRecordingStart); + + ipcMain.removeHandler("stop-recording"); + ipcMain.handle("stop-recording", onRecordingStop); + +}; diff --git a/src/background/ipc/index.ts b/src/background/ipc/index.ts index e69de29..933bda1 100644 --- a/src/background/ipc/index.ts +++ b/src/background/ipc/index.ts @@ -0,0 +1,17 @@ + +"use strict"; + +import useAccountListeners from "./account"; +import useSessionListeners from "./session"; +import useAudioListeners from "./audio"; + + +export default function useIpc(): void { + + useAccountListeners(); + + useSessionListeners(); + + useAudioListeners(); + +} diff --git a/src/background/ipc/session.ts b/src/background/ipc/session.ts new file mode 100644 index 0000000..3eaacbf --- /dev/null +++ b/src/background/ipc/session.ts @@ -0,0 +1,61 @@ + +"use strict"; + +import { initSession, emitNewMessages, sendMessage } from '@/background/session'; +import { ipcMain, IpcMainInvokeEvent, IpcMainEvent } from "electron"; +import { initAudioIO } from "@/background/audio"; + + +// Instantiate socket session with crimata-platorm. +const onSessionInit = ( + _event: IpcMainInvokeEvent, + cid: string +): void => { + + console.log('[IPC]: init-session'); + + initSession(cid); + + initAudioIO(); +} + + +const onAppMounted = ( + _event: IpcMainInvokeEvent, + _payload: any +): void => { + + console.log('[IPC]: app-mounted'); + + emitNewMessages() +}; + + +// Handle messages from window/client. +const onClientMessage = async ( + _event: IpcMainEvent, + payload: Record +): Promise => { + + console.log('[IPC]: client-message'); + + sendMessage(payload); +} + + +export default function useSessionListeners(): void { + + console.log('[IPC]: Init session listeners.'); + + // Attach listeners for frontend. + ipcMain.removeAllListeners("client-message"); + ipcMain.on("client-message", onClientMessage); + + // Attack browser window init listener. + ipcMain.removeAllListeners("app-mounted"); + ipcMain.on("app-mounted", onAppMounted); + + ipcMain.removeAllListeners("init-session"); + ipcMain.on("init-session", onSessionInit); + +} diff --git a/src/background/session.ts b/src/background/session.ts index a619f33..fc3e4fd 100644 --- a/src/background/session.ts +++ b/src/background/session.ts @@ -9,8 +9,7 @@ */ import { backgroundMitt } from "@/modules/emitter"; -import { ipcEmit, loadState, saveToJson } from './helpers'; -import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from "electron"; +import { ipcEmit, loadState } from './helpers'; import useWebSockets from "./websockets"; @@ -23,17 +22,9 @@ let win = true; // Info saved to json on quit (key, newMessages). let state: SessionState; -// Send state on new window. -export const onAppMounted = (_event: IpcMainInvokeEvent, _payload: any) => { - - ipcEmit("update-state", { - newMessages: state.newMessages, - }); - -} // Calls appropriate endpoint for a server message. -const onMessage = (data: string) => { +const onMessage = (data: string): void => { let message = JSON.parse(data); console.log('received new message', message); @@ -74,13 +65,18 @@ const onMessage = (data: string) => { // Websockets module. const { createSocket, send } = useWebSockets(onMessage); -// Handle messages from window/client. -const onClientMessage = async (_event: IpcMainEvent, payload: any): Promise => { - console.log("New client message") - if (payload.hasOwnProperty("key")) { - return +export const emitNewMessages = (): void => { + if (state) { + ipcEmit("update-state", { + newMessages: state.newMessages, + }); } + +} + + +export const sendMessage = (payload: Record): void => { try { send(payload) } catch(e) { @@ -91,7 +87,7 @@ const onClientMessage = async (_event: IpcMainEvent, payload: any): Promise { +export const initSession = (cid: string): void => { console.log("SESS:Creating new session.") // Load Json or createState. @@ -100,10 +96,6 @@ export const initSession = (cid: string) => { // Open socket connection. createSocket(); - // Attach listeners for frontend. - ipcMain.removeAllListeners("client-message"); - ipcMain.on("client-message", onClientMessage); - // Keep win up-to-date. backgroundMitt.on('window-active', (state: boolean) => { win = state; diff --git a/src/modules/http.ts b/src/modules/http.ts index 7ec423d..326b675 100644 --- a/src/modules/http.ts +++ b/src/modules/http.ts @@ -1,7 +1,7 @@ import axios, { AxiosRequestConfig } from 'axios'; -const baseURL = 'http://127.0.0.1:3010/api'; +const baseURL = 'http://127.0.0.1:3000/api'; interface Request { diff --git a/yarn.lock b/yarn.lock index e79f19f..465adc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4898,6 +4898,11 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + dotenv@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"