From 08cc97f557bee841085601ad23d9e7eeb56ed410 Mon Sep 17 00:00:00 2001 From: swve Date: Mon, 27 May 2024 20:58:32 +0200 Subject: [PATCH] fix: session auth issues --- apps/collaboration/pnpm-lock.yaml | 4 +- apps/web/app/auth/options.ts | 4 +- .../activity/[activityuuid]/edit}/loading.tsx | 2 +- .../activity/[activityuuid]/edit/page.tsx | 18 +- .../app/install/steps/account_creation.tsx | 5 +- .../app/install/steps/default_elements.tsx | 5 +- apps/web/app/install/steps/finish.tsx | 5 +- apps/web/app/install/steps/get_started.tsx | 5 +- apps/web/app/install/steps/org_creation.tsx | 5 +- apps/web/app/install/steps/sample_data.tsx | 5 +- apps/web/app/layout.tsx | 27 +-- .../collection/[collectionid]/page.tsx | 17 +- .../(withmenu)/collections/new/page.tsx | 7 +- .../[orgslug]/(withmenu)/collections/page.tsx | 10 +- .../activity/[activityid]/activity.tsx | 10 +- .../activity/[activityid]/page.tsx | 16 +- .../(withmenu)/course/[courseuuid]/course.tsx | 8 +- .../(withmenu)/course/[courseuuid]/page.tsx | 16 +- .../[orgslug]/(withmenu)/courses/page.tsx | 11 +- .../app/orgs/[orgslug]/(withmenu)/page.tsx | 17 +- .../orgs/[orgslug]/dash/ClientAdminLayout.tsx | 26 +++ .../orgs/[orgslug]/dash/courses/client.tsx | 1 - .../app/orgs/[orgslug]/dash/courses/page.tsx | 12 +- apps/web/app/orgs/[orgslug]/dash/layout.tsx | 11 +- .../user-account/settings/[subpage]/page.tsx | 4 +- .../dash/users/settings/[subpage]/page.tsx | 4 +- apps/web/app/orgs/[orgslug]/layout.tsx | 6 +- apps/web/app/orgs/[orgslug]/login/login.tsx | 16 +- apps/web/app/orgs/[orgslug]/signup/signup.tsx | 8 +- .../web/components/Contexts/CourseContext.tsx | 7 +- .../components/Contexts/LHSessionContext.tsx | 33 ++++ apps/web/components/Contexts/OrgContext.tsx | 14 +- .../EditCourseAccess/EditCourseAccess.tsx | 7 +- .../EditCourseGeneral/ThumbnailUpdate.tsx | 5 +- .../Buttons/NewActivityButton.tsx | 11 +- .../DraggableElements/ActivityElement.tsx | 7 +- .../DraggableElements/ChapterElement.tsx | 7 +- .../EditCourseStructure.tsx | 5 +- apps/web/components/Dashboard/UI/LeftMenu.tsx | 8 +- .../web/components/Dashboard/UI/SaveState.tsx | 8 +- .../UserEditGeneral/UserEditGeneral.tsx | 4 +- .../UserEditPassword/UserEditPassword.tsx | 4 +- apps/web/components/Hooks/useAdminStatus.tsx | 53 +++--- .../Objects/Activities/AI/AIActivityAsk.tsx | 8 +- .../Objects/CourseUpdates/CourseUpdates.tsx | 33 ++-- .../Objects/Editor/ActiveAvatars.tsx | 4 +- apps/web/components/Objects/Editor/Editor.tsx | 4 +- .../Objects/Editor/EditorWrapper.tsx | 4 +- apps/web/components/Objects/Menu/Menu.tsx | 10 +- .../Modals/Course/Create/CreateCourse.tsx | 8 +- .../Dash/OrgAccess/OrgInviteCodeGenerate.tsx | 6 +- .../Thumbnails/CollectionThumbnail.tsx | 4 +- .../Objects/Thumbnails/CourseThumbnail.tsx | 10 +- apps/web/components/Objects/UserAvatar.tsx | 4 +- .../Pages/CourseEdit/Draggables/Activity.tsx | 6 +- .../Pages/CourseEdit/Draggables/Chapter.tsx | 4 +- .../Pages/Trail/TrailCourseElement.tsx | 5 +- .../Security/AdminAuthorization.tsx | 158 +++++++----------- .../Security/AuthenticatedClientElement.tsx | 10 +- .../components/Security/HeaderProfileBox.tsx | 4 +- apps/web/pnpm-lock.yaml | 59 ++++--- apps/web/services/courses/activities.ts | 42 +++-- apps/web/services/courses/activity.ts | 14 +- apps/web/services/courses/chapters.ts | 42 +++-- apps/web/services/courses/collections.ts | 37 ++-- apps/web/services/courses/courses.ts | 37 ++-- apps/web/services/courses/updates.ts | 15 +- apps/web/services/utils/ts/requests.ts | 31 +++- apps/web/types/next-auth.d.ts | 15 ++ package.json | 2 +- 70 files changed, 607 insertions(+), 427 deletions(-) rename apps/web/app/{orgs/[orgslug]/(withmenu)/course/[courseuuid] => editor/course/[courseid]/activity/[activityuuid]/edit}/loading.tsx (98%) create mode 100644 apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx create mode 100644 apps/web/components/Contexts/LHSessionContext.tsx create mode 100644 apps/web/types/next-auth.d.ts diff --git a/apps/collaboration/pnpm-lock.yaml b/apps/collaboration/pnpm-lock.yaml index 867e9b80..47fd3b6f 100644 --- a/apps/collaboration/pnpm-lock.yaml +++ b/apps/collaboration/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@hocuspocus/server': specifier: ^2.11.3 - version: 2.11.3(y-protocols@1.0.6)(yjs@13.6.14) + version: 2.11.3(y-protocols@1.0.6(yjs@13.6.14))(yjs@13.6.14) bun: specifier: ^1.0.36 version: 1.1.1 @@ -133,7 +133,7 @@ snapshots: dependencies: lib0: 0.2.93 - '@hocuspocus/server@2.11.3(y-protocols@1.0.6)(yjs@13.6.14)': + '@hocuspocus/server@2.11.3(y-protocols@1.0.6(yjs@13.6.14))(yjs@13.6.14)': dependencies: '@hocuspocus/common': 2.11.3 async-lock: 1.4.1 diff --git a/apps/web/app/auth/options.ts b/apps/web/app/auth/options.ts index fe82d753..6559887d 100644 --- a/apps/web/app/auth/options.ts +++ b/apps/web/app/auth/options.ts @@ -42,7 +42,7 @@ export const nextAuthOptions = { }), ], callbacks: { - async jwt({ token, user, account }) { + async jwt({ token, user, account }: any) { // First sign in with Credentials provider if (account?.provider == 'credentials' && user) { token.user = user @@ -78,7 +78,7 @@ export const nextAuthOptions = { } return token }, - async session({ session, token }) { + async session({ session, token }: any) { // Include user information in the session if (token.user) { let api_SESSION = await getUserSession(token.user.tokens.access_token) diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/loading.tsx b/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/loading.tsx similarity index 98% rename from apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/loading.tsx rename to apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/loading.tsx index 3da44848..a6338cbf 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/loading.tsx +++ b/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/loading.tsx @@ -3,4 +3,4 @@ import PageLoading from '@components/Objects/Loaders/PageLoading' export default function Loading() { // Or a custom loading skeleton component return -} +} \ No newline at end of file diff --git a/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/page.tsx b/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/page.tsx index 9cad3186..b8e36ade 100644 --- a/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/page.tsx +++ b/apps/web/app/editor/course/[courseid]/activity/[activityuuid]/edit/page.tsx @@ -1,13 +1,13 @@ import { default as React } from 'react' import dynamic from 'next/dynamic' -import { getCourseMetadataWithAuthHeader } from '@services/courses/courses' -import { cookies } from 'next/headers' +import { getCourseMetadata } from '@services/courses/courses' import { Metadata } from 'next' import { getActivityWithAuthHeader } from '@services/courses/activities' -import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' import { getOrganizationContextInfoWithId } from '@services/organizations/orgs' import EditorOptionsProvider from '@components/Contexts/Editor/EditorContext' import AIEditorProvider from '@components/Contexts/AI/AIEditorContext' +import { nextAuthOptions } from 'app/auth/options' +import { getServerSession } from 'next-auth' const EditorWrapper = dynamic(() => import('@components/Objects/Editor/EditorWrapper'), { ssr: false }) @@ -19,10 +19,10 @@ type MetadataProps = { export async function generateMetadata({ params, }: MetadataProps): Promise { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token // Get Org context information - const course_meta = await getCourseMetadataWithAuthHeader( + const course_meta = await getCourseMetadata( params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null @@ -35,11 +35,11 @@ export async function generateMetadata({ } const EditActivity = async (params: any) => { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token const activityuuid = params.params.activityuuid const courseid = params.params.courseid - const courseInfo = await getCourseMetadataWithAuthHeader( + const courseInfo = await getCourseMetadata( courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null diff --git a/apps/web/app/install/steps/account_creation.tsx b/apps/web/app/install/steps/account_creation.tsx index 98cc1760..1a8d8531 100644 --- a/apps/web/app/install/steps/account_creation.tsx +++ b/apps/web/app/install/steps/account_creation.tsx @@ -10,6 +10,7 @@ import { getAPIUrl } from '@services/config/config' import { createNewUserInstall, updateInstall } from '@services/install/install' import { swrFetcher } from '@services/utils/ts/requests' import { useFormik } from 'formik' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React from 'react' import { BarLoader } from 'react-spinners' @@ -47,11 +48,13 @@ const validate = (values: any) => { function AccountCreation() { const [isSubmitting, setIsSubmitting] = React.useState(false) + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const router = useRouter() const formik = useFormik({ initialValues: { diff --git a/apps/web/app/install/steps/default_elements.tsx b/apps/web/app/install/steps/default_elements.tsx index 53b19190..b5f54bb6 100644 --- a/apps/web/app/install/steps/default_elements.tsx +++ b/apps/web/app/install/steps/default_elements.tsx @@ -1,16 +1,19 @@ import { getAPIUrl } from '@services/config/config' import { createDefaultElements, updateInstall } from '@services/install/install' import { swrFetcher } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React from 'react' import useSWR from 'swr' function DefaultElements() { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const [isSubmitting, setIsSubmitting] = React.useState(false) const [isSubmitted, setIsSubmitted] = React.useState(false) const router = useRouter() diff --git a/apps/web/app/install/steps/finish.tsx b/apps/web/app/install/steps/finish.tsx index 8bb09fe3..853908ec 100644 --- a/apps/web/app/install/steps/finish.tsx +++ b/apps/web/app/install/steps/finish.tsx @@ -2,16 +2,19 @@ import { getAPIUrl } from '@services/config/config' import { updateInstall } from '@services/install/install' import { swrFetcher } from '@services/utils/ts/requests' import { Check } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React from 'react' import useSWR from 'swr' const Finish = () => { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const router = useRouter() async function finishInstall() { diff --git a/apps/web/app/install/steps/get_started.tsx b/apps/web/app/install/steps/get_started.tsx index 1c247b97..75483613 100644 --- a/apps/web/app/install/steps/get_started.tsx +++ b/apps/web/app/install/steps/get_started.tsx @@ -1,16 +1,19 @@ import PageLoading from '@components/Objects/Loaders/PageLoading' import { getAPIUrl } from '@services/config/config' import { swrFetcher } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React, { useEffect } from 'react' import useSWR, { mutate } from 'swr' function GetStarted() { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const router = useRouter() async function startInstallation() { diff --git a/apps/web/app/install/steps/org_creation.tsx b/apps/web/app/install/steps/org_creation.tsx index 11ac9810..5efd2c93 100644 --- a/apps/web/app/install/steps/org_creation.tsx +++ b/apps/web/app/install/steps/org_creation.tsx @@ -14,6 +14,7 @@ import useSWR from 'swr' import { createNewOrgInstall, updateInstall } from '@services/install/install' import { useRouter } from 'next/navigation' import { Check } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' const validate = (values: any) => { const errors: any = {} @@ -40,11 +41,13 @@ const validate = (values: any) => { } function OrgCreation() { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const [isSubmitting, setIsSubmitting] = React.useState(false) const [isSubmitted, setIsSubmitted] = React.useState(false) const router = useRouter() diff --git a/apps/web/app/install/steps/sample_data.tsx b/apps/web/app/install/steps/sample_data.tsx index 7a174283..217cc547 100644 --- a/apps/web/app/install/steps/sample_data.tsx +++ b/apps/web/app/install/steps/sample_data.tsx @@ -4,16 +4,19 @@ import { updateInstall, } from '@services/install/install' import { swrFetcher } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React from 'react' import useSWR from 'swr' function SampleData() { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: install, error: error, isLoading, - } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher) + } = useSWR(`${getAPIUrl()}install/latest`, (url) => swrFetcher(url, access_token)) const router = useRouter() function createSampleData() { diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index e22e22f2..93dcdc2d 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -3,6 +3,7 @@ import '../styles/globals.css' import StyledComponentsRegistry from '../components/Utils/libs/styled-registry' import { motion } from 'framer-motion' import { SessionProvider } from 'next-auth/react' +import LHSessionProvider from '@components/Contexts/LHSessionContext' export default function RootLayout({ children, @@ -19,18 +20,20 @@ export default function RootLayout({ - - - {children} - - + + + + {children} + + + diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx index a93d57d9..f2182c38 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx @@ -1,10 +1,11 @@ import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper' -import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' import { getUriWithOrg } from '@services/config/config' -import { getCollectionByIdWithAuthHeader } from '@services/courses/collections' +import { getCollectionById } from '@services/courses/collections' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { getOrganizationContextInfo } from '@services/organizations/orgs' +import { nextAuthOptions } from 'app/auth/options' import { Metadata } from 'next' +import { getServerSession } from 'next-auth' import { cookies } from 'next/headers' import Link from 'next/link' @@ -16,15 +17,15 @@ type MetadataProps = { export async function generateMetadata({ params, }: MetadataProps): Promise { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'], }) - const col = await getCollectionByIdWithAuthHeader( + const col = await getCollectionById( params.collectionid, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] } @@ -53,14 +54,14 @@ export async function generateMetadata({ } const CollectionPage = async (params: any) => { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token const org = await getOrganizationContextInfo(params.params.orgslug, { revalidate: 1800, tags: ['organizations'], }) const orgslug = params.params.orgslug - const col = await getCollectionByIdWithAuthHeader( + const col = await getCollectionById( params.params.collectionid, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] } diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx index 77708f79..be8cbd01 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx @@ -6,9 +6,12 @@ import useSWR from 'swr' import { getAPIUrl, getUriWithOrg } from '@services/config/config' import { revalidateTags, swrFetcher } from '@services/utils/ts/requests' import { useOrg } from '@components/Contexts/OrgContext' +import { useLHSession } from '@components/Contexts/LHSessionContext' function NewCollection(params: any) { const org = useOrg() as any + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const orgslug = params.params.orgslug const [name, setName] = React.useState('') const [description, setDescription] = React.useState('') @@ -16,7 +19,7 @@ function NewCollection(params: any) { const router = useRouter() const { data: courses, error: error } = useSWR( `${getAPIUrl()}courses/org_slug/${orgslug}/page/1/limit/10`, - swrFetcher + (url) => swrFetcher(url, access_token) ) const [isPublic, setIsPublic] = useState('true') @@ -44,7 +47,7 @@ function NewCollection(params: any) { public: isPublic, org_id: org.id, } - await createCollection(collection) + await createCollection(collection, session.data?.tokens?.access_token) await revalidateTags(['collections'], org.slug) // reload the page router.refresh() diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx index aaed61cf..50f02a06 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx @@ -2,7 +2,6 @@ import AuthenticatedClientElement from '@components/Security/AuthenticatedClient import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle' import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper' import { getUriWithOrg } from '@services/config/config' -import { getOrgCollectionsWithAuthHeader } from '@services/courses/collections' import { getOrganizationContextInfo } from '@services/organizations/orgs' import { Metadata } from 'next' import { cookies } from 'next/headers' @@ -11,6 +10,9 @@ import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' import CollectionThumbnail from '@components/Objects/Thumbnails/CollectionThumbnail' import NewCollectionButton from '@components/StyledElements/Buttons/NewCollectionButton' import ContentPlaceHolderIfUserIsNotAdmin from '@components/ContentPlaceHolder' +import { nextAuthOptions } from 'app/auth/options' +import { getServerSession } from 'next-auth' +import { getOrgCollections } from '@services/courses/collections' type MetadataProps = { params: { orgslug: string; courseid: string } @@ -49,15 +51,15 @@ export async function generateMetadata({ } const CollectionsPage = async (params: any) => { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token const orgslug = params.params.orgslug const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'], }) const org_id = org.id - const collections = await getOrgCollectionsWithAuthHeader( + const collections = await getOrgCollections( org_id, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] } diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx index f7c91833..aa2641eb 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx @@ -15,6 +15,7 @@ import { useOrg } from '@components/Contexts/OrgContext' import { CourseProvider } from '@components/Contexts/CourseContext' import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk' import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext' +import { useLHSession } from '@components/Contexts/LHSessionContext' interface ActivityClientProps { activityid: string @@ -106,11 +107,10 @@ function ActivityClient(props: ActivityClientProps) { {activity ? (
{activity.activity_type == 'TYPE_DYNAMIC' && ( @@ -147,13 +147,15 @@ export function MarkStatus(props: { orgslug: string }) { const router = useRouter() + const session = useLHSession() console.log(props.course.trail) async function markActivityAsCompleteFront() { const trail = await markActivityAsComplete( props.orgslug, props.course.course_uuid, - 'activity_' + props.activityid + 'activity_' + props.activityid, + session.data?.tokens?.access_token ) router.refresh() } diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/page.tsx index 1cf87b7f..b834eae6 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/page.tsx @@ -1,10 +1,12 @@ import { getActivityWithAuthHeader } from '@services/courses/activities' -import { getCourseMetadataWithAuthHeader } from '@services/courses/courses' +import { getCourseMetadata } from '@services/courses/courses' import { cookies } from 'next/headers' import ActivityClient from './activity' import { getOrganizationContextInfo } from '@services/organizations/orgs' import { Metadata } from 'next' import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' +import { getServerSession } from 'next-auth' +import { nextAuthOptions } from 'app/auth/options' type MetadataProps = { params: { orgslug: string; courseuuid: string; activityid: string } @@ -14,15 +16,15 @@ type MetadataProps = { export async function generateMetadata({ params, }: MetadataProps): Promise { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'], }) - const course_meta = await getCourseMetadataWithAuthHeader( + const course_meta = await getCourseMetadata( params.courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null @@ -58,13 +60,13 @@ export async function generateMetadata({ } const ActivityPage = async (params: any) => { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token const activityid = params.params.activityid const courseuuid = params.params.courseuuid const orgslug = params.params.orgslug - const course_meta = await getCourseMetadataWithAuthHeader( + const course_meta = await getCourseMetadata( courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx index fd58842b..d7c48e59 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -17,10 +17,12 @@ import { useOrg } from '@components/Contexts/OrgContext' import UserAvatar from '@components/Objects/UserAvatar' import CourseUpdates from '@components/Objects/CourseUpdates/CourseUpdates' import { CourseProvider } from '@components/Contexts/CourseContext' +import { useLHSession } from '@components/Contexts/LHSessionContext' const CourseClient = (props: any) => { const [user, setUser] = useState({}) const [learnings, setLearnings] = useState([]) + const session = useLHSession() const courseuuid = props.courseuuid const orgslug = props.orgslug const course = props.course @@ -35,7 +37,7 @@ const CourseClient = (props: any) => { async function startCourseUI() { // Create activity - await startCourse('course_' + courseuuid, orgslug) + await startCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) await revalidateTags(['courses'], orgslug) router.refresh() @@ -54,7 +56,7 @@ const CourseClient = (props: any) => { async function quitCourse() { // Close activity - let activity = await removeCourse('course_' + courseuuid, orgslug) + let activity = await removeCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) // Mutate course await revalidateTags(['courses'], orgslug) router.refresh() @@ -277,7 +279,7 @@ const CourseClient = (props: any) => {
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx index f26b7caa..ae96404e 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx @@ -1,11 +1,13 @@ import React from 'react' import CourseClient from './course' import { cookies } from 'next/headers' -import { getCourseMetadataWithAuthHeader } from '@services/courses/courses' +import { getCourseMetadata } from '@services/courses/courses' import { getOrganizationContextInfo } from '@services/organizations/orgs' import { Metadata } from 'next' import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' import { getCourseThumbnailMediaDirectory } from '@services/media/media' +import { nextAuthOptions } from 'app/auth/options' +import { getServerSession } from 'next-auth' type MetadataProps = { params: { orgslug: string; courseuuid: string } @@ -15,15 +17,15 @@ type MetadataProps = { export async function generateMetadata({ params, }: MetadataProps): Promise { - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'], }) - const course_meta = await getCourseMetadataWithAuthHeader( + const course_meta = await getCourseMetadata( params.courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null @@ -67,11 +69,11 @@ export async function generateMetadata({ } const CoursePage = async (params: any) => { - const cookieStore = cookies() const courseuuid = params.params.courseuuid const orgslug = params.params.orgslug - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) - const course_meta = await getCourseMetadataWithAuthHeader( + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token + const course_meta = await getCourseMetadata( courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/page.tsx index d7c8baca..6f15306d 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/page.tsx @@ -1,10 +1,12 @@ import React from 'react' import Courses from './courses' -import { getOrgCoursesWithAuthHeader } from '@services/courses/courses' import { Metadata } from 'next' import { getOrganizationContextInfo } from '@services/organizations/orgs' import { cookies } from 'next/headers' import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' +import { nextAuthOptions } from 'app/auth/options' +import { getServerSession } from 'next-auth' +import { getOrgCourses } from '@services/courses/courses' type MetadataProps = { params: { orgslug: string } @@ -49,9 +51,10 @@ const CoursesPage = async (params: any) => { revalidate: 1800, tags: ['organizations'], }) - const cookieStore = cookies() - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) - const courses = await getOrgCoursesWithAuthHeader( + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token + + const courses = await getOrgCourses( orgslug, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx index b8bc5d0a..5718bb98 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -1,20 +1,20 @@ export const dynamic = 'force-dynamic' import { Metadata } from 'next' import { getUriWithOrg } from '@services/config/config' -import { getOrgCoursesWithAuthHeader } from '@services/courses/courses' +import { getOrgCourses } from '@services/courses/courses' import Link from 'next/link' -import { getOrgCollectionsWithAuthHeader } from '@services/courses/collections' import { getOrganizationContextInfo } from '@services/organizations/orgs' -import { cookies } from 'next/headers' import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper' import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle' -import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth' import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail' import CollectionThumbnail from '@components/Objects/Thumbnails/CollectionThumbnail' import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement' import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton' import NewCollectionButton from '@components/StyledElements/Buttons/NewCollectionButton' import ContentPlaceHolderIfUserIsNotAdmin from '@components/ContentPlaceHolder' +import { getOrgCollections } from '@services/courses/collections' +import { getServerSession } from 'next-auth' +import { nextAuthOptions } from 'app/auth/options' type MetadataProps = { params: { orgslug: string } @@ -54,10 +54,9 @@ export async function generateMetadata({ const OrgHomePage = async (params: any) => { const orgslug = params.params.orgslug - const cookieStore = cookies() - - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) - const courses = await getOrgCoursesWithAuthHeader( + const session = await getServerSession(nextAuthOptions) + const access_token = session?.tokens?.access_token + const courses = await getOrgCourses( orgslug, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null @@ -67,7 +66,7 @@ const OrgHomePage = async (params: any) => { tags: ['organizations'], }) const org_id = org.id - const collections = await getOrgCollectionsWithAuthHeader( + const collections = await getOrgCollections( org.id, access_token ? access_token : null, { revalidate: 0, tags: ['courses'] } diff --git a/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx b/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx new file mode 100644 index 00000000..32778e18 --- /dev/null +++ b/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx @@ -0,0 +1,26 @@ +'use client'; +import LeftMenu from '@components/Dashboard/UI/LeftMenu' +import AdminAuthorization from '@components/Security/AdminAuthorization' +import { SessionProvider } from 'next-auth/react' +import React from 'react' + +function ClientAdminLayout({ + children, + params, +}: { + children: React.ReactNode + params: any +}) { + return ( + + +
+ +
{children}
+
+
+
+ ) +} + +export default ClientAdminLayout \ No newline at end of file diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx index cce3a585..31ae5ce3 100644 --- a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx +++ b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx @@ -30,7 +30,6 @@ function CoursesHome(params: CourseProps) {
-
Courses
- -
- -
{children}
-
-
+ + {children} + ) } diff --git a/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx index fa542767..6162f70e 100644 --- a/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx @@ -7,7 +7,7 @@ import Link from 'next/link' import { getUriWithOrg } from '@services/config/config' import { Info, Lock } from 'lucide-react' import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' export type SettingsParams = { subpage: string @@ -15,7 +15,7 @@ export type SettingsParams = { } function SettingsPage({ params }: { params: SettingsParams }) { - const session = useSession() as any + const session = useLHSession() as any useEffect(() => {}, [session]) diff --git a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx index 1950e259..995c3a68 100644 --- a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx @@ -5,7 +5,7 @@ import Link from 'next/link' import { getUriWithOrg } from '@services/config/config' import { ScanEye, SquareUserRound, UserPlus, Users } from 'lucide-react' import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useOrg } from '@components/Contexts/OrgContext' import OrgUsers from '@components/Dashboard/Users/OrgUsers/OrgUsers' import OrgAccess from '@components/Dashboard/Users/OrgAccess/OrgAccess' @@ -18,7 +18,7 @@ export type SettingsParams = { } function UsersSettingsPage({ params }: { params: SettingsParams }) { - const session = useSession() as any + const session = useLHSession() as any const org = useOrg() as any const [H1Label, setH1Label] = React.useState('') const [H2Label, setH2Label] = React.useState('') diff --git a/apps/web/app/orgs/[orgslug]/layout.tsx b/apps/web/app/orgs/[orgslug]/layout.tsx index 219e1707..bdef53c9 100644 --- a/apps/web/app/orgs/[orgslug]/layout.tsx +++ b/apps/web/app/orgs/[orgslug]/layout.tsx @@ -1,8 +1,8 @@ 'use client' +import LHSessionProvider from '@components/Contexts/LHSessionContext' import { OrgProvider } from '@components/Contexts/OrgContext' import Toast from '@components/StyledElements/Toast/Toast' import '@styles/globals.css' -import { SessionProvider } from 'next-auth/react' export default function RootLayout({ children, @@ -13,9 +13,9 @@ export default function RootLayout({ }) { return (
- - {children} + + {children}
) diff --git a/apps/web/app/orgs/[orgslug]/login/login.tsx b/apps/web/app/orgs/[orgslug]/login/login.tsx index 9394005d..2cfebac8 100644 --- a/apps/web/app/orgs/[orgslug]/login/login.tsx +++ b/apps/web/app/orgs/[orgslug]/login/login.tsx @@ -10,12 +10,12 @@ import * as Form from '@radix-ui/react-form' import { useFormik } from 'formik' import { getOrgLogoMediaDirectory } from '@services/media/media' import React from 'react' -import { loginAndGetToken } from '@services/auth/auth' import { AlertTriangle } from 'lucide-react' import { useRouter } from 'next/navigation' import Link from 'next/link' import { signIn, useSession } from "next-auth/react" import { getUriWithOrg } from '@services/config/config' +import { useLHSession } from '@components/Contexts/LHSessionContext' interface LoginClientProps { org: any @@ -42,7 +42,7 @@ const validate = (values: any) => { const LoginClient = (props: LoginClientProps) => { const [isSubmitting, setIsSubmitting] = React.useState(false) const router = useRouter(); - const session = useSession(); + const session = useLHSession(); const [error, setError] = React.useState('') const formik = useFormik({ @@ -53,19 +53,21 @@ const LoginClient = (props: LoginClientProps) => { validate, onSubmit: async (values) => { setIsSubmitting(true) - //let res = await loginAndGetToken(values.email, values.password) const res = await signIn('credentials', { redirect: false, email: values.email, password: values.password, + callbackUrl: '/' }); if (res && res.error) { setError("Wrong Email or password"); setIsSubmitting(false); - } - else { - router.push(`/`) - setIsSubmitting(false) + }else { + await signIn('credentials', { + email: values.email, + password: values.password, + callbackUrl: '/' + }); } }, }) diff --git a/apps/web/app/orgs/[orgslug]/signup/signup.tsx b/apps/web/app/orgs/[orgslug]/signup/signup.tsx index 3f222f57..128b46b6 100644 --- a/apps/web/app/orgs/[orgslug]/signup/signup.tsx +++ b/apps/web/app/orgs/[orgslug]/signup/signup.tsx @@ -4,7 +4,7 @@ import Image from 'next/image' import { getOrgLogoMediaDirectory } from '@services/media/media' import Link from 'next/link' import { getUriWithOrg } from '@services/config/config' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import React, { useEffect } from 'react' import { MailWarning, Shield, Ticket, UserPlus } from 'lucide-react' import { useOrg } from '@components/Contexts/OrgContext' @@ -22,7 +22,7 @@ interface SignUpClientProps { } function SignUpClient(props: SignUpClientProps) { - const session = useSession() as any + const session = useLHSession() as any const [joinMethod, setJoinMethod] = React.useState('open') const [inviteCode, setInviteCode] = React.useState('') const searchParams = useSearchParams() @@ -113,7 +113,7 @@ function SignUpClient(props: SignUpClientProps) { } const LoggedInJoinScreen = (props: any) => { - const session = useSession() as any + const session = useLHSession() as any const org = useOrg() as any const [isLoading, setIsLoading] = React.useState(true) @@ -144,7 +144,7 @@ const LoggedInJoinScreen = (props: any) => { } const NoTokenScreen = (props: any) => { - const session = useSession() as any + const session = useLHSession() as any const org = useOrg() as any const router = useRouter() const [isLoading, setIsLoading] = React.useState(true) diff --git a/apps/web/components/Contexts/CourseContext.tsx b/apps/web/components/Contexts/CourseContext.tsx index aed51150..ddcf711c 100644 --- a/apps/web/components/Contexts/CourseContext.tsx +++ b/apps/web/components/Contexts/CourseContext.tsx @@ -4,6 +4,7 @@ import { getAPIUrl } from '@services/config/config' import { swrFetcher } from '@services/utils/ts/requests' import React, { createContext, useContext, useEffect, useReducer } from 'react' import useSWR from 'swr' +import { useLHSession } from '@components/Contexts/LHSessionContext' export const CourseContext = createContext(null) as any export const CourseDispatchContext = createContext(null) as any @@ -15,9 +16,11 @@ export function CourseProvider({ children: React.ReactNode courseuuid: string }) { + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const { data: courseStructureData } = useSWR( `${getAPIUrl()}courses/${courseuuid}/meta`, - swrFetcher + (url) => swrFetcher(url, access_token) ) const [courseStructure, dispatchCourseStructure] = useReducer(courseReducer, { courseStructure: courseStructureData ? courseStructureData : {}, @@ -33,7 +36,7 @@ export function CourseProvider({ payload: courseStructureData, }) } - }, [courseStructureData]) + }, [courseStructureData,session]) if (!courseStructureData) return diff --git a/apps/web/components/Contexts/LHSessionContext.tsx b/apps/web/components/Contexts/LHSessionContext.tsx new file mode 100644 index 00000000..5b2bdcad --- /dev/null +++ b/apps/web/components/Contexts/LHSessionContext.tsx @@ -0,0 +1,33 @@ +'use client' +import PageLoading from '@components/Objects/Loaders/PageLoading'; +import { useSession } from 'next-auth/react'; +import React, { useContext, createContext, useEffect } from 'react' + +export const SessionContext = createContext({}) as any + +function LHSessionProvider({ children }: { children: React.ReactNode }) { + const session = useSession(); + + useEffect(() => { + console.log('useLHSession', session); + }, [session]) + + + if (session.status == 'loading') { + return + } + + else { + return ( + + {children} + + ) + } +} + +export function useLHSession() { + return useContext(SessionContext) +} + +export default LHSessionProvider \ No newline at end of file diff --git a/apps/web/components/Contexts/OrgContext.tsx b/apps/web/components/Contexts/OrgContext.tsx index 1362cb45..28ab6c94 100644 --- a/apps/web/components/Contexts/OrgContext.tsx +++ b/apps/web/components/Contexts/OrgContext.tsx @@ -5,6 +5,8 @@ import React, { useContext, useEffect } from 'react' import useSWR from 'swr' import { createContext } from 'react' import { useRouter } from 'next/navigation' +import { useLHSession } from '@components/Contexts/LHSessionContext' +import ErrorUI from '@components/StyledElements/Error/Error' export const OrgContext = createContext({}) as any @@ -15,7 +17,10 @@ export function OrgProvider({ children: React.ReactNode orgslug: string }) { - const { data: org } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, swrFetcher) + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; + const { data: org } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, (url) => swrFetcher(url, access_token)) + const router = useRouter() // Check if Org is Active const verifyIfOrgIsActive = () => { @@ -28,7 +33,12 @@ export function OrgProvider({ verifyIfOrgIsActive() }, [org]) - return {children} + if (org) { + return {children} + } + else { + return + } } export function useOrg() { diff --git a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx index 9fa1abc3..1f5eceaf 100644 --- a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx @@ -6,6 +6,7 @@ import { getAPIUrl } from '@services/config/config' import { unLinkResourcesToUserGroup } from '@services/usergroups/usergroups' import { swrFetcher } from '@services/utils/ts/requests' import { Globe, SquareUserRound, Users, UsersRound, X } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import React from 'react' import toast from 'react-hot-toast' import useSWR, { mutate } from 'swr' @@ -17,13 +18,15 @@ type EditCourseAccessProps = { function EditCourseAccess(props: EditCourseAccessProps) { const [error, setError] = React.useState('') + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const course = useCourse() as any const dispatchCourse = useCourseDispatch() as any const courseStructure = course.courseStructure const { data: usergroups } = useSWR( courseStructure ? `${getAPIUrl()}usergroups/resource/${courseStructure.course_uuid}` : null, - swrFetcher + (url) => swrFetcher(url, access_token) ) const [isPublic, setIsPublic] = React.useState(courseStructure.public) @@ -109,7 +112,7 @@ function EditCourseAccess(props: EditCourseAccessProps) { status="info" >
- {!isPublic ? ( ) : null} + {!isPublic ? () : null}
) diff --git a/apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx b/apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx index 2a3fce58..85c47764 100644 --- a/apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx @@ -4,11 +4,13 @@ import { getAPIUrl } from '@services/config/config' import { updateCourseThumbnail } from '@services/courses/courses' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { ArrowBigUpDash, UploadCloud } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import React from 'react' import { mutate } from 'swr' function ThumbnailUpdate() { const course = useCourse() as any + const session = useLHSession() const org = useOrg() as any const [localThumbnail, setLocalThumbnail] = React.useState(null) as any const [isLoading, setIsLoading] = React.useState(false) as any @@ -20,7 +22,8 @@ function ThumbnailUpdate() { setIsLoading(true) const res = await updateCourseThumbnail( course.courseStructure.course_uuid, - file + file, + session.data?.tokens?.access_token ) mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) // wait for 1 second to show loading animation diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx b/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx index 0926d669..afa30d9a 100644 --- a/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx @@ -10,6 +10,7 @@ import { import { getOrganizationContextInfoWithoutCredentials } from '@services/organizations/orgs' import { revalidateTags } from '@services/utils/ts/requests' import { Layers } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useRouter } from 'next/navigation' import React, { useEffect } from 'react' import { mutate } from 'swr' @@ -23,6 +24,8 @@ function NewActivityButton(props: NewActivityButtonProps) { const [newActivityModal, setNewActivityModal] = React.useState(false) const router = useRouter() const course = useCourse() as any + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const openNewActivityModal = async (chapterId: any) => { setNewActivityModal(true) @@ -38,7 +41,7 @@ function NewActivityButton(props: NewActivityButtonProps) { props.orgslug, { revalidate: 1800 } ) - await createActivity(activity, props.chapterId, org.org_id) + await createActivity(activity, props.chapterId, org.org_id, access_token) mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) setNewActivityModal(false) await revalidateTags(['courses'], props.orgslug) @@ -52,7 +55,7 @@ function NewActivityButton(props: NewActivityButtonProps) { activity: any, chapterId: string ) => { - await createFileActivity(file, type, activity, chapterId) + await createFileActivity(file, type, activity, chapterId, access_token) mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) setNewActivityModal(false) await revalidateTags(['courses'], props.orgslug) @@ -68,7 +71,7 @@ function NewActivityButton(props: NewActivityButtonProps) { await createExternalVideoActivity( external_video_data, activity, - props.chapterId + props.chapterId, access_token ) mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) setNewActivityModal(false) @@ -76,7 +79,7 @@ function NewActivityButton(props: NewActivityButtonProps) { router.refresh() } - useEffect(() => {}, [course]) + useEffect(() => { }, [course]) return (
diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx index 4b182c7e..d001fa06 100644 --- a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx @@ -12,6 +12,7 @@ import { Video, X, } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import Link from 'next/link' import { useRouter } from 'next/navigation' import React from 'react' @@ -32,6 +33,8 @@ interface ModifiedActivityInterface { function ActivityElement(props: ActivitiyElementProps) { const router = useRouter() + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const [modifiedActivity, setModifiedActivity] = React.useState< ModifiedActivityInterface | undefined >(undefined) @@ -41,7 +44,7 @@ function ActivityElement(props: ActivitiyElementProps) { const activityUUID = props.activity.activity_uuid async function deleteActivityUI() { - await deleteActivity(props.activity.activity_uuid) + await deleteActivity(props.activity.activity_uuid,access_token) mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() @@ -60,7 +63,7 @@ function ActivityElement(props: ActivitiyElementProps) { content: props.activity.content, } - await updateActivity(modifiedActivityCopy, activityUUID) + await updateActivity(modifiedActivityCopy, activityUUID,access_token) mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx index ac96b86b..092dd5e7 100644 --- a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx @@ -16,6 +16,7 @@ import { revalidateTags } from '@services/utils/ts/requests' import { useRouter } from 'next/navigation' import { getAPIUrl } from '@services/config/config' import { mutate } from 'swr' +import { useLHSession } from '@components/Contexts/LHSessionContext' type ChapterElementProps = { chapter: any @@ -31,6 +32,8 @@ interface ModifiedChapterInterface { function ChapterElement(props: ChapterElementProps) { const activities = props.chapter.activities || [] + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const [modifiedChapter, setModifiedChapter] = React.useState< ModifiedChapterInterface | undefined >(undefined) @@ -41,7 +44,7 @@ function ChapterElement(props: ChapterElementProps) { const router = useRouter() const deleteChapterUI = async () => { - await deleteChapter(props.chapter.id) + await deleteChapter(props.chapter.id, access_token) mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() @@ -53,7 +56,7 @@ function ChapterElement(props: ChapterElementProps) { let modifiedChapterCopy = { name: modifiedChapter.chapterName, } - await updateChapter(chapterId, modifiedChapterCopy) + await updateChapter(chapterId, modifiedChapterCopy, access_token) mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx b/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx index bb81597b..f9b5b24a 100644 --- a/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx @@ -15,6 +15,7 @@ import { import { Hexagon } from 'lucide-react' import Modal from '@components/StyledElements/Modal/Modal' import NewChapterModal from '@components/Objects/Modals/Chapters/NewChapter' +import { useLHSession } from '@components/Contexts/LHSessionContext' type EditCourseStructureProps = { orgslug: string @@ -38,6 +39,8 @@ export type OrderPayload = const EditCourseStructure = (props: EditCourseStructureProps) => { const router = useRouter() + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; // Check window availability const [winReady, setwinReady] = useState(false) @@ -57,7 +60,7 @@ const EditCourseStructure = (props: EditCourseStructureProps) => { // Submit new chapter const submitChapter = async (chapter: any) => { - await createChapter(chapter) + await createChapter(chapter,access_token) mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Dashboard/UI/LeftMenu.tsx b/apps/web/components/Dashboard/UI/LeftMenu.tsx index 9986c00c..468e6d41 100644 --- a/apps/web/components/Dashboard/UI/LeftMenu.tsx +++ b/apps/web/components/Dashboard/UI/LeftMenu.tsx @@ -1,9 +1,8 @@ 'use client' import { useOrg } from '@components/Contexts/OrgContext' -import { useSession } from 'next-auth/react' +import { signOut } from 'next-auth/react' import ToolTip from '@components/StyledElements/Tooltip/Tooltip' import LearnHouseDashboardLogo from '@public/dashLogo.png' -import { logout } from '@services/auth/auth' import { BookCopy, Home, LogOut, School, Settings, Users } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' @@ -11,10 +10,11 @@ import { useRouter } from 'next/navigation' import React, { useEffect } from 'react' import UserAvatar from '../../Objects/UserAvatar' import AdminAuthorization from '@components/Security/AdminAuthorization' +import { useLHSession } from '@components/Contexts/LHSessionContext' function LeftMenu() { const org = useOrg() as any - const session = useSession() as any + const session = useLHSession() as any const [loading, setLoading] = React.useState(true) const route = useRouter() @@ -26,7 +26,7 @@ function LeftMenu() { } async function logOutUI() { - const res = await logout() + const res = await signOut() if (res) { route.push('/login') } diff --git a/apps/web/components/Dashboard/UI/SaveState.tsx b/apps/web/components/Dashboard/UI/SaveState.tsx index 5474b44d..a773e74b 100644 --- a/apps/web/components/Dashboard/UI/SaveState.tsx +++ b/apps/web/components/Dashboard/UI/SaveState.tsx @@ -11,9 +11,11 @@ import { useRouter } from 'next/navigation' import React, { useEffect } from 'react' import { mutate } from 'swr' import { updateCourse } from '@services/courses/courses' +import { useLHSession } from '@components/Contexts/LHSessionContext' function SaveState(props: { orgslug: string }) { const course = useCourse() as any + const session = useLHSession() as any; const router = useRouter() const saved = course ? course.isSaved : true const dispatchCourse = useCourseDispatch() as any @@ -37,7 +39,8 @@ function SaveState(props: { orgslug: string }) { mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) await updateCourseOrderStructure( course.courseStructure.course_uuid, - course.courseOrder + course.courseOrder, + session.data?.tokens?.access_token ) await revalidateTags(['courses'], props.orgslug) router.refresh() @@ -49,7 +52,8 @@ function SaveState(props: { orgslug: string }) { mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) await updateCourse( course.courseStructure.course_uuid, - course.courseStructure + course.courseStructure, + session.data?.tokens?.access_token ) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx b/apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx index 5d23fe8e..aa74203a 100644 --- a/apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx +++ b/apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx @@ -2,7 +2,7 @@ import { updateProfile } from '@services/settings/profile' import React, { useEffect } from 'react' import { Formik, Form, Field } from 'formik' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { ArrowBigUpDash, Check, @@ -14,7 +14,7 @@ import UserAvatar from '@components/Objects/UserAvatar' import { updateUserAvatar } from '@services/users/users' function UserEditGeneral() { - const session = useSession() as any + const session = useLHSession() as any const [localAvatar, setLocalAvatar] = React.useState(null) as any const [isLoading, setIsLoading] = React.useState(false) as any const [error, setError] = React.useState() as any diff --git a/apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx b/apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx index 257e4c11..e57d0302 100644 --- a/apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx +++ b/apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx @@ -1,10 +1,10 @@ -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { updatePassword } from '@services/settings/password' import { Formik, Form, Field } from 'formik' import React, { useEffect } from 'react' function UserEditPassword() { - const session = useSession() as any + const session = useLHSession() as any const updatePasswordUI = async (values: any) => { let user_id = session.data.user.id diff --git a/apps/web/components/Hooks/useAdminStatus.tsx b/apps/web/components/Hooks/useAdminStatus.tsx index 5e99a322..4a5ee5b3 100644 --- a/apps/web/components/Hooks/useAdminStatus.tsx +++ b/apps/web/components/Hooks/useAdminStatus.tsx @@ -1,43 +1,40 @@ -import { useOrg } from '@components/Contexts/OrgContext' -import { useSession } from 'next-auth/react' -import { useEffect } from 'react' +import { useOrg } from '@components/Contexts/OrgContext'; +import { useLHSession } from '@components/Contexts/LHSessionContext'; +import { useEffect, useState, useMemo } from 'react'; + +interface Role { + org: { id: number }; + role: { id: number; role_uuid: string }; +} function useAdminStatus() { - const session = useSession() as any - const org = useOrg() as any - console.log('useAdminStatus', { - session, - }) + const session = useLHSession() as any; + const org = useOrg() as any; + const [isAdmin, setIsAdmin] = useState(null); + const [loading, setLoading] = useState(true); - // If session is not loaded, redirect to login + const userRoles = useMemo(() => session?.data?.roles || [], [session?.data?.roles]); useEffect(() => { - if (session.status == 'loading') { - return - } - - } - , [session]) - - const isUserAdmin = () => { - if (session.status == 'authenticated') { - const isAdmin = session?.data?.roles.some((role: any) => { + if (session.status === 'authenticated' && org?.id) { + const isAdminVar = userRoles.some((role: Role) => { return ( role.org.id === org.id && (role.role.id === 1 || role.role.id === 2 || role.role.role_uuid === 'role_global_admin' || role.role.role_uuid === 'role_global_maintainer') - ) - }) - return isAdmin + ); + }); + setIsAdmin(isAdminVar); + setLoading(false); // Set loading to false once the status is determined + } else { + setIsAdmin(false); + setLoading(false); // Set loading to false if not authenticated or org not found } - return false - } - - // Return the user admin status - return isUserAdmin() + }, [session.status, userRoles, org.id]); + return { isAdmin, loading }; } -export default useAdminStatus \ No newline at end of file +export default useAdminStatus; diff --git a/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx b/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx index bd0c2d12..057cb986 100644 --- a/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx +++ b/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx @@ -1,4 +1,4 @@ -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { sendActivityAIChatMessage, startActivityAIChatSession, @@ -74,7 +74,7 @@ type ActivityChatMessageBoxProps = { } function ActivityChatMessageBox(props: ActivityChatMessageBoxProps) { - const session = useSession() as any + const session = useLHSession() as any const aiChatBotState = useAIChatBot() as AIChatBotStateTypes const dispatchAIChatBot = useAIChatBotDispatch() as any @@ -328,7 +328,7 @@ type AIMessageProps = { } function AIMessage(props: AIMessageProps) { - const session = useSession() as any + const session = useLHSession() as any const words = props.message.message.split(' ') @@ -378,7 +378,7 @@ const AIMessagePlaceHolder = (props: { activity_uuid: string sendMessage: any }) => { - const session = useSession() as any + const session = useLHSession() as any const [feedbackModal, setFeedbackModal] = React.useState(false) const aiChatBotState = useAIChatBot() as AIChatBotStateTypes diff --git a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx b/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx index 2b173eff..33e96b5b 100644 --- a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx +++ b/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx @@ -20,12 +20,15 @@ import toast from 'react-hot-toast' import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal' import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import { useLHSession } from '@components/Contexts/LHSessionContext' dayjs.extend(relativeTime); function CourseUpdates() { const course = useCourse() as any; - const { data: updates } = useSWR(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`, swrFetcher) + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; + const { data: updates } = useSWR(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`, (url) => swrFetcher(url, access_token)) const [isModelOpen, setIsModelOpen] = React.useState(false) function handleModelOpen() { @@ -71,7 +74,7 @@ function CourseUpdates() { const UpdatesSection = () => { const [selectedView, setSelectedView] = React.useState('list') - const isAdmin = useAdminStatus() as boolean; + const adminStatus = useAdminStatus() ; return (
@@ -80,7 +83,7 @@ const UpdatesSection = () => { Updates
- {isAdmin &&
setSelectedView('new')} className='py-2 px-4 space-x-2 items-center flex cursor-pointer text-xs font-medium hover:bg-gray-200 bg-gray-100 outline outline-1 outline-neutral-200/40'> @@ -98,6 +101,7 @@ const UpdatesSection = () => { const NewUpdateForm = ({ setSelectedView }: any) => { const org = useOrg() as any; const course = useCourse() as any; + const session = useLHSession() as any; const validate = (values: any) => { const errors: any = {} @@ -124,7 +128,7 @@ const NewUpdateForm = ({ setSelectedView }: any) => { course_uuid: course.courseStructure.course_uuid, org_id: org.id } - const res = await createCourseUpdate(body) + const res = await createCourseUpdate(body, session.data?.tokens?.access_token) if (res.status === 200) { toast.success('Update added successfully') setSelectedView('list') @@ -192,23 +196,25 @@ const NewUpdateForm = ({ setSelectedView }: any) => { const UpdatesListView = () => { const course = useCourse() as any; - const isAdmin = useAdminStatus() as boolean; - const { data: updates } = useSWR(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`, swrFetcher) + const adminStatus = useAdminStatus() ; + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; + const { data: updates } = useSWR(`${getAPIUrl()}courses/${course?.courseStructure?.course_uuid}/updates`, (url) => swrFetcher(url, access_token)) return (
- {updates && updates.map((update: any) => ( + {updates && !adminStatus.loading && updates.map((update: any) => (
{update.title} - - {dayjs(update.creation_date).fromNow()} + + {dayjs(update.creation_date).fromNow()}
- {isAdmin && }
+ {adminStatus.isAdmin && !adminStatus.loading && }
{update.content}
))} @@ -223,11 +229,12 @@ const UpdatesListView = () => { } const DeleteUpdateButton = ({ update }: any) => { + const session = useLHSession() as any; const course = useCourse() as any; const org = useOrg() as any; const handleDelete = async () => { - const res = await deleteCourseUpdate(course.courseStructure.course_uuid, update.courseupdate_uuid) + const res = await deleteCourseUpdate(course.courseStructure.course_uuid, update.courseupdate_uuid, session.data?.tokens?.access_token) if (res.status === 200) { toast.success('Update deleted successfully') mutate(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`) diff --git a/apps/web/components/Objects/Editor/ActiveAvatars.tsx b/apps/web/components/Objects/Editor/ActiveAvatars.tsx index 18e6739f..ddab6d8a 100644 --- a/apps/web/components/Objects/Editor/ActiveAvatars.tsx +++ b/apps/web/components/Objects/Editor/ActiveAvatars.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' import UserAvatar from '../UserAvatar' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { getUserAvatarMediaDirectory } from '@services/media/media'; import { getCollaborationServerUrl } from '@services/config/config'; import { useOrg } from '@components/Contexts/OrgContext'; @@ -11,7 +11,7 @@ type ActiveAvatarsProps = { } function ActiveAvatars(props: ActiveAvatarsProps) { - const session = useSession() as any; + const session = useLHSession() as any; const org = useOrg() as any; const [activeUsers, setActiveUsers] = useState({} as any); diff --git a/apps/web/components/Objects/Editor/Editor.tsx b/apps/web/components/Objects/Editor/Editor.tsx index 681e0f7a..1a31c0e4 100644 --- a/apps/web/components/Objects/Editor/Editor.tsx +++ b/apps/web/components/Objects/Editor/Editor.tsx @@ -41,7 +41,7 @@ import html from 'highlight.js/lib/languages/xml' import python from 'highlight.js/lib/languages/python' import java from 'highlight.js/lib/languages/java' import { CourseProvider } from '@components/Contexts/CourseContext' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import AIEditorToolkit from './AI/AIEditorToolkit' import useGetAIFeatures from '@components/AI/Hooks/useGetAIFeatures' import UserAvatar from '../UserAvatar' @@ -65,7 +65,7 @@ interface Editor { } function Editor(props: Editor) { - const session = useSession() as any + const session = useLHSession() as any const dispatchAIEditor = useAIEditorDispatch() as any const aiEditorState = useAIEditor() as AIEditorStateTypes const is_ai_feature_enabled = useGetAIFeatures({ feature: 'editor' }) diff --git a/apps/web/components/Objects/Editor/EditorWrapper.tsx b/apps/web/components/Objects/Editor/EditorWrapper.tsx index fbeafc52..2e3a8f59 100644 --- a/apps/web/components/Objects/Editor/EditorWrapper.tsx +++ b/apps/web/components/Objects/Editor/EditorWrapper.tsx @@ -5,7 +5,7 @@ import { updateActivity } from '@services/courses/activities' import { toast } from 'react-hot-toast' import Toast from '@components/StyledElements/Toast/Toast' import { OrgProvider } from '@components/Contexts/OrgContext' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' // Collaboration import { HocuspocusProvider } from '@hocuspocus/provider' @@ -24,7 +24,7 @@ interface EditorWrapperProps { } function EditorWrapper(props: EditorWrapperProps): JSX.Element { - const session = useSession() as any + const session = useLHSession() as any // Define provider in the state const [provider, setProvider] = React.useState(null); const [thisPageColor, setThisPageColor] = useState(randomColor({ luminosity: 'light' }) as string) diff --git a/apps/web/components/Objects/Menu/Menu.tsx b/apps/web/components/Objects/Menu/Menu.tsx index 212d2b6c..8bf3df13 100644 --- a/apps/web/components/Objects/Menu/Menu.tsx +++ b/apps/web/components/Objects/Menu/Menu.tsx @@ -7,15 +7,15 @@ import MenuLinks from './MenuLinks' import { getOrgLogoMediaDirectory } from '@services/media/media' import useSWR from 'swr' import { swrFetcher } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' +import { useOrg } from '@components/Contexts/OrgContext' export const Menu = (props: any) => { const orgslug = props.orgslug + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const [feedbackModal, setFeedbackModal] = React.useState(false) - const { - data: org, - error: error, - isLoading, - } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, swrFetcher) + const org = useOrg() as any; function closeFeedbackModal() { setFeedbackModal(false) diff --git a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx index 13fd3ce5..2c68a242 100644 --- a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx +++ b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx @@ -15,9 +15,11 @@ import React, { useState } from 'react' import { BarLoader } from 'react-spinners' import { revalidateTags } from '@services/utils/ts/requests' import { useRouter } from 'next/navigation' +import { useLHSession } from '@components/Contexts/LHSessionContext' function CreateCourseModal({ closeModal, orgslug }: any) { const [isSubmitting, setIsSubmitting] = useState(false) + const session = useLHSession() const [name, setName] = React.useState('') const [description, setDescription] = React.useState('') const [learnings, setLearnings] = React.useState('') @@ -71,7 +73,8 @@ function CreateCourseModal({ closeModal, orgslug }: any) { let status = await createNewCourse( orgId, { name, description, tags, visibility }, - thumbnail + thumbnail, + session.data?.tokens?.access_token ) await revalidateTags(['courses'], orgslug) setIsSubmitting(false) @@ -80,9 +83,6 @@ function CreateCourseModal({ closeModal, orgslug }: any) { closeModal() router.refresh() await revalidateTags(['courses'], orgslug) - - // refresh page (FIX for Next.js BUG) - // window.location.reload(); } else { alert('Error creating course, please see console logs') } diff --git a/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx b/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx index bd63fd8d..5197de72 100644 --- a/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx +++ b/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx @@ -3,6 +3,7 @@ import { getAPIUrl } from '@services/config/config' import { createInviteCode, createInviteCodeWithUserGroup } from '@services/organizations/invites' import { swrFetcher } from '@services/utils/ts/requests' import { Shield, Ticket } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import React, { useEffect } from 'react' import toast from 'react-hot-toast' import useSWR, { mutate } from 'swr' @@ -13,6 +14,7 @@ type OrgInviteCodeGenerateProps = { function OrgInviteCodeGenerate(props: OrgInviteCodeGenerateProps) { const org = useOrg() as any + const session = useLHSession() const [usergroup_id, setUsergroup_id] = React.useState(0); const { data: usergroups } = useSWR( org ? `${getAPIUrl()}usergroups/org/${org.id}` : null, @@ -20,7 +22,7 @@ function OrgInviteCodeGenerate(props: OrgInviteCodeGenerateProps) { ) async function createInviteWithUserGroup() { - let res = await createInviteCodeWithUserGroup(org.id, usergroup_id) + let res = await createInviteCodeWithUserGroup(org.id, usergroup_id, session.data?.tokens?.access_token) if (res.status == 200) { mutate(`${getAPIUrl()}orgs/${org.id}/invites`) props.setInvitesModal(false) @@ -30,7 +32,7 @@ function OrgInviteCodeGenerate(props: OrgInviteCodeGenerateProps) { } async function createInvite() { - let res = await createInviteCode(org.id) + let res = await createInviteCode(org.id, session.data?.tokens?.access_token) if (res.status == 200) { mutate(`${getAPIUrl()}orgs/${org.id}/invites`) props.setInvitesModal(false) diff --git a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx index 034a87a5..73ff2554 100644 --- a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx +++ b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx @@ -7,6 +7,7 @@ import { deleteCollection } from '@services/courses/collections' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { revalidateTags } from '@services/utils/ts/requests' import { X } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import Link from 'next/link' import { useRouter } from 'next/navigation' import React from 'react' @@ -74,9 +75,10 @@ function CollectionThumbnail(props: PropsType) { const CollectionAdminEditsArea = (props: any) => { const router = useRouter() + const session = useLHSession() ; const deleteCollectionUI = async (collectionId: number) => { - await deleteCollection(collectionId) + await deleteCollection(collectionId, session.data?.tokens?.access_token) await revalidateTags(['collections'], props.orgslug) // reload the page router.refresh() diff --git a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx index 4899e131..c9373eb5 100644 --- a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx +++ b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx @@ -7,6 +7,7 @@ import { deleteCourseFromBackend } from '@services/courses/courses' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { revalidateTags } from '@services/utils/ts/requests' import { Settings, X } from 'lucide-react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import Link from 'next/link' import { useRouter } from 'next/navigation' import React, { useEffect } from 'react' @@ -24,15 +25,16 @@ function removeCoursePrefix(course_uuid: string) { function CourseThumbnail(props: PropsType) { const router = useRouter() const org = useOrg() as any + const session = useLHSession(); async function deleteCourses(course_uuid: any) { - await deleteCourseFromBackend(course_uuid) + await deleteCourseFromBackend(course_uuid, session.data?.tokens?.access_token) await revalidateTags(['courses'], props.orgslug) router.refresh() } - useEffect(() => {}, [org]) + useEffect(() => { }, [org]) return (
@@ -92,8 +94,8 @@ const AdminEditsArea = (props: { href={getUriWithOrg( props.orgSlug, '/dash/courses/course/' + - removeCoursePrefix(props.courseId) + - '/general' + removeCoursePrefix(props.courseId) + + '/general' )} >
(undefined) @@ -33,7 +35,7 @@ function Activity(props: any) { >(undefined) async function removeActivity() { - await deleteActivity(props.activity.id) + await deleteActivity(props.activity.id, session.data?.tokens?.access_token) mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}`) await revalidateTags(['courses'], props.orgslug) router.refresh() @@ -52,7 +54,7 @@ function Activity(props: any) { content: props.activity.content, } - await updateActivity(modifiedActivityCopy, activityId) + await updateActivity(modifiedActivityCopy, activityId, session.data?.tokens?.access_token) await mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}`) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx b/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx index f0fe7bf5..121eb838 100644 --- a/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx +++ b/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx @@ -9,6 +9,7 @@ import { updateChapter } from '@services/courses/chapters' import { mutate } from 'swr' import { getAPIUrl } from '@services/config/config' import { revalidateTags } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' interface ModifiedChapterInterface { chapterId: string @@ -17,6 +18,7 @@ interface ModifiedChapterInterface { function Chapter(props: any) { const router = useRouter() + const session = useLHSession() as any; const [modifiedChapter, setModifiedChapter] = React.useState< ModifiedChapterInterface | undefined >(undefined) @@ -30,7 +32,7 @@ function Chapter(props: any) { let modifiedChapterCopy = { name: modifiedChapter.chapterName, } - await updateChapter(chapterId, modifiedChapterCopy) + await updateChapter(chapterId, modifiedChapterCopy, session.data?.tokens?.access_token) await mutate(`${getAPIUrl()}chapters/course/${props.course_uuid}/meta`) await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Pages/Trail/TrailCourseElement.tsx b/apps/web/components/Pages/Trail/TrailCourseElement.tsx index 4303e458..dfbcd456 100644 --- a/apps/web/components/Pages/Trail/TrailCourseElement.tsx +++ b/apps/web/components/Pages/Trail/TrailCourseElement.tsx @@ -4,6 +4,7 @@ import { getAPIUrl, getUriWithOrg } from '@services/config/config' import { removeCourse } from '@services/courses/activity' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { revalidateTags } from '@services/utils/ts/requests' +import { useLHSession } from '@components/Contexts/LHSessionContext' import Link from 'next/link' import { useRouter } from 'next/navigation' import { useEffect } from 'react' @@ -17,6 +18,8 @@ interface TrailCourseElementProps { function TrailCourseElement(props: TrailCourseElementProps) { const org = useOrg() as any + const session = useLHSession() as any; + const access_token = session?.data?.tokens?.access_token; const courseid = props.course.course_uuid.replace('course_', '') const course = props.course const router = useRouter() @@ -29,7 +32,7 @@ function TrailCourseElement(props: TrailCourseElementProps) { async function quitCourse(course_uuid: string) { // Close activity - let activity = await removeCourse(course_uuid, props.orgslug) + let activity = await removeCourse(course_uuid, props.orgslug,access_token) // Mutate course await revalidateTags(['courses'], props.orgslug) router.refresh() diff --git a/apps/web/components/Security/AdminAuthorization.tsx b/apps/web/components/Security/AdminAuthorization.tsx index 1f00c31a..05a7737e 100644 --- a/apps/web/components/Security/AdminAuthorization.tsx +++ b/apps/web/components/Security/AdminAuthorization.tsx @@ -1,15 +1,14 @@ -'use client' -import { useOrg } from '@components/Contexts/OrgContext' -import { useSession } from 'next-auth/react' -import useAdminStatus from '@components/Hooks/useAdminStatus' -import { usePathname, useRouter } from 'next/navigation' -import React from 'react' +'use client'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import { useOrg } from '@components/Contexts/OrgContext'; +import { useLHSession } from '@components/Contexts/LHSessionContext'; +import useAdminStatus from '@components/Hooks/useAdminStatus'; +import { usePathname, useRouter } from 'next/navigation'; type AuthorizationProps = { - children: React.ReactNode - // Authorize components rendering or page rendering - authorizationMode: 'component' | 'page' -} + children: React.ReactNode; + authorizationMode: 'component' | 'page'; +}; const ADMIN_PATHS = [ '/dash/org/*', @@ -19,106 +18,71 @@ const ADMIN_PATHS = [ '/dash/courses/*', '/dash/courses', '/dash/org/settings/general', -] +]; -function AdminAuthorization(props: AuthorizationProps) { - const session = useSession() as any - const org = useOrg() as any - const pathname = usePathname() - const router = useRouter() +const AdminAuthorization: React.FC = ({ children, authorizationMode }) => { + const session = useLHSession() as any; + const pathname = usePathname(); + const router = useRouter(); + const { isAdmin, loading } = useAdminStatus() as any + const [isAuthorized, setIsAuthorized] = useState(false); - // States - const [isLoading, setIsLoading] = React.useState(true) - const [isAuthorized, setIsAuthorized] = React.useState(false) + const isUserAuthenticated = useMemo(() => session.status === 'authenticated', [session.status]); - // Verify if the user is authenticated - const isUserAuthenticated = () => { - if (session.status === 'authenticated') { - return true - } else { - return false + const checkPathname = useCallback((pattern: string, pathname: string) => { + const regexPattern = new RegExp(`^${pattern.replace(/\//g, '\\/').replace(/\*/g, '.*')}$`); + return regexPattern.test(pathname); + }, []); + + const isAdminPath = useMemo(() => ADMIN_PATHS.some(path => checkPathname(path, pathname)), [pathname, checkPathname]); + + const authorizeUser = useCallback(() => { + if (loading) { + return; // Wait until the admin status is determined } - } - // Verify if the user is an Admin (1), Maintainer (2) or Member (3) of the organization - const isUserAdmin = useAdminStatus() + if (!isUserAuthenticated) { + router.push('/login'); + return; + } - function checkPathname(pattern: string, pathname: string) { - // Escape special characters in the pattern and replace '*' with a regex pattern - const regexPattern = new RegExp( - `^${pattern.replace(/\//g, '\\/').replace(/\*/g, '.*')}$` - ) - - // Test if the pathname matches the regex pattern - const isMatch = regexPattern.test(pathname) - - return isMatch - } - - const Authorize = () => { - if (props.authorizationMode === 'page') { - // Check if user is in an admin path - if (ADMIN_PATHS.some((path) => checkPathname(path, pathname))) { - if (isUserAuthenticated()) { - // Check if the user is an Admin - if (isUserAdmin) { - setIsAuthorized(true) - } else { - setIsAuthorized(false) - router.push('/dash') - } + if (authorizationMode === 'page') { + if (isAdminPath) { + if (isAdmin) { + setIsAuthorized(true); } else { - router.push('/login') + setIsAuthorized(false); + router.push('/dash'); } } else { - if (isUserAuthenticated()) { - setIsAuthorized(true) - } else { - setIsAuthorized(false) - router.push('/login') - } + setIsAuthorized(true); } + } else if (authorizationMode === 'component') { + setIsAuthorized(isAdmin); } + }, [loading, isUserAuthenticated, isAdmin, isAdminPath, authorizationMode, router]); - if (props.authorizationMode === 'component') { - // Component mode - if (isUserAuthenticated() && isUserAdmin) { - setIsAuthorized(true) - } else { - setIsAuthorized(false) - } - } + useEffect(() => { + authorizeUser(); + }, [authorizeUser]); + + if (loading) { + return ( +
+

Loading...

+
+ ); } - React.useEffect(() => { - if (session.status == 'loading') { - return - } + if (authorizationMode === 'page' && !isAuthorized) { + return ( +
+

You are not authorized to access this page

+
+ ); + } - Authorize() - setIsLoading(false) - }, [session, org, pathname]) + return <>{isAuthorized && children}; +}; - return ( - <> - {props.authorizationMode === 'component' && - isAuthorized === true && - props.children} - {props.authorizationMode === 'page' && - isAuthorized === true && - !isLoading && - props.children} - {props.authorizationMode === 'page' && - isAuthorized === false && - !isLoading && ( -
-

- You are not authorized to access this page -

-
- )} - - ) -} - -export default AdminAuthorization +export default AdminAuthorization; diff --git a/apps/web/components/Security/AuthenticatedClientElement.tsx b/apps/web/components/Security/AuthenticatedClientElement.tsx index 66ce5660..ec65e5b3 100644 --- a/apps/web/components/Security/AuthenticatedClientElement.tsx +++ b/apps/web/components/Security/AuthenticatedClientElement.tsx @@ -1,6 +1,6 @@ 'use client' import React from 'react' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' import { useOrg } from '@components/Contexts/OrgContext' interface AuthenticatedClientElementProps { @@ -20,7 +20,7 @@ export const AuthenticatedClientElement = ( props: AuthenticatedClientElementProps ) => { const [isAllowed, setIsAllowed] = React.useState(false) - const session = useSession() as any + const session = useLHSession() as any const org = useOrg() as any function isUserAllowed( @@ -49,13 +49,13 @@ export const AuthenticatedClientElement = ( } function check() { - if (session.status == 'authenticated') { + if (session.status == 'unauthenticated') { setIsAllowed(false) return } else { if (props.checkMethod === 'authentication') { setIsAllowed(session.status == 'authenticated') - } else if (props.checkMethod === 'roles' && session.status == 'authenticated') { + } else if (props.checkMethod === 'roles' ) { return setIsAllowed( isUserAllowed( session?.data?.roles, @@ -74,7 +74,7 @@ export const AuthenticatedClientElement = ( } check() - }, [session, org]) + }, [session.data, org]) return <>{isAllowed && props.children} } diff --git a/apps/web/components/Security/HeaderProfileBox.tsx b/apps/web/components/Security/HeaderProfileBox.tsx index 3c932bd1..f6427c0f 100644 --- a/apps/web/components/Security/HeaderProfileBox.tsx +++ b/apps/web/components/Security/HeaderProfileBox.tsx @@ -5,10 +5,10 @@ import Link from 'next/link' import { Settings } from 'lucide-react' import UserAvatar from '@components/Objects/UserAvatar' import useAdminStatus from '@components/Hooks/useAdminStatus' -import { useSession } from 'next-auth/react' +import { useLHSession } from '@components/Contexts/LHSessionContext' export const HeaderProfileBox = () => { - const session = useSession() as any + const session = useLHSession() as any const isUserAdmin = useAdminStatus() as any useEffect(() => { diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 62f82e90..8fa72ec5 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -1549,6 +1549,7 @@ packages: "@typescript-eslint/visitor-keys": 6.21.0 debug: 4.3.4 eslint: 8.57.0 + optionalDependencies: typescript: 5.4.4 transitivePeerDependencies: - supports-color @@ -1570,6 +1571,7 @@ packages: minimatch: 9.0.3 semver: 7.6.0 ts-api-utils: 1.3.0(typescript@5.4.4) + optionalDependencies: typescript: 5.4.4 transitivePeerDependencies: - supports-color @@ -1713,8 +1715,8 @@ packages: dependencies: possible-typed-array-names: 1.0.0 - avvvatars-react@0.4.2(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0): - dependencies: + ? avvvatars-react@0.4.2(csstype@3.1.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + : dependencies: goober: 2.1.14(csstype@3.1.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2070,11 +2072,12 @@ packages: "@typescript-eslint/parser": 6.21.0(eslint@8.57.0)(typescript@5.4.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + optionalDependencies: typescript: 5.4.4 transitivePeerDependencies: - eslint-import-resolver-webpack @@ -2093,8 +2096,8 @@ packages: debug: 4.3.4 enhanced-resolve: 5.16.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.3 is-core-module: 2.13.1 @@ -2109,9 +2112,11 @@ packages: : dependencies: "@typescript-eslint/parser": 6.21.0(eslint@8.57.0)(typescript@5.4.4) debug: 3.2.7 + optionalDependencies: + "@typescript-eslint/parser": 6.21.0(eslint@8.57.0)(typescript@5.4.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -2126,7 +2131,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -2136,6 +2141,8 @@ packages: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + "@typescript-eslint/parser": 6.21.0(eslint@8.57.0)(typescript@5.4.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -2324,10 +2331,8 @@ packages: fraction.js@4.3.7: {} - framer-motion@10.18.0(react-dom@18.2.0)(react@18.2.0): + framer-motion@10.18.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) tslib: 2.6.2 optionalDependencies: "@emotion/is-prop-valid": 0.8.8 @@ -2985,8 +2990,9 @@ packages: postcss-load-config@4.0.2(postcss@8.4.38): dependencies: lilconfig: 3.1.1 - postcss: 8.4.38 yaml: 2.4.1 + optionalDependencies: + postcss: 8.4.38 postcss-nested@6.0.1(postcss@8.4.38): dependencies: @@ -3146,12 +3152,12 @@ packages: randomcolor@0.6.2: {} - re-resizable@6.9.11(react-dom@18.2.0)(react@18.2.0): + re-resizable@6.9.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0): + react-beautiful-dnd@13.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: "@babel/runtime": 7.24.4 css-box-model: 1.2.1 @@ -3159,7 +3165,7 @@ packages: raf-schd: 4.0.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0) + react-redux: 7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) redux: 4.2.1 use-memo-one: 1.1.3(react@18.2.0) transitivePeerDependencies: @@ -3178,8 +3184,8 @@ packages: react-fast-compare@2.0.4: {} - react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0): - dependencies: + ? react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + : dependencies: goober: 2.1.14(csstype@3.1.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -3196,7 +3202,7 @@ packages: prop-types: 15.8.1 react: 18.2.0 - react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0): + react-redux@7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: "@babel/runtime": 7.24.4 "@types/react-redux": 7.1.33 @@ -3204,8 +3210,9 @@ packages: loose-envify: 1.4.0 prop-types: 15.8.1 react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) react-is: 17.0.2 + optionalDependencies: + react-dom: 18.2.0(react@18.2.0) react-remove-scroll-bar@2.3.6(@types/react@18.2.74)(react@18.2.0): dependencies: @@ -3213,6 +3220,8 @@ packages: react: 18.2.0 react-style-singleton: 2.2.1(@types/react@18.2.74)(react@18.2.0) tslib: 2.6.2 + optionalDependencies: + "@types/react": 18.2.74 react-remove-scroll@2.5.5(@types/react@18.2.74)(react@18.2.0): dependencies: @@ -3223,8 +3232,10 @@ packages: tslib: 2.6.2 use-callback-ref: 1.3.2(@types/react@18.2.74)(react@18.2.0) use-sidecar: 1.1.2(@types/react@18.2.74)(react@18.2.0) + optionalDependencies: + "@types/react": 18.2.74 - react-spinners@0.13.8(react-dom@18.2.0)(react@18.2.0): + react-spinners@0.13.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -3236,6 +3247,8 @@ packages: invariant: 2.2.4 react: 18.2.0 tslib: 2.6.2 + optionalDependencies: + "@types/react": 18.2.74 react-youtube@10.1.0(react@18.2.0): dependencies: @@ -3485,7 +3498,7 @@ packages: strip-json-comments@3.1.1: {} - styled-components@6.1.8(react-dom@18.2.0)(react@18.2.0): + styled-components@6.1.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: "@emotion/is-prop-valid": 1.2.1 "@emotion/unitless": 0.8.0 @@ -3672,6 +3685,8 @@ packages: "@types/react": 18.2.74 react: 18.2.0 tslib: 2.6.2 + optionalDependencies: + "@types/react": 18.2.74 use-memo-one@1.1.3(react@18.2.0): dependencies: @@ -3683,6 +3698,8 @@ packages: detect-node-es: 1.1.0 react: 18.2.0 tslib: 2.6.2 + optionalDependencies: + "@types/react": 18.2.74 use-sync-external-store@1.2.0(react@18.2.0): dependencies: diff --git a/apps/web/services/courses/activities.ts b/apps/web/services/courses/activities.ts index 1c8748a9..1fa050f0 100644 --- a/apps/web/services/courses/activities.ts +++ b/apps/web/services/courses/activities.ts @@ -1,18 +1,22 @@ import { getAPIUrl } from '@services/config/config' import { - RequestBody, - RequestBodyForm, + RequestBodyFormWithAuthHeader, RequestBodyWithAuthHeader, } from '@services/utils/ts/requests' -export async function createActivity(data: any, chapter_id: any, org_id: any) { +export async function createActivity( + data: any, + chapter_id: any, + org_id: any, + access_token: string +) { data.content = {} // remove chapter_id from data delete data.chapterId const result = await fetch( `${getAPIUrl()}activities/?coursechapter_id=${chapter_id}&org_id=${org_id}`, - RequestBody('POST', data, null) + RequestBodyWithAuthHeader('POST', data, null, access_token) ) const res = await result.json() return res @@ -22,7 +26,8 @@ export async function createFileActivity( file: File, type: string, data: any, - chapter_id: any + chapter_id: any, + access_token: string ) { // Send file thumbnail as form data const formData = new FormData() @@ -44,7 +49,7 @@ export async function createFileActivity( const result: any = await fetch( endpoint, - RequestBodyForm('POST', formData, null) + RequestBodyFormWithAuthHeader('POST', formData, null, access_token) ) const res = await result.json() return res @@ -53,7 +58,8 @@ export async function createFileActivity( export async function createExternalVideoActivity( data: any, activity: any, - chapter_id: any + chapter_id: any, + access_token: string ) { // add coursechapter_id to data data.chapter_id = chapter_id @@ -61,25 +67,29 @@ export async function createExternalVideoActivity( const result = await fetch( `${getAPIUrl()}activities/external_video`, - RequestBody('POST', data, null) + RequestBodyWithAuthHeader('POST', data, null, access_token) ) const res = await result.json() return res } -export async function getActivity(activity_id: any, next: any) { +export async function getActivity( + activity_id: any, + next: any, + access_token: string +) { const result = await fetch( `${getAPIUrl()}activities/${activity_id}`, - RequestBody('GET', null, next) + RequestBodyWithAuthHeader('GET', null, next, access_token) ) const res = await result.json() return res } -export async function deleteActivity(activity_id: any) { +export async function deleteActivity(activity_id: any, access_token: string) { const result = await fetch( `${getAPIUrl()}activities/${activity_id}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null, access_token) ) const res = await result.json() return res @@ -98,10 +108,14 @@ export async function getActivityWithAuthHeader( return res } -export async function updateActivity(data: any, activity_uuid: string) { +export async function updateActivity( + data: any, + activity_uuid: string, + access_token: string +) { const result = await fetch( `${getAPIUrl()}activities/${activity_uuid}`, - RequestBody('PUT', data, null) + RequestBodyWithAuthHeader('PUT', data, null, access_token) ) const res = await result.json() return res diff --git a/apps/web/services/courses/activity.ts b/apps/web/services/courses/activity.ts index 8b0e7838..b571741d 100644 --- a/apps/web/services/courses/activity.ts +++ b/apps/web/services/courses/activity.ts @@ -1,4 +1,4 @@ -import { RequestBody, errorHandling } from '@services/utils/ts/requests' +import { RequestBody, RequestBodyWithAuthHeader, errorHandling } from '@services/utils/ts/requests' import { getAPIUrl } from '@services/config/config' /* @@ -6,19 +6,19 @@ import { getAPIUrl } from '@services/config/config' GET requests are called from the frontend using SWR (https://swr.vercel.app/) */ -export async function startCourse(course_uuid: string, org_slug: string) { +export async function startCourse(course_uuid: string, org_slug: string,access_token:any) { const result: any = await fetch( `${getAPIUrl()}trail/add_course/${course_uuid}`, - RequestBody('POST', null, null) + RequestBodyWithAuthHeader('POST', null, null,access_token) ) const res = await errorHandling(result) return res } -export async function removeCourse(course_uuid: string, org_slug: string) { +export async function removeCourse(course_uuid: string, org_slug: string,access_token:any) { const result: any = await fetch( `${getAPIUrl()}trail/remove_course/${course_uuid}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null,access_token) ) const res = await errorHandling(result) return res @@ -27,11 +27,11 @@ export async function removeCourse(course_uuid: string, org_slug: string) { export async function markActivityAsComplete( org_slug: string, course_uuid: string, - activity_uuid: string + activity_uuid: string,access_token:any ) { const result: any = await fetch( `${getAPIUrl()}trail/add_activity/${activity_uuid}`, - RequestBody('POST', null, null) + RequestBodyWithAuthHeader('POST', null, null,access_token) ) const res = await errorHandling(result) return res diff --git a/apps/web/services/courses/chapters.ts b/apps/web/services/courses/chapters.ts index 58ed5d1f..f04c5b37 100644 --- a/apps/web/services/courses/chapters.ts +++ b/apps/web/services/courses/chapters.ts @@ -1,6 +1,9 @@ import { OrderPayload } from '@components/Dashboard/Course/EditCourseStructure/EditCourseStructure' import { getAPIUrl } from '@services/config/config' -import { RequestBody, errorHandling } from '@services/utils/ts/requests' +import { + RequestBodyWithAuthHeader, + errorHandling, +} from '@services/utils/ts/requests' /* This file includes only POST, PUT, DELETE requests @@ -8,28 +11,40 @@ import { RequestBody, errorHandling } from '@services/utils/ts/requests' */ //TODO : depreciate this function -export async function getCourseChaptersMetadata(course_uuid: any, next: any) { +export async function getCourseChaptersMetadata( + course_uuid: any, + next: any, + access_token: any +) { const result = await fetch( `${getAPIUrl()}chapters/meta/course_${course_uuid}`, - RequestBody('GET', null, next) + RequestBodyWithAuthHeader('GET', null, next, access_token) ) const res = await errorHandling(result) return res } -export async function updateChaptersMetadata(course_uuid: any, data: any) { +export async function updateChaptersMetadata( + course_uuid: any, + data: any, + access_token: any +) { const result: any = await fetch( `${getAPIUrl()}chapters/course/course_${course_uuid}/order`, - RequestBody('PUT', data, null) + RequestBodyWithAuthHeader('PUT', data, null, access_token) ) const res = await errorHandling(result) return res } -export async function updateChapter(coursechapter_id: any, data: any) { +export async function updateChapter( + coursechapter_id: any, + data: any, + access_token: any +) { const result: any = await fetch( `${getAPIUrl()}chapters/${coursechapter_id}`, - RequestBody('PUT', data, null) + RequestBodyWithAuthHeader('PUT', data, null, access_token) ) const res = await errorHandling(result) return res @@ -37,30 +52,31 @@ export async function updateChapter(coursechapter_id: any, data: any) { export async function updateCourseOrderStructure( course_uuid: any, - data: OrderPayload + data: OrderPayload, + access_token: any ) { const result: any = await fetch( `${getAPIUrl()}chapters/course/${course_uuid}/order`, - RequestBody('PUT', data, null) + RequestBodyWithAuthHeader('PUT', data, null, access_token) ) const res = await errorHandling(result) return res } -export async function createChapter(data: any) { +export async function createChapter(data: any, access_token: any) { const result: any = await fetch( `${getAPIUrl()}chapters/`, - RequestBody('POST', data, null) + RequestBodyWithAuthHeader('POST', data, null, access_token) ) const res = await errorHandling(result) return res } -export async function deleteChapter(coursechapter_id: any) { +export async function deleteChapter(coursechapter_id: any, access_token: any) { const result: any = await fetch( `${getAPIUrl()}chapters/${coursechapter_id}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null, access_token) ) const res = await errorHandling(result) return res diff --git a/apps/web/services/courses/collections.ts b/apps/web/services/courses/collections.ts index 84575309..5439f61a 100644 --- a/apps/web/services/courses/collections.ts +++ b/apps/web/services/courses/collections.ts @@ -1,6 +1,5 @@ import { getAPIUrl } from '../config/config' import { - RequestBody, RequestBodyWithAuthHeader, errorHandling, } from '@services/utils/ts/requests' @@ -10,36 +9,30 @@ import { GET requests are called from the frontend using SWR (https://swr.vercel.app/) */ -export async function deleteCollection(collection_uuid: any) { +export async function deleteCollection( + collection_uuid: any, + access_token: any +) { const result: any = await fetch( `${getAPIUrl()}collections/${collection_uuid}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null, access_token) ) const res = await errorHandling(result) return res } // Create a new collection -export async function createCollection(collection: any) { +export async function createCollection(collection: any, access_token: any) { + console.log(collection) const result: any = await fetch( `${getAPIUrl()}collections/`, - RequestBody('POST', collection, null) + RequestBodyWithAuthHeader('POST', collection, null, access_token) ) const res = await errorHandling(result) return res } -// Get a colletion by id -export async function getCollectionById(collection_uuid: any) { - const result: any = await fetch( - `${getAPIUrl()}collections/${collection_uuid}`, - { next: { revalidate: 10 } } - ) - const res = await errorHandling(result) - return res -} - -export async function getCollectionByIdWithAuthHeader( +export async function getCollectionById( collection_uuid: any, access_token: string, next: any @@ -52,17 +45,7 @@ export async function getCollectionByIdWithAuthHeader( return res } -// Get collections -// TODO : add per org filter -export async function getOrgCollections() { - const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, { - next: { revalidate: 10 }, - }) - const res = await errorHandling(result) - return res -} - -export async function getOrgCollectionsWithAuthHeader( +export async function getOrgCollections( org_id: string, access_token: string, next: any diff --git a/apps/web/services/courses/courses.ts b/apps/web/services/courses/courses.ts index 3e32968a..40dbeaf8 100644 --- a/apps/web/services/courses/courses.ts +++ b/apps/web/services/courses/courses.ts @@ -2,6 +2,7 @@ import { getAPIUrl } from '@services/config/config' import { RequestBody, RequestBodyForm, + RequestBodyFormWithAuthHeader, RequestBodyWithAuthHeader, errorHandling, getResponseMetadata, @@ -12,19 +13,10 @@ import { GET requests are called from the frontend using SWR (https://swr.vercel.app/) */ -export async function getOrgCourses(org_id: number, next: any) { - const result: any = await fetch( - `${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, - RequestBody('GET', null, next) - ) - const res = await errorHandling(result) - return res -} - -export async function getOrgCoursesWithAuthHeader( +export async function getOrgCourses( org_id: number, next: any, - access_token: string + access_token: any ) { const result: any = await fetch( `${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, @@ -34,7 +26,7 @@ export async function getOrgCoursesWithAuthHeader( return res } -export async function getCourseMetadataWithAuthHeader( +export async function getCourseMetadata( course_uuid: any, next: any, access_token: string @@ -47,30 +39,30 @@ export async function getCourseMetadataWithAuthHeader( return res } -export async function updateCourse(course_uuid: any, data: any) { +export async function updateCourse(course_uuid: any, data: any, access_token:any) { const result: any = await fetch( `${getAPIUrl()}courses/${course_uuid}`, - RequestBody('PUT', data, null) + RequestBodyWithAuthHeader('PUT', data, null,access_token) ) const res = await errorHandling(result) return res } -export async function getCourse(course_uuid: string, next: any) { +export async function getCourse(course_uuid: string, next: any, access_token:any) { const result: any = await fetch( `${getAPIUrl()}courses/${course_uuid}`, - RequestBody('GET', null, next) + RequestBodyWithAuthHeader('GET', null, next,access_token) ) const res = await errorHandling(result) return res } -export async function updateCourseThumbnail(course_uuid: any, thumbnail: any) { +export async function updateCourseThumbnail(course_uuid: any, thumbnail: any, access_token:any) { const formData = new FormData() formData.append('thumbnail', thumbnail) const result: any = await fetch( `${getAPIUrl()}courses/${course_uuid}/thumbnail`, - RequestBodyForm('PUT', formData, null) + RequestBodyFormWithAuthHeader('PUT', formData, null,access_token) ) const res = await getResponseMetadata(result) return res @@ -79,7 +71,8 @@ export async function updateCourseThumbnail(course_uuid: any, thumbnail: any) { export async function createNewCourse( org_id: string, course_body: any, - thumbnail: any + thumbnail: any, + access_token: any ) { // Send file thumbnail as form data const formData = new FormData() @@ -96,16 +89,16 @@ export async function createNewCourse( const result = await fetch( `${getAPIUrl()}courses/?org_id=${org_id}`, - RequestBodyForm('POST', formData, null) + RequestBodyFormWithAuthHeader('POST', formData, null, access_token) ) const res = await errorHandling(result) return res } -export async function deleteCourseFromBackend(course_uuid: any) { +export async function deleteCourseFromBackend(course_uuid: any, access_token:any) { const result: any = await fetch( `${getAPIUrl()}courses/${course_uuid}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null,access_token) ) const res = await errorHandling(result) return res diff --git a/apps/web/services/courses/updates.ts b/apps/web/services/courses/updates.ts index 8b515106..061d6a33 100644 --- a/apps/web/services/courses/updates.ts +++ b/apps/web/services/courses/updates.ts @@ -1,10 +1,14 @@ import { getAPIUrl } from '@services/config/config' -import { RequestBody, getResponseMetadata } from '@services/utils/ts/requests' +import { + RequestBody, + RequestBodyWithAuthHeader, + getResponseMetadata, +} from '@services/utils/ts/requests' -export async function createCourseUpdate(body: any) { +export async function createCourseUpdate(body: any, access_token: string) { const result: any = await fetch( `${getAPIUrl()}courses/${body.course_uuid}/updates`, - RequestBody('POST', body, null) + RequestBodyWithAuthHeader('POST', body, null, access_token) ) const res = await getResponseMetadata(result) return res @@ -12,11 +16,12 @@ export async function createCourseUpdate(body: any) { export async function deleteCourseUpdate( course_uuid: string, - update_uuid: number + update_uuid: number, + access_token: string ) { const result: any = await fetch( `${getAPIUrl()}courses/${course_uuid}/update/${update_uuid}`, - RequestBody('DELETE', null, null) + RequestBodyWithAuthHeader('DELETE', null, null, access_token) ) const res = await getResponseMetadata(result) return res diff --git a/apps/web/services/utils/ts/requests.ts b/apps/web/services/utils/ts/requests.ts index 2947964a..1ac8fd8b 100644 --- a/apps/web/services/utils/ts/requests.ts +++ b/apps/web/services/utils/ts/requests.ts @@ -32,7 +32,7 @@ export const RequestBodyWithAuthHeader = ( headers: HeadersConfig, redirect: 'follow', credentials: 'include', - body: data, + body: (method === 'POST' || method === 'PUT') ? JSON.stringify(data) : null, // Next.js next: next, } @@ -41,6 +41,27 @@ export const RequestBodyWithAuthHeader = ( export const RequestBodyForm = (method: string, data: any, next: any) => { let HeadersConfig = new Headers({}) + let options: any = { + method: method, + headers: HeadersConfig, + redirect: 'follow', + credentials: 'include', + body: (method === 'POST' || method === 'PUT') ? JSON.stringify(data) : null, + // Next.js + next: next, + } + return options +} + +export const RequestBodyFormWithAuthHeader = ( + method: string, + data: any, + next: any, + access_token: string +) => { + let HeadersConfig = new Headers({ + Authorization: `Bearer ${access_token}`, + }) let options: any = { method: method, headers: HeadersConfig, @@ -53,9 +74,13 @@ export const RequestBodyForm = (method: string, data: any, next: any) => { return options } -export const swrFetcher = async (url: string) => { +export const swrFetcher = async (url: string, token?: string) => { // Create the request options - let HeadersConfig = new Headers({ 'Content-Type': 'application/json' }) + let HeadersConfig = new Headers( + token + ? { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` } + : { 'Content-Type': 'application/json' } + ) let options: any = { method: 'GET', headers: HeadersConfig, diff --git a/apps/web/types/next-auth.d.ts b/apps/web/types/next-auth.d.ts new file mode 100644 index 00000000..5287cbc2 --- /dev/null +++ b/apps/web/types/next-auth.d.ts @@ -0,0 +1,15 @@ +import type { NextAuthOptions } from 'next-auth/index'; + +// next-auth.d.ts +declare module 'next-auth' { + interface Session { + user: any | undefined + roles?: string[] | undefined + tokens?: + | { + access_token?: string | undefined + refresh_token?: string | undefined + } + | undefined + } +} diff --git a/package.json b/package.json index e80202c6..7174e3cc 100644 --- a/package.json +++ b/package.json @@ -12,5 +12,5 @@ "prettier": "^3.2.5", "turbo": "^1.13.2" }, - "packageManager": "pnpm@8.6.10" + "packageManager": "pnpm@9.0.6" }