update sockets, account, auth
This commit is contained in:
parent
9e8641b4de
commit
d851db2fcc
20 changed files with 411 additions and 326 deletions
|
|
@ -1,79 +0,0 @@
|
|||
# Do not edit. File was generated by node-gyp's "configure" step
|
||||
{
|
||||
"target_defaults": {
|
||||
"cflags": [],
|
||||
"default_configuration": "Release",
|
||||
"defines": [],
|
||||
"include_dirs": [],
|
||||
"libraries": []
|
||||
},
|
||||
"variables": {
|
||||
"asan": 0,
|
||||
"build_v8_with_gn": "false",
|
||||
"coverage": "false",
|
||||
"dcheck_always_on": 0,
|
||||
"debug_nghttp2": "false",
|
||||
"debug_node": "false",
|
||||
"enable_lto": "false",
|
||||
"enable_pgo_generate": "false",
|
||||
"enable_pgo_use": "false",
|
||||
"error_on_warn": "false",
|
||||
"force_dynamic_crt": 0,
|
||||
"host_arch": "x64",
|
||||
"icu_data_in": "../../deps/icu-tmp/icudt67l.dat",
|
||||
"icu_endianness": "l",
|
||||
"icu_gyp_path": "tools/icu/icu-generic.gyp",
|
||||
"icu_path": "deps/icu-small",
|
||||
"icu_small": "false",
|
||||
"icu_ver_major": "67",
|
||||
"is_debug": 0,
|
||||
"llvm_version": "0.0",
|
||||
"napi_build_version": "6",
|
||||
"node_byteorder": "little",
|
||||
"node_debug_lib": "false",
|
||||
"node_enable_d8": "false",
|
||||
"node_install_npm": "true",
|
||||
"node_module_version": 83,
|
||||
"node_no_browser_globals": "false",
|
||||
"node_prefix": "/",
|
||||
"node_release_urlbase": "https://nodejs.org/download/release/",
|
||||
"node_shared": "false",
|
||||
"node_shared_brotli": "false",
|
||||
"node_shared_cares": "false",
|
||||
"node_shared_http_parser": "false",
|
||||
"node_shared_libuv": "false",
|
||||
"node_shared_nghttp2": "false",
|
||||
"node_shared_openssl": "false",
|
||||
"node_shared_zlib": "false",
|
||||
"node_tag": "",
|
||||
"node_target_type": "executable",
|
||||
"node_use_bundled_v8": "true",
|
||||
"node_use_dtrace": "true",
|
||||
"node_use_etw": "false",
|
||||
"node_use_node_code_cache": "true",
|
||||
"node_use_node_snapshot": "true",
|
||||
"node_use_openssl": "true",
|
||||
"node_use_v8_platform": "true",
|
||||
"node_with_ltcg": "false",
|
||||
"node_without_node_options": "false",
|
||||
"openssl_fips": "",
|
||||
"openssl_is_fips": "false",
|
||||
"shlib_suffix": "83.dylib",
|
||||
"target_arch": "x64",
|
||||
"v8_enable_31bit_smis_on_64bit_arch": 0,
|
||||
"v8_enable_gdbjit": 0,
|
||||
"v8_enable_i18n_support": 1,
|
||||
"v8_enable_inspector": 1,
|
||||
"v8_enable_pointer_compression": 0,
|
||||
"v8_no_strict_aliasing": 1,
|
||||
"v8_optimized_debug": 1,
|
||||
"v8_promise_internal_field_count": 1,
|
||||
"v8_random_seed": 0,
|
||||
"v8_trace_maps": 0,
|
||||
"v8_use_siphash": 1,
|
||||
"want_separate_host_toolset": 0,
|
||||
"xcode_version": "11.0",
|
||||
"nodedir": "/Users/Enrique/Library/Caches/node-gyp/14.4.0",
|
||||
"standalone_static_library": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"main": "background.js",
|
||||
"main": "init.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/speech": "^4.2.0",
|
||||
"@types/animejs": "^3.1.2",
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
"lintOnSave": false,
|
||||
"pluginOptions": {
|
||||
"electronBuilder": {
|
||||
"preload": "src/preload.ts",
|
||||
"preload": "src/renderer/preload.ts",
|
||||
"builderOptions": {
|
||||
"appId": "com.crimata.ElectronUpdaterApp",
|
||||
"artifactName": "${productName}-${version}.${ext}",
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@
|
|||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<!-- <h1>Hello World!</h1>
|
||||
We are using Node.js <span id="node-version"></span>, Chromium
|
||||
<span id="chrome-version"></span>, and Electron
|
||||
<span id="electron-version"></span>. -->
|
||||
<div id="app"></div>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
105
src/account.ts
Normal file
105
src/account.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
import { postAuth, postLogin, postLogout } from "@/api/account";
|
||||
import { endSession, launchSession } from "@/session";
|
||||
import { getToken, clearToken, setToken } from "@/composables/store";
|
||||
import { parseAuthRes } from "./auth";
|
||||
|
||||
export const accountAuth = async (): Promise<Error | AuthState> => {
|
||||
|
||||
/* attempt to get a login token from the store */
|
||||
const token = getToken();
|
||||
|
||||
/* try to login with it, returns platform secret and new token on success */
|
||||
if (token) {
|
||||
try {
|
||||
|
||||
const res = await postAuth(token);
|
||||
|
||||
const parsed = parseAuthRes(res);
|
||||
|
||||
setToken(parsed.token)
|
||||
|
||||
return {
|
||||
profile: parsed.profile,
|
||||
token: parsed.token
|
||||
};
|
||||
|
||||
} catch(e) {
|
||||
console.log('[ACCOUNT]', e);
|
||||
clearToken();
|
||||
throw(new Error('Failed to authenticate.'));
|
||||
|
||||
}
|
||||
} else {
|
||||
throw(new Error('Unable to authenticate.'));
|
||||
}
|
||||
};
|
||||
|
||||
export const accountLogin: IpcHandlerCallback<AccountCredentials, Profile> = async (payload) => {
|
||||
const account = payload as AccountCredentials;
|
||||
try {
|
||||
|
||||
// attempt login with email password
|
||||
const res = await postLogin(account.email, account.password);
|
||||
const parsed = parseAuthRes(res);
|
||||
|
||||
// save jwt token and profile
|
||||
setToken(parsed.token)
|
||||
|
||||
// launch session
|
||||
launchSession(parsed.token)
|
||||
|
||||
// return profile to renderer
|
||||
return parsed.profile;
|
||||
|
||||
} catch(e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// export const accountLogin = async (account: Account): Promise<Error | Profile> => {
|
||||
//
|
||||
// try {
|
||||
//
|
||||
// // attempt login with email password
|
||||
// const res = await postLogin(account.email, account.password);
|
||||
// const parsed = parseAuthRes(res);
|
||||
//
|
||||
// // save jwt token and profile
|
||||
// setToken(parsed.token)
|
||||
//
|
||||
// // launch session
|
||||
// launchSession(parsed.token)
|
||||
//
|
||||
// // return profile to renderer
|
||||
// return parsed.profile;
|
||||
//
|
||||
// } catch(e) {
|
||||
// console.log('[ACCOUNT]', e);
|
||||
// throw (new Error('Failed to authenticate'));
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
export const accountLogout = async (): Promise<Error | void> => {
|
||||
|
||||
try {
|
||||
// post logout to backend
|
||||
await postLogout();
|
||||
|
||||
// remove key and crimataId
|
||||
clearToken();
|
||||
|
||||
// kill crimata platform session
|
||||
endSession();
|
||||
|
||||
return;
|
||||
|
||||
} catch(e) {
|
||||
console.log('[ACCOUNT]', e);
|
||||
return (new Error('Failed to logout. Please try again.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,36 +1,31 @@
|
|||
|
||||
import { useHttp } from "@/composables/http";
|
||||
import axios from "axios";
|
||||
import {config} from "@/config";
|
||||
|
||||
const { post } = useHttp();
|
||||
|
||||
export const usrPwdAuth = async (email: string, password: string) => {
|
||||
|
||||
try {
|
||||
return await post('/account/login', { email, password })
|
||||
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const tokenAuth = async (cid: string, token: string) => {
|
||||
|
||||
try {
|
||||
return await axios({
|
||||
url: "http://127.0.0.1:3000/api/account/profile",
|
||||
headers: {
|
||||
Cookie: `jwt=${token}`
|
||||
},
|
||||
method: 'GET',
|
||||
data: {
|
||||
cid,
|
||||
}
|
||||
export const postAuth = async (token: string) => (
|
||||
await axios({
|
||||
url: config.BUSINESS_URL + config.BUSINESS_PREFIX + '/account/authenticate',
|
||||
headers: {
|
||||
Cookie: `jwt=${token}`
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
).data;
|
||||
|
||||
|
||||
export const postLogin = async (email: string, password: string) => (
|
||||
await post('/account/login', { email, password })
|
||||
).data;
|
||||
|
||||
|
||||
export const postLogout =
|
||||
async (): Promise<null | Error> => (await post('/account/logout'));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
13
src/auth.ts
Normal file
13
src/auth.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
export 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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import {config} from "@/config";
|
||||
|
||||
const preFix = '/api';
|
||||
|
||||
const baseURL = "http://127.0.0.1:3000" + preFix;
|
||||
|
||||
const baseURL = config.BUSINESS_URL + config.BUSINESS_PREFIX;
|
||||
|
||||
interface Request {
|
||||
endpoint: string;
|
||||
|
|
@ -12,7 +10,6 @@ interface Request {
|
|||
config?: Record<string, any>;
|
||||
}
|
||||
|
||||
|
||||
const makeQuery = (reqQuery: Record<string, any>) => {
|
||||
|
||||
let result = '';
|
||||
|
|
|
|||
50
src/composables/ipcHandler.ts
Normal file
50
src/composables/ipcHandler.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
import { ipcMain, IpcMainInvokeEvent } from "electron";
|
||||
|
||||
export class IpcHandler<InputType, ReturnType> implements IIpcHandler<InputType, ReturnType> {
|
||||
|
||||
readonly channel: string;
|
||||
|
||||
readonly _handlerCallback: IpcHandlerCallback<InputType, ReturnType>;
|
||||
|
||||
constructor(options: {
|
||||
channel: string;
|
||||
handlerCallback: IpcHandlerCallback<InputType, ReturnType>;
|
||||
}) {
|
||||
this.channel = options.channel;
|
||||
this._handlerCallback = options.handlerCallback;
|
||||
}
|
||||
|
||||
handle() {
|
||||
ipcMain.handle(this.channel, this._onInvoke);
|
||||
}
|
||||
|
||||
remove() {
|
||||
ipcMain.removeHandler(this.channel);
|
||||
}
|
||||
|
||||
private async _onInvoke(_e: IpcMainInvokeEvent, payload?: string | null): Promise<Error | ReturnType> {
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
||||
console.log(`[IPC]:${this.channel}`);
|
||||
|
||||
try {
|
||||
|
||||
const params = payload ? JSON.parse(payload) : null;
|
||||
|
||||
const res = await this._handlerCallback(params);
|
||||
|
||||
resolve(res as unknown as ReturnType);
|
||||
|
||||
} catch(e) {
|
||||
console.log(`[IPC]:${this.channel}`, e);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
|
||||
import {config} from "@/config";
|
||||
import fs from 'fs';
|
||||
|
||||
export const saveToJson = (fileName: string, data: any) => {
|
||||
|
||||
fs.writeFile(configPath + fileName, JSON.stringify(data), (err) => {
|
||||
fs.writeFile(config.configPath + fileName, JSON.stringify(data), (err) => {
|
||||
if (err) {
|
||||
console.log("Error when saving to json.")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/composables/store.ts
Normal file
19
src/composables/store.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
const Store = require('electron-store');
|
||||
|
||||
const schema = {
|
||||
key: {
|
||||
type: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
const store = new Store({
|
||||
schema,
|
||||
encryptionKey: "super user test"
|
||||
});
|
||||
|
||||
export const getToken = (): string | undefined => (store.get("token"));
|
||||
|
||||
export const clearToken = (): void => (store.delete("token"));
|
||||
|
||||
export const setToken = (token: string): void => (store.set('token', token));
|
||||
|
||||
|
|
@ -3,9 +3,18 @@
|
|||
|
||||
import WebSocket from 'ws';
|
||||
|
||||
export default function useWebSockets(onMessageCallback: (s: string) => void) {
|
||||
|
||||
let socket: WebSocket | null = null;
|
||||
const _connectionCheckTimeout = 4000;
|
||||
const _reconnectTimeout = 1000;
|
||||
let _connectionCheckInterval: ReturnType<typeof setTimeout>;
|
||||
|
||||
|
||||
export default function useWebSockets(
|
||||
messageCallback: (message: string) => void,
|
||||
connectionStatusCallback: (alive: boolean) => void,
|
||||
) {
|
||||
|
||||
let socket: WebSocket;
|
||||
|
||||
const send = async (data: Record<string, any>): Promise<boolean> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -21,30 +30,52 @@ export default function useWebSockets(onMessageCallback: (s: string) => void) {
|
|||
|
||||
const connect = (socketUrl: string, secret: string) => {
|
||||
|
||||
// avoid setting multiple interval;
|
||||
if (_connectionCheckInterval) clearInterval(_connectionCheckInterval);
|
||||
|
||||
/* create a new socket */
|
||||
socket = new WebSocket(socketUrl)
|
||||
socket = new WebSocket(socketUrl);
|
||||
|
||||
/* add event listeners */
|
||||
|
||||
socket.on("open", () => {
|
||||
if (socket)
|
||||
|
||||
socket.send(secret);
|
||||
|
||||
// ping server
|
||||
_connectionCheckInterval = setInterval(() => {
|
||||
|
||||
socket.ping(null, true, (e: Error) => {
|
||||
if (e) {
|
||||
socket.close();
|
||||
connectionStatusCallback(false);
|
||||
setTimeout(() => connect(socketUrl, secret), _reconnectTimeout);
|
||||
}
|
||||
});
|
||||
|
||||
}, _connectionCheckTimeout);
|
||||
|
||||
});
|
||||
|
||||
socket.on("message", (event: WebSocket.MessageEvent) => {
|
||||
onMessageCallback(event.data.toString())
|
||||
messageCallback(event.data.toString())
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
return
|
||||
socket.on("close", (event: WebSocket.CloseEvent) => {
|
||||
connectionStatusCallback(false);
|
||||
clearInterval(_connectionCheckInterval);
|
||||
if (!event.wasClean) {
|
||||
setTimeout(() => connect(socketUrl, secret), _reconnectTimeout);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
socket.on("pong", () => connectionStatusCallback(true));
|
||||
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
if (socket) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
17
src/config.ts
Normal file
17
src/config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
import { app } from "electron";
|
||||
|
||||
const env = process.env;
|
||||
|
||||
const PLATFORM_PORT = env.PLATFORM_PORT || 8760;
|
||||
const PLATFORM_IP = env.PLATFORM_IP || 'http://127.0.0.1';
|
||||
|
||||
const BUSINESS_PORT = env.BUSINESS_PORT || 3010;
|
||||
const BUSINESS_IP = env.BUSINESS_IP || 'http://127.0.0.1';
|
||||
|
||||
export const config = {
|
||||
PLATFORM_URL: `${PLATFORM_IP}:${PLATFORM_PORT}`,
|
||||
BUSINESS_URL: `${BUSINESS_IP}:${BUSINESS_PORT}`,
|
||||
BUSINESS_PREFIX: '/api',
|
||||
configPath: app.getPath('userData')
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ import { app, protocol } from "electron";
|
|||
import createWindow from "./window";
|
||||
import main from "./main";
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
console.log('Starting Crimata electron app.');
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
|
|
@ -43,4 +41,4 @@ if (isDev) {
|
|||
process.on("SIGTERM", () => {
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,126 +1,32 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import { submit, fetchProfile, logout } from "../api/account";
|
||||
import { ipcMain, IpcMainInvokeEvent } from "electron";
|
||||
import { store } from "@/composables/store";
|
||||
import { accountLogin, accountLogout } from "@/account";
|
||||
import {IpcHandler} from "@/composables/ipcHandler";
|
||||
|
||||
const LOGIN_CHANNEL = "account-login";
|
||||
const LOGOUT_CHANNEL = "account-logout";
|
||||
|
||||
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 loginHandler = new IpcHandler({
|
||||
channel: LOGIN_CHANNEL,
|
||||
handlerCallback: accountLogin
|
||||
});
|
||||
|
||||
const logoutHandler = new IpcHandler({
|
||||
channel: LOGOUT_CHANNEL,
|
||||
handlerCallback: accountLogout
|
||||
});
|
||||
|
||||
const handlers = [loginHandler, logoutHandler];
|
||||
|
||||
export default handlers;
|
||||
|
||||
|
||||
/**
|
||||
* Get user profile from store and try to login with it.
|
||||
*/
|
||||
const onTokenLogin = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): Promise<Profile | Error> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-profile');
|
||||
|
||||
// get jwt token and crimataId from store
|
||||
const token = store.get('key');
|
||||
const crimataId = store.get('crimataId');
|
||||
|
||||
// authenticate and fetch profile
|
||||
try {
|
||||
|
||||
// attempt login with email token
|
||||
const res = await fetchProfile(crimataId, token);
|
||||
const parsed = parseAuthRes(res);
|
||||
|
||||
// return profile to renderer
|
||||
resolve(parsed.profile);
|
||||
|
||||
} catch(e) {
|
||||
reject(new Error('Failed to fetch profile.'));
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
const onLogin = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
payload: string
|
||||
): Promise<Profile | Error> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-login');
|
||||
|
||||
const account = JSON.parse(payload);
|
||||
|
||||
if ( account.password && account.email ) {
|
||||
try {
|
||||
|
||||
// attempt login with email password
|
||||
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);
|
||||
|
||||
// init session
|
||||
|
||||
// return profile to renderer
|
||||
resolve(parsed.profile);
|
||||
|
||||
} catch(e) {
|
||||
console.log('[API]', e);
|
||||
reject(new Error('Failed to authenticate'));
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const onLogout = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
_payload: null
|
||||
): Promise<void> => (
|
||||
|
||||
new Promise(async (resolve, reject) => {
|
||||
console.log('[IPC]: user-logout');
|
||||
|
||||
try {
|
||||
// post logout to backend
|
||||
await logout();
|
||||
|
||||
// remove key and crimataId
|
||||
store.delete('key');
|
||||
store.delete('crimataId');
|
||||
|
||||
// TODO: kill crimata platform session
|
||||
// endSession();
|
||||
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(new Error('Failed to logout. Please try again.'));
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
export default function useAccountListeners(): void {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
// export default function useAccountListeners(): void {
|
||||
//
|
||||
// ipcMain.removeHandler(LOGIN_HANDLER);
|
||||
// ipcMain.handle(LOGIN_HANDLER, onLogin);
|
||||
//
|
||||
// ipcMain.removeHandler(LOGOUT_HANDLER);
|
||||
// ipcMain.handle(LOGOUT_HANDLER, onLogout);
|
||||
//
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,17 +1,36 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import useAccountListeners from "./account";
|
||||
import { IpcHandler } from "@/composables/ipcHandler";
|
||||
import handlers from "./account";
|
||||
import useSessionListeners from "./session";
|
||||
// import useAudioListeners from "./audio";
|
||||
|
||||
interface IPCHandlers {
|
||||
[channel: string]: IpcHandler<any, any>;
|
||||
}
|
||||
|
||||
const ipcHandlers: IPCHandlers = {};
|
||||
|
||||
const _initHandlers = () => {
|
||||
handlers.forEach((h) => {
|
||||
if (!(h.channel in ipcHandlers)) {
|
||||
ipcHandlers[h.channel] = h;
|
||||
h.handle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function useIpc(): void {
|
||||
|
||||
useAccountListeners();
|
||||
_initHandlers();
|
||||
|
||||
// useAccountListeners();
|
||||
|
||||
useSessionListeners();
|
||||
|
||||
// useAudioListeners();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ function onSendMessage(_event: IpcMainEvent, payload: Message): void {
|
|||
}
|
||||
|
||||
// Login attempt, returns success or not.
|
||||
function onLogin(_event: IpcMainEvent, payload: LoginPayload) => {
|
||||
authenticate(payload.email, payload.password);
|
||||
function onLogin(_event: IpcMainEvent, payload: LoginPayload): void {
|
||||
console.log('hello')
|
||||
}
|
||||
|
||||
export default function useSessionListeners(): void {
|
||||
ipcMain.removeAllListeners("client-message");
|
||||
ipcMain.on("client-message", onSendMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
60
src/main.ts
60
src/main.ts
|
|
@ -1,41 +1,21 @@
|
|||
/**
|
||||
* Where the background logic really begins, gets called by app.onReady().
|
||||
*
|
||||
*
|
||||
* Handles authentication. If profile is set, we launch a session, which consis
|
||||
* of opening a connection with the platform, initializing the audio streams.
|
||||
*
|
||||
*
|
||||
* The session is primarily an interface between the frontend and the platform,
|
||||
* relaying messages from one to the other.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import { tokenAuth, usrPwdAuth } from "@/api/account";
|
||||
import { launchSession, endSession } from "@/session";
|
||||
import useIpc from "@/ipc/index";
|
||||
import store from "@/composables/store";
|
||||
import { accountAuth } from "./account";
|
||||
import { launchSession } from "./session";
|
||||
import ipcEmit from "./composables/emitter";
|
||||
import createWindow from "./window";
|
||||
|
||||
/* authenticate the user */
|
||||
export async function authenticate(email: string, password: string) {
|
||||
|
||||
/* attempt normal login */
|
||||
const platformKey, token, crimataId = await usrPwdAuth(email, password);
|
||||
|
||||
/* launch if successful */
|
||||
if (platformKey)
|
||||
launchSession(platformKey, crimataId);
|
||||
|
||||
/* save the token */
|
||||
store.set("token", token);
|
||||
|
||||
}
|
||||
|
||||
/* logout the user, end the session */
|
||||
export function deauthenticate() {
|
||||
|
||||
/* terminate the session */
|
||||
endSession();
|
||||
|
||||
}
|
||||
let authState: AuthState | null;
|
||||
|
||||
export default async function main() {
|
||||
|
||||
|
|
@ -45,18 +25,14 @@ export default async function main() {
|
|||
/* launch browser window */
|
||||
await createWindow();
|
||||
|
||||
/* attempt to get a login token from the store */
|
||||
const token = store.get("token");
|
||||
try {
|
||||
authState = await accountAuth() as AuthState;
|
||||
} catch(e) {
|
||||
console.log('AUTH:', e);
|
||||
authState = null;
|
||||
} finally {
|
||||
if (authState) launchSession(authState.token as string);
|
||||
ipcEmit("set-profile", authState?.profile);
|
||||
}
|
||||
|
||||
/* try to login with it, returns platform secret and new token on success */
|
||||
if (token)
|
||||
const newToken, profile = await tokenAuth(token);
|
||||
|
||||
/* if secret, we launch a session */
|
||||
if (newToken)
|
||||
launchSession(newToken, profile);
|
||||
|
||||
/* finally, save the most recent token */
|
||||
store.set("token", newToken);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,34 @@
|
|||
|
||||
|
||||
|
||||
import useAudio from "@/audio";
|
||||
import Canvas from "@/composables/convas";
|
||||
// import useAudio from "@/audio";
|
||||
import ipcEmit from "@/composables/emitter";
|
||||
import useWebsockets from "./composables/websockets";
|
||||
import {config} from "@/config";
|
||||
|
||||
/* data structure of messages that's tied to the UI */
|
||||
let msgrState: UIState | null = null;
|
||||
const uiState: any | null = null;
|
||||
|
||||
/* start and stop audio functionality */
|
||||
const { initAudio, closeAudio } = useAudio();
|
||||
// const { initAudio, closeAudio } = useAudio();
|
||||
|
||||
let isInitMessage: any;
|
||||
|
||||
let deauthenticate: any;
|
||||
let isAddMessage: any
|
||||
|
||||
/**
|
||||
* Controls for interfacing with the platform.
|
||||
* Takes an onMessage callback which we define below.
|
||||
*/
|
||||
const { connect, send, close } = useWebsockets((content: any) => {
|
||||
|
||||
|
||||
const onMessageCallback = (message: string) => {
|
||||
|
||||
const content = JSON.parse(message);
|
||||
|
||||
/* if the platform fails to authenticate, we must back down */
|
||||
if (content === "auth_error") {
|
||||
if (content === "CLOSE_AUTH_FAIL") {
|
||||
deauthenticate();
|
||||
return;
|
||||
}
|
||||
|
|
@ -28,8 +38,8 @@ const { connect, send, close } = useWebsockets((content: any) => {
|
|||
if (isInitMessage(content)) {
|
||||
uiState.set(content);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
else if (isAddMessage(content)) {
|
||||
uiState.add(content);
|
||||
}
|
||||
|
|
@ -37,10 +47,15 @@ const { connect, send, close } = useWebsockets((content: any) => {
|
|||
else {
|
||||
uiState.update(content);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
const onConnectionStatusCallback = (alive: boolean) => {
|
||||
ipcEmit('connection-state', alive);
|
||||
}
|
||||
|
||||
/* send a message to the platform */
|
||||
const { connect, send, close } = useWebsockets(onMessageCallback, onConnectionStatusCallback);
|
||||
|
||||
/* send a message to the platform */
|
||||
export function sendMessage(message: Message) {
|
||||
|
||||
/* socket send */
|
||||
|
|
@ -48,24 +63,22 @@ export function sendMessage(message: Message) {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/* launch a new session (the main process for authenticated users) */
|
||||
export function launchSession(platformKey: string, crimata_id: string) {
|
||||
export function launchSession(platformKey: string) {
|
||||
|
||||
/* connect to the platform */
|
||||
connect(PLATFORM_URL, platformKey);
|
||||
connect(config.PLATFORM_URL, platformKey);
|
||||
|
||||
/* initialize the audio streams */
|
||||
initAudio();
|
||||
|
||||
/* push profile to window */
|
||||
if (win)
|
||||
ipcEmit("update-auth", crimata_id);
|
||||
// initAudio();
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function endSession() {
|
||||
|
||||
closeAudio();
|
||||
// closeAudio();
|
||||
|
||||
close();
|
||||
|
||||
|
|
|
|||
23
src/types.ts
23
src/types.ts
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
interface Message {
|
||||
text: boolean | string;
|
||||
context: boolean | string;
|
||||
|
|
@ -28,3 +29,25 @@ interface LoginPayload {
|
|||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
profile: Profile | null;
|
||||
token: string | null;
|
||||
}
|
||||
|
||||
interface AccountCredentials {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface IpcHandlerCallback<I, O> {
|
||||
(payload: I | null): Promise<Error | O>;
|
||||
}
|
||||
|
||||
interface IIpcHandler<I, O> {
|
||||
handle(): void;
|
||||
remove(): void;
|
||||
readonly _handlerCallback: IpcHandlerCallback<I, O>;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
import { BrowserWindow, ipcMain } from "electron";
|
||||
import { BrowserWindow, ipcMain, app } from "electron";
|
||||
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
||||
import { backgroundMitt } from './coposables/emitter';
|
||||
import { saveToJson } from "./coposables/json";
|
||||
import { backgroundMitt } from './composables/emitter';
|
||||
import { saveToJson } from "./composables/json";
|
||||
import * as path from "path";
|
||||
import fs from 'fs';
|
||||
import { config } from "@/config";
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
|
||||
interface IpcRendererPayload {
|
||||
|
|
@ -19,8 +21,8 @@ const loadWinState = (fileName: string): WindowState => {
|
|||
let state: WindowState;
|
||||
|
||||
try {
|
||||
state = JSON.parse(fs.readFileSync(configPath + fileName).toString());
|
||||
}
|
||||
state = JSON.parse(fs.readFileSync(config.configPath + fileName).toString());
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
state = {
|
||||
|
|
@ -32,8 +34,8 @@ const loadWinState = (fileName: string): WindowState => {
|
|||
}
|
||||
|
||||
return state
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Called when a NavBar button is pressed.
|
||||
const onNavBar = (_event: any, action: string): void => {
|
||||
|
|
@ -110,8 +112,8 @@ export default async function createWindow(): Promise<void> {
|
|||
win = new BrowserWindow({
|
||||
width: winState.width,
|
||||
height: winState.height,
|
||||
x: winState.x,
|
||||
y: winState.y,
|
||||
x: winState.x as number,
|
||||
y: winState.y as number,
|
||||
resizable: true,
|
||||
backgroundColor: '#EBEBEB',
|
||||
frame: false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue