add electron store
This commit is contained in:
parent
ea3fc85371
commit
cc2babdadc
15 changed files with 391 additions and 135 deletions
41
src/App.vue
41
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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
36
src/api/account.ts
Normal file
36
src/api/account.ts
Normal file
|
|
@ -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<null | Error> => (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,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
|
@ -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<Error | Profile> => (
|
||||
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<void> {
|
||||
|
||||
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();
|
||||
|
||||
|
|
|
|||
105
src/background/ipc/account.ts
Normal file
105
src/background/ipc/account.ts
Normal file
|
|
@ -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);
|
||||
|
||||
}
|
||||
0
src/background/ipc/index.ts
Normal file
0
src/background/ipc/index.ts
Normal file
|
|
@ -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<void> => {
|
||||
const onClientMessage = async (_event: IpcMainEvent, payload: any): Promise<void> => {
|
||||
console.log("New client message")
|
||||
|
||||
if (payload.hasOwnProperty("key")) {
|
||||
|
|
@ -128,7 +91,7 @@ const onClientMessage = async (_event: IpcMainEvent, payload: any):Promise<void>
|
|||
|
||||
|
||||
// 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);
|
||||
|
|
|
|||
16
src/background/store.ts
Normal file
16
src/background/store.ts
Normal file
|
|
@ -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"
|
||||
});
|
||||
|
|
@ -27,7 +27,7 @@ export default function useWebSockets(
|
|||
|
||||
}
|
||||
|
||||
const send = async (data: Record<string, any>):Promise<boolean> => (
|
||||
const send = async (data: Record<string, any>): Promise<boolean> => (
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.readyState !== 1) {
|
||||
reject(false);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<!-- submit button; position: fixed -->
|
||||
<button class="submitButton button" type="submit">Submit</button>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<!-- back to login button: position: fixed -->
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<UserState> = 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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
52
src/modules/http.ts
Normal file
52
src/modules/http.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
|
||||
const baseURL = 'http://127.0.0.1:3010/api';
|
||||
|
||||
|
||||
interface Request {
|
||||
endpoint: string;
|
||||
query?: Record<string, any>;
|
||||
config?: Record<string, any>;
|
||||
}
|
||||
|
||||
|
||||
const makeQuery = (reqQuery: Record<string, any>) => {
|
||||
|
||||
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<string, any>): Promise<any> => (
|
||||
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
|
||||
}
|
||||
}
|
||||
10
src/types.ts
10
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue