Skip to content

Commit

Permalink
Merge pull request #39 from TritonSE/feature/christenjack/auth
Browse files Browse the repository at this point in the history
Feature/christenjack/auth
  • Loading branch information
jennymar authored Jun 1, 2024
2 parents c740d98 + 857c478 commit 2b4d1c4
Show file tree
Hide file tree
Showing 28 changed files with 920 additions and 318 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ jobs:
npm ci --legacy-peer-deps
npm run lint-check
npm run build
env:
NEXT_PUBLIC_BACKEND_URL: ${{ vars.NEXT_PUBLIC_BACKEND_URL }}
NEXT_PUBLIC_FIREBASE_SETTINGS: ${{ vars.NEXT_PUBLIC_FIREBASE_SETTINGS }}
NEXT_PUBLIC_PAYPAL_CLIENT_SECRET: ${{ vars.NEXT_PUBLIC_PAYPAL_CLIENT_SECRET }}
NEXT_PUBLIC_PAYPAL_CLIENT_ID: ${{ vars.NEXT_PUBLIC_PAYPAL_CLIENT_ID }}
8 changes: 4 additions & 4 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/nodemailer": "^6.4.14",
"@types/nodemailer": "^6.4.15",
"@typescript-eslint/eslint-plugin": "^6.18.0",
"@typescript-eslint/parser": "^6.18.0",
"eslint": "^8.56.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/util/validateEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ export default cleanEnv(process.env, {
EMAIL_NOTIFICATIONS_RECIPIENT: email(), // Recipient of VSR notification emails
BACKEND_FIREBASE_SETTINGS: json(), // Firebase settings for backend, stored as a JSON string
SERVICE_ACCOUNT_KEY: json(), // Private service account key for backend, stored as a JSON string
PAYPAL_CLIENT_ID: str(), // Client ID credential for PayPal account
PAYPAL_CLIENT_SECRET: str(), // Client secret credential for PayPal account
});
586 changes: 308 additions & 278 deletions frontend/package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"@mui/icons-material": "^5.15.12",
"@mui/material": "^5.15.12",
"@mui/x-data-grid": "^7.1.1",
"@paypal/react-paypal-js": "^8.2.0",
"envalid": "^8.0.0",
"firebase": "^10.11.0",
"@paypal/react-paypal-js": "^8.2.0",
"html2canvas": "^1.4.1",
"html2pdf.js": "^0.9.3",
"jspdf": "^2.5.1",
Expand All @@ -30,6 +30,7 @@
"nodemailer": "^6.9.11",
"react": "^18",
"react-dom": "^18",
"react-firebase-hooks": "^5.1.1",
"react-material-symbols": "^4.3.1"
},
"devDependencies": {
Expand Down
Binary file added frontend/public/Checker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions frontend/public/admin/loginLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontend/src/app/(web app)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Footer from "@/components/Footer";
import HeaderBar from "@/components/HeaderBar";
import HeaderBarSpace from "@/components/HeaderBarSpace";
import "./globals.css";
import "../globals.css";
import "react-material-symbols/rounded";

export default function Layout({ children }: { children: React.ReactNode }) {
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/app/(web app)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use client";
import React, { useEffect, useState } from "react";

import "../globals.css";
import { getPageText } from "../../api/pageeditor";

import "./globals.css";
import BackgroundHeader from "../../components/BackgroundHeader";

import styles from "./page.module.css";
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/app/admin/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Admin Dashboard landing page
"use client";

import styles from "./page.module.css";

import DashboardCard from "@/components/DashboardCard";
Expand All @@ -19,14 +20,12 @@ export default function Dashboard() {
title="Page Editor"
last_updated="Month XX, XXXX, XX:XX"
/>

<DashboardCard
imageURI="/dashboard_newsletter.png"
url="/newsletter-creator"
title="Newsletter"
last_updated="Month XX, XXXX, XX:XX"
/>

<DashboardCard
imageURI="/dashboard_mailinglist.png"
url="/mailing-list"
Expand Down
63 changes: 55 additions & 8 deletions frontend/src/app/admin/firebase/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
import { FirebaseOptions, initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { FirebaseApp, FirebaseOptions, initializeApp } from "firebase/app";
import { Auth, getAuth, signInWithEmailAndPassword, signOut } from "firebase/auth";
import { FirebaseStorage, getStorage } from "firebase/storage";

import env from "@/app/admin/util/validateEnv";
import env from "../util/validateEnv";

export const initFirebase = () => {
export type FirebaseServices = { auth: Auth; storage: FirebaseStorage };

/**
* Initialize Firebase with the Firebase config
* @returns FirebaseServices array instance
*/
export const initFirebase = (): FirebaseServices => {
if (!env.NEXT_PUBLIC_FIREBASE_SETTINGS) {
throw new Error("Cannot get firebase settings");
throw new Error("Cannot get firebase settings. " + process.env.NEXT_PUBLIC_FIREBASE_SETTINGS);
}

const firebaseConfig = env.NEXT_PUBLIC_FIREBASE_SETTINGS as FirebaseOptions;

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const app: FirebaseApp = initializeApp(firebaseConfig);
const auth: Auth = getAuth(app);
const storage: FirebaseStorage = getStorage(app);

return { auth, storage };
};

/**
* Uses Firebase to login with email and password
* @param auth Firebase Auth instance. In components get with useFirebase()
* @param email email address
* @param password password
* @returns boolean true if sign in successful, false otherwise
*/
export const firebaseSignIn = async (auth: Auth, email: string, password: string) => {
try {
await signInWithEmailAndPassword(auth, email, password);
return true;
} catch (error) {
return false;
}
};

return { app, auth };
/**
* Sign out using Firebase and delete client data holding the JWT
* @param auth Firebase Auth instance. In components get with useFirebase()
*/
export const firebaseSignOut = async (auth: Auth) => {
try {
await signOut(auth);
} catch (error) {
alert(error);
}
};

/**
* Get's the firebase config for use with reactfire components
*
*/
export const getFirebaseConfig = (): FirebaseOptions => {
if (!env.NEXT_PUBLIC_FIREBASE_SETTINGS) {
throw new Error("Cannot get firebase settings");
}
return env.NEXT_PUBLIC_FIREBASE_SETTINGS as FirebaseOptions;
};
27 changes: 27 additions & 0 deletions frontend/src/app/admin/firebase/firebaseProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { Auth } from "firebase/auth";
import { FirebaseStorage } from "firebase/storage";
import React, { FC, ReactNode, createContext, useContext } from "react";

import { FirebaseServices, initFirebase } from "./firebase";

const AuthContext = createContext<Auth | undefined>(undefined);
const StorageContext = createContext<FirebaseStorage | undefined>(undefined);

let firebaseServices: FirebaseServices | undefined;

export const FirebaseProvider: FC<{ children: ReactNode }> = ({ children }) => {
if (firebaseServices === undefined) {
firebaseServices = initFirebase();
}

return (
<AuthContext.Provider value={firebaseServices.auth}>
<StorageContext.Provider value={firebaseServices.storage}>{children}</StorageContext.Provider>
</AuthContext.Provider>
);
};

export const useAuth = () => useContext(AuthContext);
export const useStorage = () => useContext(StorageContext);
30 changes: 24 additions & 6 deletions frontend/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
"use client";

import { usePathname } from "next/navigation";

import { FirebaseProvider } from "./firebase/firebaseProvider";

import "../globals.css";
import HeaderBarSpace from "@/components/HeaderBarSpace";
import NavigationBar from "@/components/NavigationBar";
import "./globals.css";
import PrivatePage from "@/components/admin/PrivatePage";

export default function AdminLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const isLoginPage = pathname === "/admin";

return (
<section>
<NavigationBar />
<HeaderBarSpace />
{children}
</section>
<FirebaseProvider>
{!isLoginPage ? (
<PrivatePage>
<section>
<NavigationBar />
<HeaderBarSpace />
{children}
</section>
</PrivatePage>
) : (
children
)}
</FirebaseProvider>
);
}
1 change: 1 addition & 0 deletions frontend/src/app/admin/page.module.css
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/* Styles for admin login screen */
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Roboto+Slab:wght@100..900&display=swap");
4 changes: 3 additions & 1 deletion frontend/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import React from "react";

import LoginSection from "@/components/admin/LoginSection";

export default function Login() {
return <div>Login</div>;
return <LoginSection />;
}
8 changes: 6 additions & 2 deletions frontend/src/app/admin/util/validateEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { cleanEnv } from "envalid";
import { json, str } from "envalid/dist/validators";
import { str, json } from "envalid/dist/validators";

/**
* NextJS only allows the frontend to access environment variables if they start with
Expand All @@ -14,9 +14,13 @@ export default cleanEnv(
{
NEXT_PUBLIC_BACKEND_URL: process.env.NEXT_PUBLIC_BACKEND_URL,
NEXT_PUBLIC_FIREBASE_SETTINGS: process.env.NEXT_PUBLIC_FIREBASE_SETTINGS,
NEXT_PUBLIC_PAYPAL_CLIENT_SECRET: process.env.NEXT_PUBLIC_PAYPAL_CLIENT_SECRET,
NEXT_PUBLIC_PAYPAL_CLIENT_ID: process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID,
},
{
NEXT_PUBLIC_BACKEND_URL: str(), // URL of our backend
NEXT_PUBLIC_FIREBASE_SETTINGS: json(), // Firebase settings for frontend, stored as a JSON string
NEXT_PUBLIC_FIREBASE_SETTINGS: json(), // Firebase settings for frontend
NEXT_PUBLIC_PAYPAL_CLIENT_SECRET: str(),
NEXT_PUBLIC_PAYPAL_CLIENT_ID: str(),
},
);
File renamed without changes.
9 changes: 1 addition & 8 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,7 @@ const robotoSlab = Roboto_Slab({

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html
lang="en"
className={`
${openSans.className} ${openSans.variable}
${inter.className} ${inter.variable}
${robotoSlab.className} ${robotoSlab.variable}
`}
>
<html lang="en" className={`${openSans.variable} ${inter.variable} ${robotoSlab.variable}`}>
<body>{children}</body>
</html>
);
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Link from "next/link";
import React, { useEffect, useState } from "react";

import styles from "./NavigationBar.module.css";
import UserIcon from "./UserIcon";

const NavigationBar = () => {
const [activeMenu, setActiveMenu] = useState<string>("");
Expand Down Expand Up @@ -83,9 +84,7 @@ const NavigationBar = () => {
)}
</div>
<div className={styles.user}>
<Image src={"/sampleUserPhoto.svg"} alt="userImage" width={36} height={36}></Image>
<p>John Doe</p>
<Image src={"/ic_caretdown.svg"} alt="upArrow" width={24} height={24}></Image>
<UserIcon />
</div>
</div>
<div className={styles.navBar}>
Expand Down
Loading

0 comments on commit 2b4d1c4

Please sign in to comment.