mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: middleware support for oauth
This commit is contained in:
parent
52f2235942
commit
5ca1ba75e1
15 changed files with 212 additions and 43 deletions
2
apps/web/.gitignore
vendored
2
apps/web/.gitignore
vendored
|
|
@ -43,3 +43,5 @@ next.config.original.js
|
||||||
|
|
||||||
# Sentry Config File
|
# Sentry Config File
|
||||||
.sentryclirc
|
.sentryclirc
|
||||||
|
|
||||||
|
certificates
|
||||||
70
apps/web/app/auth/cookies.ts
Normal file
70
apps/web/app/auth/cookies.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { LEARNHOUSE_TOP_DOMAIN } from '@services/config/config'
|
||||||
|
|
||||||
|
const cookiePrefix = '__LRN-'
|
||||||
|
const cookieDomain =
|
||||||
|
LEARNHOUSE_TOP_DOMAIN == `.${LEARNHOUSE_TOP_DOMAIN}`
|
||||||
|
const cookieSecure = LEARNHOUSE_TOP_DOMAIN == 'localhost' ? true : true
|
||||||
|
const cookieSameSite = LEARNHOUSE_TOP_DOMAIN == 'localhost' ? 'lax' : 'None'
|
||||||
|
|
||||||
|
export const cookiesOptions = {
|
||||||
|
sessionToken: {
|
||||||
|
name: `__Secure-next-auth.session-token`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
callbackUrl: {
|
||||||
|
name: `__Secure-next-auth.callback-url`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
csrfToken: {
|
||||||
|
name: `__Host-next-auth.csrf-token`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkceCodeVerifier: {
|
||||||
|
name: `${cookiePrefix}next-auth.pkce.code_verifier`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
name: `${cookiePrefix}next-auth.state`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nonce: {
|
||||||
|
name: `${cookiePrefix}next-auth.nonce`,
|
||||||
|
options: {
|
||||||
|
domain: cookieDomain,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: cookieSameSite,
|
||||||
|
path: '/',
|
||||||
|
secure: cookieSecure,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,16 @@ import {
|
||||||
loginAndGetToken,
|
loginAndGetToken,
|
||||||
loginWithOAuthToken,
|
loginWithOAuthToken,
|
||||||
} from '@services/auth/auth'
|
} from '@services/auth/auth'
|
||||||
|
import { LEARNHOUSE_TOP_DOMAIN, getUriWithOrg } from '@services/config/config'
|
||||||
import { getResponseMetadata } from '@services/utils/ts/requests'
|
import { getResponseMetadata } from '@services/utils/ts/requests'
|
||||||
import CredentialsProvider from 'next-auth/providers/credentials'
|
import CredentialsProvider from 'next-auth/providers/credentials'
|
||||||
import GoogleProvider from 'next-auth/providers/google'
|
import GoogleProvider from 'next-auth/providers/google'
|
||||||
|
import { cookiesOptions } from './cookies'
|
||||||
|
|
||||||
|
const isDevEnv = LEARNHOUSE_TOP_DOMAIN == 'localhost' ? true : false
|
||||||
|
|
||||||
export const nextAuthOptions = {
|
export const nextAuthOptions = {
|
||||||
|
debug: true,
|
||||||
providers: [
|
providers: [
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
// The name to display on the sign in form (e.g. 'Sign in with...')
|
// The name to display on the sign in form (e.g. 'Sign in with...')
|
||||||
|
|
@ -41,6 +46,24 @@ export const nextAuthOptions = {
|
||||||
clientSecret: process.env.LEARNHOUSE_GOOGLE_CLIENT_SECRET || '',
|
clientSecret: process.env.LEARNHOUSE_GOOGLE_CLIENT_SECRET || '',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
pages: {
|
||||||
|
signIn: getUriWithOrg('auth', '/'),
|
||||||
|
verifyRequest: getUriWithOrg('auth', '/'),
|
||||||
|
error: getUriWithOrg('auth', '/'), // Error code passed in query string as ?error=
|
||||||
|
},
|
||||||
|
cookies: {
|
||||||
|
sessionToken: {
|
||||||
|
name: `${!isDevEnv ? '__Secure-' : ''}next-auth.session-token`,
|
||||||
|
options: {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
path: '/',
|
||||||
|
// When working on localhost, the cookie domain must be omitted entirely (https://stackoverflow.com/a/1188145)
|
||||||
|
domain: `.${LEARNHOUSE_TOP_DOMAIN}`,
|
||||||
|
secure: !isDevEnv,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, user, account }: any) {
|
async jwt({ token, user, account }: any) {
|
||||||
// First sign in with Credentials provider
|
// First sign in with Credentials provider
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,16 @@ const LoginClient = (props: LoginClientProps) => {
|
||||||
redirect: false,
|
redirect: false,
|
||||||
email: values.email,
|
email: values.email,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
callbackUrl: '/'
|
callbackUrl: '/redirect_from_auth'
|
||||||
});
|
});
|
||||||
if (res && res.error) {
|
if (res && res.error) {
|
||||||
setError("Wrong Email or password");
|
setError("Wrong Email or password");
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}else {
|
} else {
|
||||||
await signIn('credentials', {
|
await signIn('credentials', {
|
||||||
email: values.email,
|
email: values.email,
|
||||||
password: values.password,
|
password: values.password,
|
||||||
callbackUrl: '/'
|
callbackUrl: '/redirect_from_auth'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -177,7 +177,7 @@ const LoginClient = (props: LoginClientProps) => {
|
||||||
</div>
|
</div>
|
||||||
</FormLayout>
|
</FormLayout>
|
||||||
<div className='flex h-0.5 rounded-2xl bg-slate-100 mt-5 mb-5 mx-10'></div>
|
<div className='flex h-0.5 rounded-2xl bg-slate-100 mt-5 mb-5 mx-10'></div>
|
||||||
<button onClick={() => signIn('google')} className="flex justify-center py-3 text-md w-full bg-white text-slate-600 space-x-3 font-semibold text-center p-2 rounded-md shadow hover:cursor-pointer">
|
<button onClick={() => signIn('google', { callbackUrl: '/redirect_from_auth' })} className="flex justify-center py-3 text-md w-full bg-white text-slate-600 space-x-3 font-semibold text-center p-2 rounded-md shadow hover:cursor-pointer">
|
||||||
<img src="https://fonts.gstatic.com/s/i/productlogos/googleg/v6/24px.svg" alt="" />
|
<img src="https://fonts.gstatic.com/s/i/productlogos/googleg/v6/24px.svg" alt="" />
|
||||||
<span>Sign in with Google</span>
|
<span>Sign in with Google</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -3,14 +3,14 @@ import LoginClient from './login'
|
||||||
import { Metadata } from 'next'
|
import { Metadata } from 'next'
|
||||||
|
|
||||||
type MetadataProps = {
|
type MetadataProps = {
|
||||||
params: { orgslug: string; courseid: string }
|
params: { orgslug: string }
|
||||||
searchParams: { [key: string]: string | string[] | undefined }
|
searchParams: { [key: string]: string | string[] | undefined }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata(params: MetadataProps): Promise<Metadata> {
|
||||||
params,
|
const orgslug = params.searchParams.orgslug
|
||||||
}: MetadataProps): Promise<Metadata> {
|
|
||||||
const orgslug = params.orgslug
|
//const orgslug = params.orgslug
|
||||||
// Get Org context information
|
// Get Org context information
|
||||||
const org = await getOrganizationContextInfo(orgslug, {
|
const org = await getOrganizationContextInfo(orgslug, {
|
||||||
revalidate: 0,
|
revalidate: 0,
|
||||||
|
|
@ -22,8 +22,8 @@ export async function generateMetadata({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Login = async (params: any) => {
|
const Login = async (params: MetadataProps) => {
|
||||||
const orgslug = params.params.orgslug
|
const orgslug = params.searchParams.orgslug
|
||||||
const org = await getOrganizationContextInfo(orgslug, {
|
const org = await getOrganizationContextInfo(orgslug, {
|
||||||
revalidate: 0,
|
revalidate: 0,
|
||||||
tags: ['organizations'],
|
tags: ['organizations'],
|
||||||
|
|
@ -17,7 +17,7 @@ export default function Error({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ErrorUI></ErrorUI>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export function CourseProvider({
|
||||||
}
|
}
|
||||||
}, [courseStructureData,session])
|
}, [courseStructureData,session])
|
||||||
|
|
||||||
if (!courseStructureData) return <PageLoading></PageLoading>
|
if (!courseStructureData) return
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CourseContext.Provider value={courseStructure}>
|
<CourseContext.Provider value={courseStructure}>
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,17 @@ function LHSessionProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('useLHSession', session);
|
console.log('useLHSession', session);
|
||||||
}, [session])
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
if (session.status == 'loading') {
|
if (session && session.status == 'loading') {
|
||||||
return <PageLoading />
|
return <PageLoading />
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else if (session) {
|
||||||
return (
|
return (
|
||||||
<SessionContext.Provider value={session}>
|
<SessionContext.Provider value={session}>
|
||||||
|
{console.log('rendered')}
|
||||||
{children}
|
{children}
|
||||||
</SessionContext.Provider>
|
</SessionContext.Provider>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
'use client'
|
'use client'
|
||||||
import { getAPIUrl } from '@services/config/config'
|
import { getAPIUrl } from '@services/config/config'
|
||||||
import { swrFetcher } from '@services/utils/ts/requests'
|
import { swrFetcher } from '@services/utils/ts/requests'
|
||||||
import React, { useContext, useEffect } from 'react'
|
import React, { useContext, useEffect, useState } from 'react'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { createContext } from 'react'
|
import { createContext } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
@ -20,24 +20,31 @@ export function OrgProvider({
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const access_token = session?.data?.tokens?.access_token;
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
const { data: org } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, (url) => swrFetcher(url, access_token))
|
const { data: org } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, (url) => swrFetcher(url, access_token))
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [isOrgActive, setIsOrgActive] = useState(true);
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
// Check if Org is Active
|
// Check if Org is Active
|
||||||
const verifyIfOrgIsActive = () => {
|
const verifyIfOrgIsActive = () => {
|
||||||
if (org && org?.config.config.GeneralConfig.active === false) {
|
if (org && org?.config.config.GeneralConfig.active === false) {
|
||||||
router.push('/404')
|
setIsOrgActive(false)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setIsOrgActive(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
|
||||||
verifyIfOrgIsActive()
|
|
||||||
}, [org])
|
|
||||||
|
|
||||||
if (org) {
|
useEffect(() => {
|
||||||
|
if (org && session) {
|
||||||
|
verifyIfOrgIsActive()
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}, [org, session])
|
||||||
|
|
||||||
|
if (!isLoading) {
|
||||||
return <OrgContext.Provider value={org}>{children}</OrgContext.Provider>
|
return <OrgContext.Provider value={org}>{children}</OrgContext.Provider>
|
||||||
}
|
}
|
||||||
else {
|
if (!isOrgActive) {
|
||||||
return <ErrorUI />
|
return <ErrorUI message='This organization is no longer active' />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ import React, { useEffect } from 'react'
|
||||||
import UserAvatar from '../../Objects/UserAvatar'
|
import UserAvatar from '../../Objects/UserAvatar'
|
||||||
import AdminAuthorization from '@components/Security/AdminAuthorization'
|
import AdminAuthorization from '@components/Security/AdminAuthorization'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
|
import { getUriWithOrg, getUriWithoutOrg } from '@services/config/config'
|
||||||
|
|
||||||
function LeftMenu() {
|
function LeftMenu() {
|
||||||
const org = useOrg() as any
|
const org = useOrg() as any
|
||||||
const session = useLHSession() as any
|
const session = useLHSession() as any
|
||||||
const [loading, setLoading] = React.useState(true)
|
const [loading, setLoading] = React.useState(true)
|
||||||
const route = useRouter()
|
|
||||||
|
|
||||||
function waitForEverythingToLoad() {
|
function waitForEverythingToLoad() {
|
||||||
if (org && session) {
|
if (org && session) {
|
||||||
|
|
@ -26,9 +26,9 @@ function LeftMenu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logOutUI() {
|
async function logOutUI() {
|
||||||
const res = await signOut()
|
const res = await signOut({ redirect: true, callbackUrl: getUriWithoutOrg('/login?orgslug=' + org.slug) })
|
||||||
if (res) {
|
if (res) {
|
||||||
route.push('/login')
|
getUriWithOrg(org.slug, '/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,16 @@ import { Settings } from 'lucide-react'
|
||||||
import UserAvatar from '@components/Objects/UserAvatar'
|
import UserAvatar from '@components/Objects/UserAvatar'
|
||||||
import useAdminStatus from '@components/Hooks/useAdminStatus'
|
import useAdminStatus from '@components/Hooks/useAdminStatus'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
|
import { getUriWithOrg, getUriWithoutOrg } from '@services/config/config'
|
||||||
|
|
||||||
export const HeaderProfileBox = () => {
|
export const HeaderProfileBox = () => {
|
||||||
const session = useLHSession() as any
|
const session = useLHSession() as any
|
||||||
const isUserAdmin = useAdminStatus() as any
|
const isUserAdmin = useAdminStatus() as any
|
||||||
|
const org = useOrg() as any
|
||||||
|
|
||||||
useEffect(() => {}
|
useEffect(() => { }
|
||||||
, [session])
|
, [session])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileArea>
|
<ProfileArea>
|
||||||
|
|
@ -20,10 +23,11 @@ export const HeaderProfileBox = () => {
|
||||||
<UnidentifiedArea className="flex text-sm text-gray-700 font-bold p-1.5 px-2 rounded-lg">
|
<UnidentifiedArea className="flex text-sm text-gray-700 font-bold p-1.5 px-2 rounded-lg">
|
||||||
<ul className="flex space-x-3 items-center">
|
<ul className="flex space-x-3 items-center">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/login">Login</Link>
|
<Link
|
||||||
|
href={{ pathname: getUriWithoutOrg('/login'), query: org ? { orgslug: org.slug } : null }} >Login</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="bg-black rounded-lg shadow-md p-2 px-3 text-white">
|
<li className="bg-black rounded-lg shadow-md p-2 px-3 text-white">
|
||||||
<Link href="/signup">Sign up</Link>
|
<Link href={{ pathname: getUriWithoutOrg('/signup'), query: org ? { orgslug: org.slug } : null }}>Sign up</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</UnidentifiedArea>
|
</UnidentifiedArea>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { AlertTriangle, RefreshCcw } from 'lucide-react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
function ErrorUI() {
|
function ErrorUI(params: { message?: string }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
function reloadPage() {
|
function reloadPage() {
|
||||||
|
|
@ -15,7 +15,7 @@ function ErrorUI() {
|
||||||
<div className="flex flex-col py-10 mx-auto antialiased items-center space-y-6 bg-gradient-to-b from-rose-100 to-rose-100/5 ">
|
<div className="flex flex-col py-10 mx-auto antialiased items-center space-y-6 bg-gradient-to-b from-rose-100 to-rose-100/5 ">
|
||||||
<div className="flex flex-row items-center space-x-5 rounded-xl ">
|
<div className="flex flex-row items-center space-x-5 rounded-xl ">
|
||||||
<AlertTriangle className="text-rose-700" size={45} />
|
<AlertTriangle className="text-rose-700" size={45} />
|
||||||
<p className="text-3xl font-bold text-rose-700">Something went wrong</p>
|
<p className="text-3xl font-bold text-rose-700">{params.message ? params.message : 'Something went wrong'}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import { isInstallModeEnabled } from '@services/install/install'
|
import { isInstallModeEnabled } from '@services/install/install'
|
||||||
import {
|
import {
|
||||||
LEARNHOUSE_DOMAIN,
|
LEARNHOUSE_DOMAIN,
|
||||||
|
LEARNHOUSE_TOP_DOMAIN,
|
||||||
getDefaultOrg,
|
getDefaultOrg,
|
||||||
|
getUriWithOrg,
|
||||||
isMultiOrgModeEnabled,
|
isMultiOrgModeEnabled,
|
||||||
} from './services/config/config'
|
} from './services/config/config'
|
||||||
import { NextResponse } from 'next/server'
|
import { NextResponse } from 'next/server'
|
||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
|
|
@ -25,12 +28,16 @@ export default async function middleware(req: NextRequest) {
|
||||||
// Get initial data
|
// Get initial data
|
||||||
const hosting_mode = isMultiOrgModeEnabled() ? 'multi' : 'single'
|
const hosting_mode = isMultiOrgModeEnabled() ? 'multi' : 'single'
|
||||||
const default_org = getDefaultOrg()
|
const default_org = getDefaultOrg()
|
||||||
const pathname = req.nextUrl.pathname
|
const { pathname, search } = req.nextUrl
|
||||||
const fullhost = req.headers ? req.headers.get('host') : ''
|
const fullhost = req.headers ? req.headers.get('host') : ''
|
||||||
|
const cookie_orgslug = req.cookies.get('learnhouse_current_orgslug')?.value
|
||||||
|
const orgslug = fullhost
|
||||||
|
? fullhost.replace(`.${LEARNHOUSE_DOMAIN}`, '')
|
||||||
|
: (default_org as string)
|
||||||
|
|
||||||
// Organizations & Global settings
|
// Login
|
||||||
if (pathname.startsWith('/organizations')) {
|
if (orgslug == 'auth' || pathname.startsWith('/login')) {
|
||||||
return NextResponse.rewrite(new URL(pathname, req.url))
|
return NextResponse.rewrite(new URL(`/login${search}`, req.url))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install Page
|
// Install Page
|
||||||
|
|
@ -49,19 +56,63 @@ export default async function middleware(req: NextRequest) {
|
||||||
return NextResponse.rewrite(new URL(`/editor${pathname}`, req.url))
|
return NextResponse.rewrite(new URL(`/editor${pathname}`, req.url))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auth Redirects
|
||||||
|
if (pathname == '/redirect_from_auth') {
|
||||||
|
if (cookie_orgslug) {
|
||||||
|
const searchParams = req.nextUrl.searchParams
|
||||||
|
const queryString = searchParams.toString()
|
||||||
|
const redirectPathname = '/'
|
||||||
|
const redirectUrl = new URL(
|
||||||
|
getUriWithOrg(cookie_orgslug, redirectPathname),
|
||||||
|
req.url
|
||||||
|
)
|
||||||
|
|
||||||
|
if (queryString) {
|
||||||
|
redirectUrl.search = queryString
|
||||||
|
}
|
||||||
|
return NextResponse.redirect(redirectUrl)
|
||||||
|
} else{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Multi Organization Mode
|
// Multi Organization Mode
|
||||||
if (hosting_mode === 'multi') {
|
if (hosting_mode === 'multi') {
|
||||||
// Get the organization slug from the URL
|
// Get the organization slug from the URL
|
||||||
const orgslug = fullhost
|
const orgslug = fullhost
|
||||||
? fullhost.replace(`.${LEARNHOUSE_DOMAIN}`, '')
|
? fullhost.replace(`.${LEARNHOUSE_DOMAIN}`, '')
|
||||||
: default_org
|
: (default_org as string)
|
||||||
return NextResponse.rewrite(new URL(`/orgs/${orgslug}${pathname}`, req.url))
|
const response = NextResponse.rewrite(
|
||||||
|
new URL(`/orgs/${orgslug}${pathname}`, req.url)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set the cookie with the orgslug value
|
||||||
|
response.cookies.set({
|
||||||
|
name: 'learnhouse_current_orgslug',
|
||||||
|
value: orgslug,
|
||||||
|
domain: LEARNHOUSE_TOP_DOMAIN == 'localhost' ? '' : LEARNHOUSE_TOP_DOMAIN,
|
||||||
|
path: '/',
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single Organization Mode
|
// Single Organization Mode
|
||||||
if (hosting_mode === 'single') {
|
if (hosting_mode === 'single') {
|
||||||
// Get the default organization slug
|
// Get the default organization slug
|
||||||
const orgslug = default_org
|
const orgslug = default_org as string
|
||||||
return NextResponse.rewrite(new URL(`/orgs/${orgslug}${pathname}`, req.url))
|
const response = NextResponse.rewrite(
|
||||||
|
new URL(`/orgs/${orgslug}${pathname}`, req.url)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set the cookie with the orgslug value
|
||||||
|
response.cookies.set({
|
||||||
|
name: 'learnhouse_current_orgslug',
|
||||||
|
value: orgslug,
|
||||||
|
domain: LEARNHOUSE_TOP_DOMAIN == 'localhost' ? '' : LEARNHOUSE_TOP_DOMAIN,
|
||||||
|
path: '/',
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
"dev-https" : "next dev --experimental-https -p 443",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ export const LEARNHOUSE_HTTP_PROTOCOL =
|
||||||
const LEARNHOUSE_API_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL}`
|
const LEARNHOUSE_API_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL}`
|
||||||
export const LEARNHOUSE_BACKEND_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}`
|
export const LEARNHOUSE_BACKEND_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}`
|
||||||
export const LEARNHOUSE_DOMAIN = process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN
|
export const LEARNHOUSE_DOMAIN = process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN
|
||||||
|
export const LEARNHOUSE_TOP_DOMAIN =
|
||||||
|
process.env.NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN
|
||||||
export const LEARNHOUSE_COLLABORATION_WS_URL =
|
export const LEARNHOUSE_COLLABORATION_WS_URL =
|
||||||
process.env.NEXT_PUBLIC_LEARNHOUSE_COLLABORATION_WS_URL
|
process.env.NEXT_PUBLIC_LEARNHOUSE_COLLABORATION_WS_URL
|
||||||
|
|
||||||
|
|
@ -21,6 +23,14 @@ export const getUriWithOrg = (orgslug: string, path: string) => {
|
||||||
return `${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}${path}`
|
return `${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}${path}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getUriWithoutOrg = (path: string) => {
|
||||||
|
const multi_org = isMultiOrgModeEnabled()
|
||||||
|
if (multi_org) {
|
||||||
|
return `${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}${path}`
|
||||||
|
}
|
||||||
|
return `${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
export const getOrgFromUri = () => {
|
export const getOrgFromUri = () => {
|
||||||
const multi_org = isMultiOrgModeEnabled()
|
const multi_org = isMultiOrgModeEnabled()
|
||||||
if (multi_org) {
|
if (multi_org) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue