Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue/504 add auth #518

Merged
merged 5 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/nowcasting-app/components/map/deltaMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import mapboxgl, { Expression } from "mapbox-gl";
import { FailedStateMap, LoadStateMap, Map, MeasuringUnit } from "./";
import { ActiveUnit, SelectedData } from "./types";
import { DELTA_BUCKET, VIEWS } from "../../constant";
import ButtonGroup from "../../components/button-group";
import gspShapeData from "../../data/gsp_regions_20220314.json";
import useGlobalState from "../helpers/globalState";
import { formatISODateString, formatISODateStringHuman } from "../helpers/utils";
Expand All @@ -28,7 +27,9 @@ import DeltaColorGuideBar from "./delta-color-guide-bar";
import { safelyUpdateMapData } from "../helpers/mapUtils";
import { generateGeoJsonForecastData } from "../helpers/data";
import { components } from "../../types/quartz-api";
import dynamic from "next/dynamic";
const yellow = theme.extend.colors["ocf-yellow"].DEFAULT;
const ButtonGroup = dynamic(() => import("../../components/button-group"), { ssr: false });

const getRoundedPv = (pv: number, round: boolean = true) => {
if (!round) return Math.round(pv);
Expand Down
4 changes: 3 additions & 1 deletion apps/nowcasting-app/components/map/pvLatestMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import mapboxgl, { Expression } from "mapbox-gl";
import { FailedStateMap, LoadStateMap, Map, MeasuringUnit } from "./";
import { ActiveUnit, SelectedData } from "./types";
import { MAX_POWER_GENERATED, VIEWS } from "../../constant";
import ButtonGroup from "../../components/button-group";
import gspShapeData from "../../data/gsp_regions_20220314.json";
import useGlobalState from "../helpers/globalState";
import { formatISODateString, formatISODateStringHuman } from "../helpers/utils";
Expand All @@ -17,8 +16,11 @@ import { FeatureCollection } from "geojson";
import { safelyUpdateMapData } from "../helpers/mapUtils";
import { components } from "../../types/quartz-api";
import { generateGeoJsonForecastData } from "../helpers/data";
import dynamic from "next/dynamic";
peterdudfield marked this conversation as resolved.
Show resolved Hide resolved
const yellow = theme.extend.colors["ocf-yellow"].DEFAULT;

const ButtonGroup = dynamic(() => import("../../components/button-group"), { ssr: false });

type PvLatestMapProps = {
className?: string;
combinedData: CombinedData;
Expand Down
3 changes: 2 additions & 1 deletion apps/nowcasting-app/components/map/sitesMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
MAX_POWER_GENERATED,
VIEWS
} from "../../constant";
import ButtonGroup from "../../components/button-group";
import gspShapeData from "../../data/gsp_regions_20220314.json";
import dnoShapeData from "../../data/dno_regions_lat_long_converted.json";
import useGlobalState from "../helpers/globalState";
Expand All @@ -26,8 +25,10 @@ import { Feature, FeatureCollection } from "geojson";
import Slider from "./sitesMapFeatures/sitesZoomSlider";
import SitesLegend from "./sitesMapFeatures/sitesLegend";
import { safelyUpdateMapData } from "../helpers/mapUtils";
import dynamic from "next/dynamic";

const yellow = theme.extend.colors["ocf-yellow"].DEFAULT;
const ButtonGroup = dynamic(() => import("../../components/button-group"), { ssr: false });

const getRoundedPv = (pv: number, round: boolean = true) => {
if (!round) return Math.round(pv);
Expand Down
1 change: 1 addition & 0 deletions apps/quartz-app/.env.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AUTH0_BASE_URL=https://$VERCEL_URL
1 change: 1 addition & 0 deletions apps/quartz-app/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AUTH0_BASE_URL=https://$VERCEL_URL
14 changes: 14 additions & 0 deletions apps/quartz-app/app/api/auth/[auth0]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { handleAuth, handleLogout, HandleAuth } from "@auth0/nextjs-auth0";

peterdudfield marked this conversation as resolved.
Show resolved Hide resolved
/**
* This is a GET endpoint that automatically handles authentication using Auth0.
* We're using a logout option override to redirect to "/logout" after logout.
*
* @function GET
* @returns {HandleAuth} A function that handles authentication.
*/
export const GET = handleAuth({
logout: handleLogout({
returnTo: "/logout",
}),
});
19 changes: 19 additions & 0 deletions apps/quartz-app/app/api/token/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NextRequest, NextResponse } from "next/server";
import { getAccessToken, withApiAuthRequired } from "@auth0/nextjs-auth0";

peterdudfield marked this conversation as resolved.
Show resolved Hide resolved
/**
* This is a secured GET endpoint that requires API authentication.
* It retrieves the access token for the authenticated user.
*
* @async
* @function GET
* @param {NextRequest} req - The Next.js API request object.
* @returns {NextResponse} A JSON response containing the access token.
*/
const GET = withApiAuthRequired(async function GET(req: NextRequest) {
const res = new NextResponse();
const { accessToken } = await getAccessToken(req, res);
return NextResponse.json({ accessToken }, res);
});

export { GET };
6 changes: 0 additions & 6 deletions apps/quartz-app/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

@layer utilities {
Expand Down
15 changes: 7 additions & 8 deletions apps/quartz-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "../src/components/layout/Header";
import Providers from "@/app/providers";
import { ReactNode } from "react";
import { UserProvider } from "@auth0/nextjs-auth0/client";
import LayoutWrapper from "@/src/components/layout/LayoutWrapper";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -19,12 +19,11 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>
<Header />
{children}
</Providers>
</body>
<UserProvider>
<body className={inter.className}>
<LayoutWrapper>{children}</LayoutWrapper>
</body>
</UserProvider>
</html>
);
}
93 changes: 93 additions & 0 deletions apps/quartz-app/app/logout/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use client";

import { useRouter } from "next/navigation";
import { ChevronRightIcon } from "@heroicons/react/solid";
import { SupportIcon, ViewListIcon } from "@heroicons/react/outline";

const MyPage = () => {
const router = useRouter();
const links = [
// {
// title: "Documentation",
// description: "Learn how to integrate our tools with your app",
// icon: BookOpenIcon,
// url: "https://openclimatefix.notion.site/Quartz-Solar-Documentation-0d718915650e4f098470d695aa3494bf",
// },
{
title: "API Reference",
description: "A complete API reference for our library",
icon: ViewListIcon,
url:
process.env.NEXT_PUBLIC_API_URL + "docs" ||
"https://api.quartz.energy/docs",
},
{
title: "Support",
description: "Get help with any problems you encounter",
icon: SupportIcon,
url: "mailto:[email protected]?subject=Quartz%20Energy%20Support%20Request",
},
];
return (
<div className="container flex-1 flex flex-col gap-6 py-24 items-center">
<div className="flex-1 flex flex-col gap-6 justify-center text-center md:text-left items-center">
<h1 className="text-4xl text-white">See you next time.</h1>
<h2 className="text-xl text-white px-3">
You have been successfully logged out.
</h2>
<button
className="bg-ocf-yellow py-2 px-3 rounded-md"
onClick={() => router.push("/api/auth/login")}
>
Log back in &rarr;
</button>
</div>
<div className="max-w-3xl w-full flex flex-1 flex-col gap-6 justify-center items-center">
<div className="mt-16">
<h2 className="text-sm font-semibold tracking-wide text-gray-300 uppercase">
More from Quartz
</h2>
<ul
role="list"
className="mt-4 border-t border-b border-gray-200 divide-y divide-gray-200"
>
{links.map((link, linkIdx) => (
<li
key={`LogoutLink${linkIdx}`}
className="relative flex items-start py-6 space-x-4"
>
<div className="flex-shrink-0">
<span className="flex items-center justify-center w-12 h-12 rounded-lg">
<link.icon
className="w-6 h-6 text-gray-300"
aria-hidden="true"
/>
</span>
</div>
<div className="flex-1 min-w-0">
<h3 className="text-base font-medium text-gray-100">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500">
<a href={link.url} className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" />
{link.title}
</a>
</span>
</h3>
<p className="text-base text-gray-400">{link.description}</p>
</div>
<div className="self-center flex-shrink-0">
<ChevronRightIcon
className="w-5 h-5 text-gray-400"
aria-hidden="true"
/>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
);
};

export default MyPage;
143 changes: 21 additions & 122 deletions apps/quartz-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,130 +1,29 @@
"use client";
import Image from "next/image";
import Sidebar from "../src/components/Sidebar";
import Charts from "../src/components/charts/Charts";
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from "@tanstack/react-query";
import { GET_REGIONS, getRegionsQuery } from "@/src/data/queries";
import {
useGetForecastedGenerationForRegionQuery,
useGetGenerationForRegionQuery,
useGetRegionsQuery,
} from "@/src/hooks/queries";
import { CombinedData } from "@/src/types/data";
import { useEffect, useMemo } from "react";
import useGlobalState from "@/src/components/helpers/globalState";

export default function Home() {
const [combinedData, setCombinedData] = useGlobalState("combinedData");

const queryClient = new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
refetchInterval: 60 * 1000,
import { withPageAuthRequired } from "@auth0/nextjs-auth0";
import { Main } from "@/src/components/Main";

export default withPageAuthRequired(
async function Home() {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
refetchInterval: 60 * 1000,
},
},
}, //
});

queryClient.prefetchQuery({
queryKey: [GET_REGIONS, "solar"],
queryFn: () => getRegionsQuery("solar"),
});

const { data: solarRegionsData, error: solarRegionsError } =
useGetRegionsQuery("solar");
console.log("page solarRegionsData", solarRegionsData);

const { data: windRegionsData, error: windRegionsError } =
useGetRegionsQuery("wind");
console.log("page windRegionsData", windRegionsData);

const { data: solarGenerationData, error: solarGenerationError } =
useGetGenerationForRegionQuery(
"solar",
solarRegionsData?.regions[0] || "",
!!solarRegionsData?.regions[0]
);
const { data: windGenerationData, error: windGenerationError } =
useGetGenerationForRegionQuery(
"wind",
windRegionsData?.regions[0] || "",
!!windRegionsData?.regions[0]
);

// Get forecast data
const { data: solarForecastData, error: solarForecastError } =
useGetForecastedGenerationForRegionQuery(
"solar",
solarRegionsData?.regions[0] || "",
!!solarRegionsData?.regions[0]
);
const { data: windForecastData, error: windForecastError } =
useGetForecastedGenerationForRegionQuery(
"wind",
windRegionsData?.regions[0] || "",
!!windRegionsData?.regions[0]
);
});

const latestCombinedData: CombinedData = useMemo(() => {
return {
solarGenerationData,
windGenerationData,
solarForecastData,
windForecastData,
};
}, [
solarGenerationData,
windGenerationData,
solarForecastData,
windForecastData,
]);

useEffect(() => {
console.log("combinedData updated", latestCombinedData);
setCombinedData(latestCombinedData);
}, [latestCombinedData]);

if (
solarRegionsError ||
windRegionsError ||
solarGenerationError ||
windGenerationError ||
solarForecastError ||
windForecastError
) {
console.log(
"error",
solarRegionsError,
windRegionsError,
solarGenerationError,
windGenerationError,
solarForecastError,
windForecastError
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Main />
</HydrationBoundary>
);
return <div>Error</div>;
}

console.log("page solarGenerationData", solarGenerationData);
console.log("page windGenerationData", windGenerationData);

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<main className="flex min-h-screen bg-ocf-gray-900 flex-row items-stretch justify-between pt-16">
<Sidebar
title={"Rajasthan"}
solarForecastData={solarForecastData}
windForecastData={windForecastData}
solarGenerationData={solarGenerationData}
windGenerationData={windGenerationData}
/>
<Charts combinedData={combinedData} />
</main>
</HydrationBoundary>
);
}
},
{ returnTo: "/" }
);
1 change: 1 addition & 0 deletions apps/quartz-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test:watch": "jest --watch"
},
"dependencies": {
"@auth0/nextjs-auth0": "3.2.0",
"@babel/eslint-parser": "^7.18.9",
"@tanstack/react-query": "^5.20.2",
"eslint-config-prettier": "^8.5.0",
Expand Down
Loading
Loading