trying with the store and such

This commit is contained in:
talksik
2022-03-17 20:13:49 -04:00
parent 453121c987
commit 34fe8e0c29
21 changed files with 478 additions and 35758 deletions
-30636
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
MONGO_CONNECTION_STRING=mongodb+srv://default:M9iZXokJlZpN4KLX@cluster0.mkuqa.mongodb.net/default?retryWrites=true&w=majority
+6 -3
View File
@@ -21,12 +21,15 @@ export const authCheck = async (
audience:
"423533244953-banligobgbof8hg89i6cr1l7u0p7c2pk.apps.googleusercontent.com", // Specify the CLIENT_ID of the app that accesses the backend
});
const userid = ticket.getPayload()?.sub;
const userId = ticket.getPayload()?.sub;
console.log(userid);
// used in subsequent handlers
res.locals.userId = userId;
console.log(userId);
next();
} catch (error) {
res.status(401).send(error);
res.status(401).send("unauthorized");
}
};
+6 -3
View File
@@ -4,13 +4,16 @@
"main": "index.ts",
"license": "MIT",
"dependencies": {
"@nirvana/core": "*",
"cors": "^2.8.5",
"express": "^4.17.3",
"google-auth-library": "^7.14.0"
"google-auth-library": "^7.14.0",
"mongodb": "^4.4.1"
},
"scripts": {
"dev": "nodemon",
"start": "ts-node index.ts"
"dev": "NODE_ENV=development && nodemon",
"start": "ts-node index.ts",
"production": "NODE_ENV=production && nodemon"
},
"devDependencies": {
"@types/express": "^4.17.13",
+51 -2
View File
@@ -1,16 +1,65 @@
import { GoogleUserInfo, User } from "@nirvana/core/models";
import express, { Application, Request, Response } from "express";
import { ObjectId } from "mongodb";
import { authCheck } from "../middleware/auth";
import { collections } from "../services/database.service";
export default function getUserRoutes() {
const router = express.Router();
// get user details based on id from token
router.use(express.json());
// get user details based on id token
router.get("/", authCheck, getUserDetails);
//
router.post("/", createUser);
return router;
}
/**
* Use token from middleware and get user properties
* create user if doesn't exist
*/
async function getUserDetails(req: Request, res: Response) {
res.send({ message: "check" });
const userId: string = res.locals.userId;
try {
const query = { _id: new ObjectId(userId) };
// return user details if it passed auth middleware
const user = (await collections.users?.findOne(query)) as unknown as User;
res.status(200).send(user);
} catch (error) {
res
.status(404)
.send(`unable to find a matching document with id: ${userId}`);
}
}
async function createUser(req: Request, res: Response) {
try {
const { access_token } = req.query;
if (!access_token) {
res.status(400);
return;
}
// create user if not exists
// get user info from access token
const userInfo: GoogleUserInfo = await (
await fetch(
`https://www.googleapis.com/oauth2/v1/userinfo?access_token=${access_token}`
)
).json();
res.send();
} catch (error) {
res.status(500);
}
}
+26
View File
@@ -0,0 +1,26 @@
// External Dependencies
import * as mongoDB from "mongodb";
// import * as dotenv from "dotenv";
// Global Variables
export const collections: { users?: mongoDB.Collection } = {};
// Initialize Connection
export async function connectToDatabase() {
const client: mongoDB.MongoClient = new mongoDB.MongoClient(
process.env.DB_CONN_STRING!
);
await client.connect();
const db: mongoDB.Db = client.db(process.env.DB_NAME);
const usersCollection: mongoDB.Collection = db.collection("users");
collections.users = usersCollection;
console.log(
`Successfully connected to database: ${db.databaseName} and collection: ${usersCollection.collectionName}`
);
}
@@ -1,6 +1,8 @@
export class UserInfo {
import { ObjectId } from "mongodb";
export class GoogleUserInfo {
constructor(
public id: string,
public id: ObjectId,
public email: string,
public verifiedEmail: boolean,
public name: string,
+2 -1
View File
@@ -1 +1,2 @@
export * from "./userInfo.model";
export * from "./googleUserInfo.model";
export * from "./user.model";
+29
View File
@@ -0,0 +1,29 @@
import { GoogleUserInfo } from "./googleUserInfo.model";
import { ObjectId } from "mongodb";
export class User extends GoogleUserInfo {
constructor(
id: ObjectId,
email: string,
verifiedEmail: boolean,
name: string,
given_name: string,
family_name: string,
picture: string,
locale: string,
// additional properties specific to our users collection
mongoId: ObjectId
) {
super(
id,
email,
verifiedEmail,
name,
given_name,
family_name,
picture,
locale
);
}
}
+4 -1
View File
@@ -2,5 +2,8 @@
"name": "@nirvana/core",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
"license": "MIT",
"dependencies": {
"mongodb": "^4.4.1"
}
}
+7 -1
View File
@@ -53,7 +53,10 @@
{
"html": "./src/index.html",
"js": "./src/renderer.ts",
"name": "main_window"
"name": "main_window",
"preload": {
"js": "./src/electron/preload.ts"
}
}
]
}
@@ -90,8 +93,11 @@
"typescript": "~4.5.4"
},
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@getstation/electron-google-oauth2": "^2.1.0",
"@mui/icons-material": "^5.5.1",
"@mui/material": "^5.5.1",
"axios": "^0.26.1",
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.0.1",
@@ -1,10 +1,16 @@
import { Route, useNavigate } from "react-router-dom";
import { STORE_ITEMS } from "../../electron/store";
import { useEffect } from "react";
import { useGetUserDetails } from "../../controller/index";
export default function ProtectedRoute({ ...children }) {
const { isLoading, isError } = useGetUserDetails();
useEffect(() => {
console.log(window.electronAPI.store.get(STORE_ITEMS.AUTH_TOKENS));
}, []);
const navigate = useNavigate();
if (isLoading) return <span>please wait while we authenticate you</span>;
@@ -1,4 +1,6 @@
import axios, { AxiosRequestConfig } from "axios";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { User } from "@nirvana/core/models";
// export const localHost = process.env.REACT_APP_API_DOMAIN;
@@ -12,22 +14,22 @@ class NirvanaApi {
this._authToken = _googleIdToken;
}
async fetch(options: AxiosRequestConfig, privateRoute = false) {
async fetch<T>(options: AxiosRequestConfig, privateRoute = false) {
// use the auth token if it's a private route
// error if no auth token and it's a private route
// throw error and show message on anything that is an error from the backend
if (privateRoute && this._authToken) {
return await axios({
return await axios.request<T>({
...options,
headers: { Authorization: this._authToken },
});
}
return axios(options);
return axios.request<T>(options);
}
async getUserDetails() {
return await this.fetch(
async getUserDetails(): Promise<AxiosResponse<User>> {
return await this.fetch<User>(
{
method: "GET",
url: localHost + `/users`,
+8
View File
@@ -0,0 +1,8 @@
import { electronAPI } from "./preload";
export {};
declare global {
interface Window {
electronAPI: typeof electronAPI;
}
}
+5 -3
View File
@@ -1,8 +1,9 @@
import store, { STORE_ITEMS } from "./store";
import Channels from "./constants";
import ElectronGoogleOAuth2 from "@getstation/electron-google-oauth2";
import { browserWindow } from "../index";
import { ipcMain } from "electron";
import store from "./store";
const myApiOauth = new ElectronGoogleOAuth2(
"423533244953-banligobgbof8hg89i6cr1l7u0p7c2pk.apps.googleusercontent.com",
@@ -14,7 +15,7 @@ const myApiOauth = new ElectronGoogleOAuth2(
export async function handleLogin() {
// read saved refresh token if any
// todo: fix this...should be working
const refreshToken = await store.get("tokens");
const authTokens = await store.get(STORE_ITEMS.AUTH_TOKENS);
// todo: remove this when I have way of getting access token from a refresh token
// if (refreshToken) {
@@ -35,7 +36,8 @@ export async function handleLogin() {
// }
const tokens = await myApiOauth.openAuthWindowAndGetTokens();
store.set("tokens", tokens);
store.set(STORE_ITEMS.AUTH_TOKENS, tokens);
browserWindow.webContents.send(Channels.AUTH_TOKENS, tokens);
}
+29
View File
@@ -0,0 +1,29 @@
import { contextBridge, ipcRenderer } from "electron";
import Channels from "./constants";
const electronAPI = {
auth: {
initiateLogin() {
ipcRenderer.send(Channels.ACTIVATE_LOG_IN);
},
receiveTokens(callback: any) {
ipcRenderer.on(Channels.AUTH_TOKENS, callback);
},
},
batteryApi: {},
fileApi: {},
store: {
get(val: string) {
return ipcRenderer.sendSync("electron-store-get", val);
},
set(property: string, val: any) {
ipcRenderer.send("electron-store-set", property, val);
},
// Other method you want to add like has(), reset(), etc.
},
};
contextBridge.exposeInMainWorld("electronAPI", electronAPI);
export default electronAPI;
+5
View File
@@ -1,4 +1,9 @@
import Store from "electron-store";
import { ipcMain } from "electron";
const store = new Store();
export enum STORE_ITEMS {
AUTH_TOKENS = "AUTH_TOKENS",
}
export default store;
+12 -4
View File
@@ -2,11 +2,13 @@ import { BrowserWindow, app, ipcMain } from "electron";
import Channels from "./electron/constants";
import { handleLogin } from "./electron/handleLogin";
import store from "./electron/store";
// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: any;
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require("electron-squirrel-startup")) {
@@ -24,7 +26,7 @@ const createWindow = (): void => {
height: 600,
width: 800,
webPreferences: {
nodeIntegration: true,
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
},
});
@@ -42,12 +44,18 @@ app
.whenReady()
.then(createWindow)
.then(() => {
console.log("hahha");
// End of the file
ipcMain.on(Channels.ACTIVATE_LOG_IN, async (event, arg) => {
console.log("initiating log in");
await handleLogin();
});
// IPC listener
ipcMain.on("electron-store-get", async (event, val) => {
event.returnValue = store.get(val);
});
ipcMain.on("electron-store-set", async (event, key, val) => {
store.set(key, val);
});
});
// Quit when all windows are closed, except on macOS. There, it's common
@@ -7,6 +7,8 @@ import {
} from "@mui/icons-material";
import Logo, { LogoType } from "../../components/Logo";
import { useEffect } from "react";
export default function Home() {
return (
<div className="h-screen w-screen bg-slate-700">
+29 -39
View File
@@ -1,65 +1,55 @@
import { Button, CircularProgress } from "@mui/material";
import { useEffect, useState } from "react";
import Channels from "../../electron/constants";
import { CircularProgress } from "@mui/material";
import Logo from "../../components/Logo";
import { UserInfo } from "@nirvana/core/models";
import axios from "axios";
import { useGetUserDetails } from "../../controller";
import { useNavigate } from "react-router-dom";
export default function Login() {
const navigate = useNavigate();
const logIn = () => {
setIsLoading(true);
// send to main process
// ipcRenderer.send(Channels.ACTIVATE_LOG_IN);
window.electronAPI.auth.initiateLogin();
};
// useEffect(() => {
// window.API.receive(channels.AUTH_TOKENS, async (tokens: any) => {
// console.log(tokens);
useEffect(() => {
window.electronAPI.auth.receiveTokens(async (tokens: any) => {
console.log(tokens);
// const { access_token, id_token, refresh_token } = tokens;
const { access_token, id_token, refresh_token } = tokens;
// // get user info from access token
// const userInfo: UserInfo = await (
// await fetch(
// `https://www.googleapis.com/oauth2/v1/userinfo?access_token=${access_token}`
// )
// ).json();
// home should handle this user now that they have signed in with google
navigate("/home");
});
// console.log("user data: ");
// console.log(userInfo);
// todo: figure out how to clean up with the preload api
// return () => {
// window.electronAPI.removeAllListeners(Channels.AUTH_TOKENS);
// };
}, []);
// // if user is an existing user, then continue to next route
// axios({
// method: "GET",
// url: localHost + `/user`,
// headers: {
// Authorization:
// "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ2M2RiZTczYWFkODhjODU0ZGUwZDhkNmMwMTRjMzZkYzI1YzQyOTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MjM1MzMyNDQ5NTMtYmFubGlnb2JnYm9mOGhnODlpNmNyMWw3dTBwN2MycGsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MjM1MzMyNDQ5NTMtYmFubGlnb2JnYm9mOGhnODlpNmNyMWw3dTBwN2MycGsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM0NzA3ODY2OTAzNTMxMDkwODYiLCJlbWFpbCI6InBhdGVsLmFyanVuNTBAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJKRlBBOC15TGgxeGlGOW5acUZQU0FBIiwibmFtZSI6IkFyanVuIFBhdGVsIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdqbTI5U2QxV25tOE52WmJYa29zdmY2U29JRDZrQlA1T0hSTFZJT0JRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkFyanVuIiwiZmFtaWx5X25hbWUiOiJQYXRlbCIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNjQ3Mzc4MzgzLCJleHAiOjE2NDczODE5ODN9.llIYMEjq3Ye1UFxiKIsgtjnRho2Ad7dQjCSFVDDlsiH3AVq_MssoKTXMS0tuHpQAZAfyqbi1kRpp4Ax3mFJb44z2OIyzOTDzhcNB_L_3C8U3gRuSmUaVfBa7EdkobkxByee3ddvjekD7Y9dobtXs3IllldjewD9Eg9FZmEkuSN6yYVxxRLYNYTjk_BAA17QKKkPPpyanAHHXLwxpMFhQc34tuAYCYmmDQQijV8YVvU4r9ruPpysumVXlUmX3CuNg9P415Dq1lUHQxaf5hWf_6RxIsw9PNg1wZ8CuJGvmLFnGBRQACKgnAkI9E7-tu2tWe3glRhORZlQmgRCKqG5xoA",
// },
// }).then((res) => console.log(res));
// navigate("/home");
// // if user is not an existing user, then show create account page
// });
// // return () => {
// // ipcRenderer.removeAllListeners(channels.AUTH_TOKEN);
// // };
// }, []);
const [isLoading, setIsLoading] = useState<boolean>(false);
return (
<div className="container flex flex-col space-y-5 justify-center items-center h-screen bg-slate-900">
<Logo className="scale-50" />
<CircularProgress />
<Button onClick={logIn} variant="contained">
Sign In
</Button>
{isLoading ? (
<>
<CircularProgress />
<span className="text-white">Attempting to log you in</span>
</>
) : (
<button
onClick={logIn}
className="text-white p-3 rounded shadow border-white"
>
Sign In
</button>
)}
</div>
);
}
+238 -5057
View File
File diff suppressed because it is too large Load Diff