mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 12:19:06 +00:00
Auth front-end
This commit is contained in:
43
www/app/(auth)/userInfo.tsx
Normal file
43
www/app/(auth)/userInfo.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
import {
|
||||
useFiefIsAuthenticated,
|
||||
useFiefUserinfo,
|
||||
} from "@fief/fief/nextjs/react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function UserInfo() {
|
||||
const isAuthenticated = useFiefIsAuthenticated();
|
||||
const userinfo = useFiefUserinfo();
|
||||
|
||||
return (
|
||||
<header className="bg-black w-full border-b border-gray-700 flex justify-between items-center py-2 mb-3">
|
||||
{/* Logo on the left */}
|
||||
<Link href="/">
|
||||
<Image
|
||||
src="/reach.png"
|
||||
width={16}
|
||||
height={16}
|
||||
className="h-6 w-auto ml-2"
|
||||
alt="Reflector"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{/* Text link on the right */}
|
||||
{!isAuthenticated && (
|
||||
<span className="text-white hover:underline font-thin px-2">
|
||||
<Link href="/login">Log in or create account</Link>
|
||||
</span>
|
||||
)}
|
||||
{isAuthenticated && (
|
||||
<span className="text-white font-thin px-2">
|
||||
{userinfo?.email} (
|
||||
<span className="hover:underline">
|
||||
<Link href="/logout">Log out</Link>
|
||||
</span>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ models/PageGetTranscript.ts
|
||||
models/RtcOffer.ts
|
||||
models/TranscriptTopic.ts
|
||||
models/UpdateTranscript.ts
|
||||
models/UserInfo.ts
|
||||
models/ValidationError.ts
|
||||
models/index.ts
|
||||
runtime.ts
|
||||
|
||||
@@ -742,4 +742,49 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* User Me
|
||||
*/
|
||||
async v1UserMeRaw(
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<any>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/me`,
|
||||
method: "GET",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
},
|
||||
initOverrides,
|
||||
);
|
||||
|
||||
if (this.isJsonMime(response.headers.get("content-type"))) {
|
||||
return new runtime.JSONApiResponse<any>(response);
|
||||
} else {
|
||||
return new runtime.TextApiResponse(response) as any;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User Me
|
||||
*/
|
||||
async v1UserMe(
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<any> {
|
||||
const response = await this.v1UserMeRaw(initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
}
|
||||
|
||||
84
www/app/api/models/UserInfo.ts
Normal file
84
www/app/api/models/UserInfo.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* FastAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from "../runtime";
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface UserInfo
|
||||
*/
|
||||
export interface UserInfo {
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
sub: any | null;
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
email: any | null;
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
emailVerified: any | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the UserInfo interface.
|
||||
*/
|
||||
export function instanceOfUserInfo(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "sub" in value;
|
||||
isInstance = isInstance && "email" in value;
|
||||
isInstance = isInstance && "emailVerified" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function UserInfoFromJSON(json: any): UserInfo {
|
||||
return UserInfoFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function UserInfoFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): UserInfo {
|
||||
if (json === undefined || json === null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
sub: json["sub"],
|
||||
email: json["email"],
|
||||
emailVerified: json["email_verified"],
|
||||
};
|
||||
}
|
||||
|
||||
export function UserInfoToJSON(value?: UserInfo | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
sub: value.sub,
|
||||
email: value.email,
|
||||
email_verified: value.emailVerified,
|
||||
};
|
||||
}
|
||||
@@ -8,4 +8,5 @@ export * from "./PageGetTranscript";
|
||||
export * from "./RtcOffer";
|
||||
export * from "./TranscriptTopic";
|
||||
export * from "./UpdateTranscript";
|
||||
export * from "./UserInfo";
|
||||
export * from "./ValidationError";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import "./styles/globals.scss";
|
||||
import { Roboto } from "next/font/google";
|
||||
import { Metadata } from "next";
|
||||
import FiefWrapper from "./fiefWrapper";
|
||||
import UserInfo from "./userInfo";
|
||||
import FiefWrapper from "./(auth)/fiefWrapper";
|
||||
import UserInfo from "./(auth)/userInfo";
|
||||
|
||||
const roboto = Roboto({ subsets: ["latin"], weight: "400" });
|
||||
|
||||
@@ -55,8 +55,19 @@ export default function RootLayout({ children }) {
|
||||
<html lang="en">
|
||||
<body className={roboto.className + " flex flex-col min-h-screen"}>
|
||||
<FiefWrapper>
|
||||
<div id="container">
|
||||
<div className="flex flex-col items-center h-[100svh] bg-gradient-to-r from-[#8ec5fc30] to-[#e0c3fc42]">
|
||||
<UserInfo />
|
||||
|
||||
<div className="h-[13svh] flex flex-col justify-center items-center">
|
||||
<h1 className="text-5xl font-bold text-blue-500">Reflector</h1>
|
||||
<p className="text-gray-500">
|
||||
Capture The Signal, Not The Noise
|
||||
</p>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</FiefWrapper>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
18
www/app/lib/getApi.ts
Normal file
18
www/app/lib/getApi.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Configuration } from "../api/runtime";
|
||||
import { DefaultApi } from "../api/apis/DefaultApi";
|
||||
|
||||
import { useFiefAccessTokenInfo } from "@fief/fief/nextjs/react";
|
||||
|
||||
export default function getApi(): DefaultApi {
|
||||
const accessTokenInfo = useFiefAccessTokenInfo();
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
accessToken: accessTokenInfo
|
||||
? "Bearer " + accessTokenInfo.access_token
|
||||
: undefined,
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
|
||||
return api;
|
||||
}
|
||||
21
www/app/lib/user.ts
Normal file
21
www/app/lib/user.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export async function getCurrentUser(): Promise<any> {
|
||||
try {
|
||||
const response = await fetch("/api/current-user");
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Ensure the data structure is as expected
|
||||
if (data.userinfo && data.access_token_info) {
|
||||
return data;
|
||||
} else {
|
||||
throw new Error("Unexpected data structure");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching the user data:", error);
|
||||
throw error; // or you can return an appropriate fallback or error indicator
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,7 @@ const App = () => {
|
||||
const webSockets = useWebSockets(transcript.response?.id);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center h-[100svh] bg-gradient-to-r from-[#8ec5fc30] to-[#e0c3fc42]">
|
||||
<div className="h-[13svh] flex flex-col justify-center items-center">
|
||||
<h1 className="text-5xl font-bold text-blue-500">Reflector</h1>
|
||||
<p className="text-gray-500">Capture The Signal, Not The Noise</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<Recorder
|
||||
setStream={setStream}
|
||||
onStop={() => {
|
||||
@@ -48,7 +43,7 @@ const App = () => {
|
||||
topics={webSockets.topics}
|
||||
disconnected={disconnected}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { DefaultApi, V1TranscriptsCreateRequest } from "../api/apis/DefaultApi";
|
||||
import { Configuration } from "../api/runtime";
|
||||
import { GetTranscript } from "../api";
|
||||
import getApi from "../lib/getApi";
|
||||
|
||||
type UseTranscript = {
|
||||
response: GetTranscript | null;
|
||||
@@ -15,11 +16,7 @@ const useTranscript = (): UseTranscript => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
// accessToken:
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
const api = getApi();
|
||||
|
||||
const createTranscript = () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
V1TranscriptRecordWebrtcRequest,
|
||||
} from "../api/apis/DefaultApi";
|
||||
import { Configuration } from "../api/runtime";
|
||||
import getApi from "../lib/getApi";
|
||||
|
||||
const useWebRTC = (
|
||||
stream: MediaStream | null,
|
||||
@@ -17,10 +18,7 @@ const useWebRTC = (
|
||||
return;
|
||||
}
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
const api = getApi();
|
||||
|
||||
let p: Peer = new Peer({ initiator: true, stream: stream });
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
"use client";
|
||||
import { useFiefUserinfo } from "@fief/fief/nextjs/react";
|
||||
|
||||
export default function UserInfo() {
|
||||
const userinfo = useFiefUserinfo();
|
||||
|
||||
return (
|
||||
<>
|
||||
{userinfo && (
|
||||
<>
|
||||
<h1>Logged In</h1>
|
||||
<p>{userinfo.email}</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!userinfo && (
|
||||
<>
|
||||
<h1>Not Logged In</h1>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user