mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init install mode
This commit is contained in:
parent
687f7b2116
commit
056365dac9
21 changed files with 1230 additions and 6 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
@ -13,6 +12,9 @@ __pycache__/
|
|||
# Learnhouse
|
||||
content/*
|
||||
|
||||
# Flyio
|
||||
fly.toml
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
|
|
|
|||
80
front/app/install/install.tsx
Normal file
80
front/app/install/install.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
'use client'
|
||||
import React, { use, useEffect } from 'react'
|
||||
import { INSTALL_STEPS } from './steps/steps'
|
||||
import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
|
||||
|
||||
|
||||
|
||||
function InstallClient() {
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
const step: any = parseInt(searchParams.get('step') || '0');
|
||||
const [stepNumber, setStepNumber] = React.useState(step)
|
||||
const [stepsState, setStepsState] = React.useState(INSTALL_STEPS)
|
||||
|
||||
function handleStepChange(stepNumber: number) {
|
||||
setStepNumber(stepNumber)
|
||||
router.push(`/install?step=${stepNumber}`)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setStepNumber(step)
|
||||
}, [step])
|
||||
|
||||
return (
|
||||
<GeneralWrapperStyled>
|
||||
<div className='flex justify-center '>
|
||||
<div className='grow'>
|
||||
<LearnHouseLogo />
|
||||
</div>
|
||||
<div className="steps flex space-x-2 justify-center text-sm p-3 bg-slate-50 rounded-full w-fit m-auto px-10">
|
||||
<div className="flex space-x-8">
|
||||
{stepsState.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex items-center cursor-pointer space-x-2`}
|
||||
onClick={() => handleStepChange(index)}
|
||||
>
|
||||
<div className={`flex w-7 h-7 rounded-full text-slate-700 bg-slate-200 justify-center items-center m-auto align-middle hover:bg-slate-300 transition-all ${index === stepNumber ? 'bg-slate-300' : ''}`}>
|
||||
{index}
|
||||
</div>
|
||||
<div>{step.name}</div>
|
||||
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex pt-8 flex-col" >
|
||||
<h1 className='font-bold text-3xl'>{stepsState[stepNumber].name}</h1>
|
||||
<div className="pt-8">
|
||||
{stepsState[stepNumber].component}
|
||||
</div>
|
||||
</div>
|
||||
</GeneralWrapperStyled>
|
||||
)
|
||||
}
|
||||
|
||||
const LearnHouseLogo = () => {
|
||||
return (
|
||||
<svg width="133" height="80" viewBox="0 0 433 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="80" height="80" rx="24" fill="black" />
|
||||
<rect width="80" height="80" rx="24" fill="url(#paint0_angular_1555_220)" />
|
||||
<rect x="0.5" y="0.5" width="79" height="79" rx="23.5" stroke="white" strokeOpacity="0.12" />
|
||||
<path d="M37.546 55.926V35.04L33.534 30.497L37.546 29.258V27.016L33.534 22.473L44.626 19.11V55.926L48.992 61H33.18L37.546 55.926Z" fill="white" />
|
||||
<path d="M113.98 54.98V30.2L109.22 24.81L113.98 23.34V20.68L109.22 15.29L122.38 11.3V54.98L127.56 61H108.8L113.98 54.98ZM157.704 41.19V41.26H135.234C136.004 50.29 140.834 54.07 146.294 54.07C151.054 54.07 155.254 51.69 156.304 48.75L157.354 49.17C154.834 55.54 149.864 61.98 141.534 61.98C132.364 61.98 127.184 53.79 127.184 45.39C127.184 36.36 132.784 26 144.194 26C152.524 26 157.634 31.6 157.704 41.05L157.774 41.19H157.704ZM148.674 39.16V38.53C148.674 31.04 145.664 28.1 142.584 28.1C137.264 28.1 135.094 34.47 135.094 38.67V39.16H148.674ZM178.717 61V55.12C176.057 57.71 171.157 61.7 166.537 61.7C161.707 61.7 158.137 59.32 158.137 53.65C158.137 46.51 166.607 42.87 178.717 38.6C178.717 33 178.577 28.66 172.837 28.66C167.237 28.66 163.877 32.58 160.307 37.9H159.817V26.7H188.657L187.117 32.72V56.45H187.187L192.367 61H178.717ZM178.717 53.23V40.56C167.727 44.97 167.377 47.98 167.377 51.34C167.377 54.7 169.687 56.17 172.627 56.17C174.797 56.17 176.967 55.05 178.717 53.23ZM221.429 39.09H220.869C217.789 31.74 213.659 29.29 210.439 29.29C205.609 29.29 205.609 32.79 205.609 39.93V54.98L212.119 61H192.029L197.209 54.98V32.09L192.449 26.7H221.429V39.09ZM261.467 61H242.707L247.747 54.98V39.44C247.747 34.05 246.977 30.62 241.587 30.62C238.997 30.62 236.337 31.74 234.097 34.75V54.98L239.137 61H220.377L225.697 54.98V36.08L220.937 30.69L234.097 26V32.37C236.897 28.03 241.447 25.86 245.647 25.86C252.787 25.86 256.147 30.48 256.147 37.06V54.98L261.467 61ZM274.343 11.3V32.23C277.143 27.89 281.693 25.72 285.893 25.72C293.033 25.72 296.393 30.34 296.393 36.92V54.98H296.463L301.643 61H282.883L287.993 55.05V39.3C287.993 33.91 287.223 30.48 281.833 30.48C279.243 30.48 276.583 31.6 274.343 34.61V54.98L279.523 61H260.763L265.943 54.98V21.38L261.183 15.99L274.343 11.3ZM335.945 42.31C335.945 51.34 329.855 61.84 316.835 61.84C306.895 61.84 301.645 53.79 301.645 45.39C301.645 36.36 307.735 25.86 320.755 25.86C330.695 25.86 335.945 33.91 335.945 42.31ZM316.975 28.52C311.165 28.52 310.535 34.82 310.535 39.02C310.535 49.94 314.525 59.18 320.685 59.18C325.865 59.18 327.195 52.32 327.195 48.68C327.195 37.76 323.135 28.52 316.975 28.52ZM349.01 26.63V48.12C349.01 53.51 349.78 56.94 355.17 56.94C357.55 56.94 360 55.75 361.82 53.65V32.72L356.64 26.63H370.22V55.26L374.98 61L361.82 61.42V55.82C359.3 59.32 356.08 61.7 351.11 61.7C343.97 61.7 340.61 57.08 340.61 50.5V32.72L335.36 26.63H349.01ZM374.617 47.77H375.177C376.997 53.79 382.527 59.04 388.267 59.04C391.137 59.04 393.517 57.64 393.517 54.49C393.517 46.23 374.967 50.29 374.967 36.43C374.967 31.25 379.517 26.7 386.657 26.7H394.357L396.947 25.23V36.85L396.527 36.78C394.007 32.23 389.807 28.87 385.327 28.94C382.387 29.01 380.707 30.83 380.707 33.56C380.707 40.77 399.887 37.62 399.887 50.43C399.887 58.55 391.697 61.7 386.167 61.7C382.667 61.7 377.907 61.21 375.247 60.09L374.617 47.77ZM430.416 41.19V41.26H407.946C408.716 50.29 413.546 54.07 419.006 54.07C423.766 54.07 427.966 51.69 429.016 48.75L430.066 49.17C427.546 55.54 422.576 61.98 414.246 61.98C405.076 61.98 399.896 53.79 399.896 45.39C399.896 36.36 405.496 26 416.906 26C425.236 26 430.346 31.6 430.416 41.05L430.486 41.19H430.416ZM421.386 39.16V38.53C421.386 31.04 418.376 28.1 415.296 28.1C409.976 28.1 407.806 34.47 407.806 38.67V39.16H421.386Z" fill="#121212" />
|
||||
<defs>
|
||||
<radialGradient id="paint0_angular_1555_220" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(40 40) rotate(90) scale(40)">
|
||||
<stop stop-color="#FBFBFB" stop-opacity="0.15" />
|
||||
<stop offset="0.442708" stop-opacity="0.1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default InstallClient
|
||||
18
front/app/install/page.tsx
Normal file
18
front/app/install/page.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react'
|
||||
import InstallClient from './install'
|
||||
|
||||
|
||||
export const metadata = {
|
||||
title: "Install LearnHouse",
|
||||
description: "Install Learnhouse on your server",
|
||||
}
|
||||
|
||||
function InstallPage() {
|
||||
return (
|
||||
<div className='bg-white h-screen'>
|
||||
<InstallClient />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InstallPage
|
||||
132
front/app/install/steps/account_creation.tsx
Normal file
132
front/app/install/steps/account_creation.tsx
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import FormLayout, { ButtonBlack, FormField, FormLabel, FormLabelAndMessage, FormMessage, Input } from '@components/StyledElements/Form/Form'
|
||||
import * as Form from '@radix-ui/react-form';
|
||||
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 { useRouter } from 'next/navigation';
|
||||
import React from 'react'
|
||||
import { BarLoader } from 'react-spinners';
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
const validate = (values: any) => {
|
||||
const errors: any = {};
|
||||
|
||||
if (!values.email) {
|
||||
errors.email = 'Required';
|
||||
}
|
||||
else if (
|
||||
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
|
||||
) {
|
||||
errors.email = 'Invalid email address';
|
||||
}
|
||||
|
||||
if (!values.password) {
|
||||
errors.password = 'Required';
|
||||
}
|
||||
else if (values.password.length < 8) {
|
||||
errors.password = 'Password must be at least 8 characters';
|
||||
}
|
||||
|
||||
if (!values.confirmPassword) {
|
||||
errors.confirmPassword = 'Required';
|
||||
}
|
||||
else if (values.confirmPassword !== values.password) {
|
||||
errors.confirmPassword = 'Passwords must match';
|
||||
}
|
||||
|
||||
if (!values.username) {
|
||||
errors.username = 'Required';
|
||||
}
|
||||
else if (values.username.length < 3) {
|
||||
errors.username = 'Username must be at least 3 characters';
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
function AccountCreation() {
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const router = useRouter(
|
||||
|
||||
)
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
org_slug: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
username: '',
|
||||
},
|
||||
validate,
|
||||
onSubmit: values => {
|
||||
console.log(install.data[1].slug)
|
||||
let finalvalues = { ...values, org_slug: install.data[1].slug }
|
||||
let finalvalueswithoutpasswords = { ...values, password: '', confirmPassword: '', org_slug: install.data[1].slug }
|
||||
let install_data = { ...install.data, 3: finalvalues }
|
||||
let install_data_without_passwords = { ...install.data, 3: finalvalueswithoutpasswords }
|
||||
updateInstall({ ...install_data_without_passwords }, 4)
|
||||
createNewUserInstall(finalvalues)
|
||||
|
||||
// await 2 seconds
|
||||
setTimeout(() => {
|
||||
setIsSubmitting(false)
|
||||
}, 2000)
|
||||
|
||||
router.push('/install?step=4')
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormLayout onSubmit={formik.handleSubmit}>
|
||||
<FormField name="email">
|
||||
<FormLabelAndMessage label='Email' message={formik.errors.email} />
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.email} type="email" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
{/* for password */}
|
||||
<FormField name="password">
|
||||
<FormLabelAndMessage label='Password' message={formik.errors.password} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.password} type="password" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
{/* for confirm password */}
|
||||
<FormField name="confirmPassword">
|
||||
|
||||
<FormLabelAndMessage label='Confirm Password' message={formik.errors.confirmPassword} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.confirmPassword} type="password" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
{/* for username */}
|
||||
<FormField name="username">
|
||||
|
||||
<FormLabelAndMessage label='Username' message={formik.errors.username} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.username} type="text" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<div className="flex flex-row-reverse py-4">
|
||||
<Form.Submit asChild>
|
||||
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
|
||||
{isSubmitting ? <BarLoader cssOverride={{ borderRadius: 60, }} width={60} color="#ffffff" />
|
||||
: "Create Admin Account"}
|
||||
</ButtonBlack>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
|
||||
</FormLayout>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountCreation
|
||||
45
front/app/install/steps/default_elements.tsx
Normal file
45
front/app/install/steps/default_elements.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { getAPIUrl } from '@services/config/config';
|
||||
import { createDefaultElements, updateInstall } from '@services/install/install';
|
||||
import { swrFetcher } from '@services/utils/ts/requests';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react'
|
||||
import useSWR from "swr";
|
||||
|
||||
function DefaultElements() {
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = React.useState(false);
|
||||
const router = useRouter()
|
||||
|
||||
function createDefElementsAndUpdateInstall() {
|
||||
try {
|
||||
createDefaultElements()
|
||||
// add an {} to the install.data object
|
||||
|
||||
let install_data = { ...install.data, 2: { status: 'OK' } }
|
||||
|
||||
updateInstall(install_data, 3)
|
||||
// await 2 seconds
|
||||
setTimeout(() => {
|
||||
setIsSubmitting(false)
|
||||
}, 2000)
|
||||
|
||||
router.push('/install?step=3')
|
||||
setIsSubmitted(true)
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex py-10 justify-center items-center space-x-3'>
|
||||
<h1>Install Default Elements </h1>
|
||||
<div onClick={createDefElementsAndUpdateInstall} className='p-3 font-bold bg-gray-200 text-gray-900 rounded-lg hover:cursor-pointer' >
|
||||
Install
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DefaultElements
|
||||
19
front/app/install/steps/disable_install_mode.tsx
Normal file
19
front/app/install/steps/disable_install_mode.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Check, Link } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
function DisableInstallMode() {
|
||||
return (
|
||||
<div className='p-4 bg-green-300 text-green-950 rounded-md flex space-x-4 items-center'>
|
||||
<div>
|
||||
<Check size={32} />
|
||||
</div>
|
||||
<div><p className='font-bold text-lg'>You have reached the end of the Installation process, <b><i>please don't forget to disable installation mode.</i></b> </p>
|
||||
<div className='flex space-x-2 items-center'>
|
||||
<Link size={20} />
|
||||
<a rel='noreferrer' target='_blank' className="text-blue-950 font-medium" href="http://docs.learnhouse.app">LearnHouse Docs</a>
|
||||
</div></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DisableInstallMode
|
||||
39
front/app/install/steps/finish.tsx
Normal file
39
front/app/install/steps/finish.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
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 { useRouter } from 'next/navigation';
|
||||
import React from 'react'
|
||||
import useSWR from "swr";
|
||||
|
||||
const Finish = () => {
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const router = useRouter()
|
||||
|
||||
async function finishInstall() {
|
||||
console.log('install_data')
|
||||
let install_data = { ...install.data, 5: { status: 'OK' } }
|
||||
|
||||
let data = await updateInstall(install_data, 6)
|
||||
if (data) {
|
||||
router.push('/install?step=6')
|
||||
}
|
||||
else {
|
||||
console.log('Error')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex py-10 justify-center items-center space-x-3'>
|
||||
<h1>Installation Complete</h1>
|
||||
<br />
|
||||
<Check size={32} />
|
||||
<div onClick={finishInstall} className='p-3 font-bold bg-gray-200 text-gray-900 rounded-lg hover:cursor-pointer' >
|
||||
Next Step
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Finish
|
||||
67
front/app/install/steps/get_started.tsx
Normal file
67
front/app/install/steps/get_started.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import PageLoading from '@components/Objects/Loaders/PageLoading';
|
||||
import { getAPIUrl } from '@services/config/config';
|
||||
import { swrFetcher } from '@services/utils/ts/requests';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { use, useEffect } from 'react'
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
function GetStarted() {
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const router = useRouter()
|
||||
|
||||
function startInstallation() {
|
||||
fetch(`${getAPIUrl()}install/start`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
}).then(res => res.json()).then(res => {
|
||||
if (res.success) {
|
||||
mutate(`${getAPIUrl()}install/latest`)
|
||||
router.push(`/install?step=1`)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function redirectToStep() {
|
||||
const step = install.step
|
||||
router.push(`/install?step=${step}`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (install) {
|
||||
redirectToStep()
|
||||
}
|
||||
}, [install])
|
||||
|
||||
|
||||
if (error) return <div className='flex py-10 justify-center items-center space-x-3'>
|
||||
<h1>Start a new installation</h1>
|
||||
<div onClick={startInstallation} className='p-3 font-bold bg-green-200 text-green-900 rounded-lg hover:cursor-pointer' >
|
||||
Start
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (isLoading) return <PageLoading />
|
||||
if (install) {
|
||||
return (
|
||||
<div>
|
||||
<div className='flex py-10 justify-center items-center space-x-3'>
|
||||
<h1>You already started an installation</h1>
|
||||
<div onClick={redirectToStep} className='p-3 font-bold bg-orange-200 text-orange-900 rounded-lg hover:cursor-pointer' >
|
||||
Continue
|
||||
</div>
|
||||
<div onClick={startInstallation} className='p-3 font-bold bg-green-200 text-green-900 rounded-lg hover:cursor-pointer' >
|
||||
Start
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GetStarted
|
||||
138
front/app/install/steps/org_creation.tsx
Normal file
138
front/app/install/steps/org_creation.tsx
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
import FormLayout, { ButtonBlack, FormField, FormLabel, FormLabelAndMessage, FormMessage, Input } from '@components/StyledElements/Form/Form'
|
||||
import * as Form from '@radix-ui/react-form';
|
||||
import { useFormik } from 'formik';
|
||||
import { BarLoader } from 'react-spinners';
|
||||
import React from 'react'
|
||||
import { createNewOrganization } from '@services/organizations/orgs';
|
||||
import { swrFetcher } from '@services/utils/ts/requests';
|
||||
import { getAPIUrl } from '@services/config/config';
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { createNewOrgInstall, updateInstall } from '@services/install/install';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
const validate = (values: any) => {
|
||||
const errors: any = {};
|
||||
|
||||
if (!values.name) {
|
||||
errors.name = 'Required';
|
||||
}
|
||||
|
||||
if (!values.description) {
|
||||
errors.description = 'Required';
|
||||
}
|
||||
|
||||
if (!values.slug) {
|
||||
errors.slug = 'Required';
|
||||
}
|
||||
|
||||
if (!values.email) {
|
||||
errors.email = 'Required';
|
||||
}
|
||||
else if (
|
||||
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
|
||||
) {
|
||||
errors.email = 'Invalid email address';
|
||||
}
|
||||
|
||||
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
function OrgCreation() {
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = React.useState(false);
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
function createOrgAndUpdateInstall(values: any) {
|
||||
try {
|
||||
createNewOrgInstall(values)
|
||||
install.data = {
|
||||
1: values
|
||||
}
|
||||
let install_data = { ...install.data, 1: values }
|
||||
updateInstall(install_data, 2)
|
||||
// await 2 seconds
|
||||
setTimeout(() => {
|
||||
setIsSubmitting(false)
|
||||
}, 2000)
|
||||
|
||||
router.push('/install?step=2')
|
||||
setIsSubmitted(true)
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
name: '',
|
||||
description: '',
|
||||
slug: '',
|
||||
email: '',
|
||||
},
|
||||
validate,
|
||||
onSubmit: values => {
|
||||
createOrgAndUpdateInstall(values)
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<FormLayout onSubmit={formik.handleSubmit}>
|
||||
<FormField name="name">
|
||||
<FormLabelAndMessage label='Name' message={formik.errors.name} />
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.name} type="text" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<FormField name="description">
|
||||
<FormLabelAndMessage label='Description' message={formik.errors.description} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.description} type="text" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<FormField name="slug">
|
||||
|
||||
<FormLabelAndMessage label='Slug' message={formik.errors.slug} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.slug} type="text" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
{/* for username */}
|
||||
<FormField name="email">
|
||||
|
||||
<FormLabelAndMessage label='Email' message={formik.errors.email} />
|
||||
|
||||
<Form.Control asChild>
|
||||
<Input onChange={formik.handleChange} value={formik.values.email} type="email" required />
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<div className="flex flex-row-reverse py-4">
|
||||
<Form.Submit asChild>
|
||||
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
|
||||
{isSubmitting ? <BarLoader cssOverride={{ borderRadius: 60, }} width={60} color="#ffffff" />
|
||||
: "Create Organization"}
|
||||
</ButtonBlack>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
|
||||
{isSubmitted && <div className='flex space-x-3'> <Check /> Organization Created Successfully</div>}
|
||||
|
||||
|
||||
</FormLayout>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrgCreation
|
||||
43
front/app/install/steps/sample_data.tsx
Normal file
43
front/app/install/steps/sample_data.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { getAPIUrl } from '@services/config/config';
|
||||
import { createSampleDataInstall, updateInstall } from '@services/install/install';
|
||||
import { swrFetcher } from '@services/utils/ts/requests';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react'
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
function SampleData() {
|
||||
const { data: install, error: error, isLoading } = useSWR(`${getAPIUrl()}install/latest`, swrFetcher);
|
||||
const router = useRouter()
|
||||
|
||||
function createSampleData() {
|
||||
|
||||
try {
|
||||
let username = install.data[3].username
|
||||
let slug = install.data[1].slug
|
||||
console.log(install.data)
|
||||
createSampleDataInstall(username, slug)
|
||||
|
||||
let install_data = { ...install.data, 4: { status: 'OK' } }
|
||||
updateInstall(install_data, 5)
|
||||
|
||||
router.push('/install?step=5')
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className='flex py-10 justify-center items-center space-x-3'>
|
||||
<h1>Install Sample data on your organization </h1>
|
||||
<div onClick={createSampleData} className='p-3 font-bold bg-purple-200 text-pruple-900 rounded-lg hover:cursor-pointer' >
|
||||
Start
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SampleData
|
||||
53
front/app/install/steps/steps.tsx
Normal file
53
front/app/install/steps/steps.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import AccountCreation from "./account_creation";
|
||||
import DefaultElements from "./default_elements";
|
||||
import DisableInstallMode from "./disable_install_mode";
|
||||
import Finish from "./finish";
|
||||
import GetStarted from "./get_started";
|
||||
import OrgCreation from "./org_creation";
|
||||
import SampleData from "./sample_data";
|
||||
|
||||
export const INSTALL_STEPS = [
|
||||
{
|
||||
id: "INSTALL_STATUS",
|
||||
name: "Get started",
|
||||
component: <GetStarted />,
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "ORGANIZATION_CREATION",
|
||||
name: "Organization Creation",
|
||||
component: <OrgCreation />,
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "DEFAULT_ELEMENTS",
|
||||
name: "Default Elements",
|
||||
component: <DefaultElements />,
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "ACCOUNT_CREATION",
|
||||
name: "Account Creation",
|
||||
component: <AccountCreation />,
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "SAMPLE_DATA",
|
||||
name: "Sample Data",
|
||||
component: <SampleData />,
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "FINISH",
|
||||
name: "Finish",
|
||||
component: <Finish />,
|
||||
completed: false,
|
||||
|
||||
},
|
||||
{
|
||||
id: "DISABLING_INSTALLATION_MODE",
|
||||
name: "Disabling Installation Mode",
|
||||
component: <DisableInstallMode />,
|
||||
completed: false,
|
||||
},
|
||||
];
|
||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import * as Form from '@radix-ui/react-form';
|
||||
import { styled, keyframes } from '@stitches/react';
|
||||
import { blackA, violet, mauve } from '@radix-ui/colors';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
const FormLayout = (props: any, onSubmit: any) => (
|
||||
<FormRoot onSubmit={props.onSubmit}>
|
||||
|
|
@ -9,6 +10,13 @@ const FormLayout = (props: any, onSubmit: any) => (
|
|||
</FormRoot>
|
||||
);
|
||||
|
||||
export const FormLabelAndMessage = (props: { label: string, message?: string }) => (
|
||||
<div className='flex items-center space-x-3'>
|
||||
<FormLabel className='grow'>{props.label}</FormLabel>
|
||||
{props.message && <div className='text-red-700 text-sm items-center rounded-md flex space-x-1'><Info size={10} /><div>{props.message}</div></div> || <></>}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const FormRoot = styled(Form.Root, {
|
||||
margin: 7
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ export default function middleware(req: NextRequest) {
|
|||
return NextResponse.rewrite(new URL(pathname, req.url));
|
||||
}
|
||||
|
||||
// Install Page
|
||||
if (pathname.startsWith("/install")) {
|
||||
return NextResponse.rewrite(new URL(pathname, req.url));
|
||||
}
|
||||
|
||||
// Dynamic Pages Editor
|
||||
if (pathname.match(/^\/course\/[^/]+\/activity\/[^/]+\/edit$/)) {
|
||||
return NextResponse.rewrite(new URL(`/editor${pathname}`, req.url));
|
||||
|
|
|
|||
32
front/services/install/install.ts
Normal file
32
front/services/install/install.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { getAPIUrl } from "@services/config/config";
|
||||
import { RequestBody, errorHandling } from "@services/utils/ts/requests";
|
||||
|
||||
export async function updateInstall(body: any, step: number) {
|
||||
const result = await fetch(`${getAPIUrl()}install/update?step=${step}`, RequestBody("POST", body, null));
|
||||
const res = await errorHandling(result);
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function createNewOrgInstall(body: any) {
|
||||
const result = await fetch(`${getAPIUrl()}install/org`, RequestBody("POST", body, null));
|
||||
const res = await errorHandling(result);
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function createNewUserInstall(body: any) {
|
||||
const result = await fetch(`${getAPIUrl()}install/user?org_slug=${body.org_slug}`, RequestBody("POST", body, null));
|
||||
const res = await errorHandling(result);
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function createSampleDataInstall(username: string, org_slug: string) {
|
||||
const result = await fetch(`${getAPIUrl()}install/sample?username=${username}&org_slug=${org_slug}`, RequestBody("POST", null, null));
|
||||
const res = await errorHandling(result);
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function createDefaultElements() {
|
||||
const result = await fetch(`${getAPIUrl()}install/default_elements`, RequestBody("POST", null, null));
|
||||
const res = await errorHandling(result);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from src.routers import blocks, dev, trail, users, auth, orgs, roles
|
||||
from src.routers.courses import chapters, collections, courses, activities
|
||||
from src.routers.install import install
|
||||
from src.services.dev.dev import isDevModeEnabled
|
||||
|
||||
|
||||
|
|
@ -23,3 +24,6 @@ v1_router.include_router(trail.router, prefix="/trail", tags=["trail"])
|
|||
v1_router.include_router(
|
||||
dev.router, prefix="/dev", tags=["dev"], dependencies=[Depends(isDevModeEnabled)]
|
||||
)
|
||||
|
||||
# Install Routes
|
||||
v1_router.include_router(install.router, prefix="/install", tags=["install"])
|
||||
|
|
|
|||
0
src/routers/install/__init__.py
Normal file
0
src/routers/install/__init__.py
Normal file
70
src/routers/install/install.py
Normal file
70
src/routers/install/install.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
from fastapi import APIRouter, Request
|
||||
|
||||
from src.services.install.install import (
|
||||
create_install_instance,
|
||||
create_sample_data,
|
||||
get_latest_install_instance,
|
||||
install_create_organization,
|
||||
install_create_organization_user,
|
||||
install_default_elements,
|
||||
update_install_instance,
|
||||
)
|
||||
from src.services.orgs.schemas.orgs import Organization
|
||||
from src.services.users.schemas.users import UserWithPassword
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/start")
|
||||
async def api_create_install_instance(request: Request, data: dict):
|
||||
# create install
|
||||
install = await create_install_instance(request, data)
|
||||
|
||||
return install
|
||||
|
||||
|
||||
@router.get("/latest")
|
||||
async def api_get_latest_install_instance(request: Request):
|
||||
# get latest created install
|
||||
install = await get_latest_install_instance(request)
|
||||
|
||||
return install
|
||||
|
||||
|
||||
@router.post("/default_elements")
|
||||
async def api_install_def_elements(request: Request):
|
||||
elements = await install_default_elements(request, {})
|
||||
|
||||
return elements
|
||||
|
||||
|
||||
@router.post("/org")
|
||||
async def api_install_org(request: Request, org: Organization):
|
||||
organization = await install_create_organization(request, org)
|
||||
|
||||
return organization
|
||||
|
||||
|
||||
@router.post("/user")
|
||||
async def api_install_user(request: Request, data: UserWithPassword, org_slug: str):
|
||||
user = await install_create_organization_user(request, data, org_slug)
|
||||
|
||||
return user
|
||||
|
||||
|
||||
@router.post("/sample")
|
||||
async def api_install_user_sample(request: Request, username: str, org_slug: str):
|
||||
sample = await create_sample_data(org_slug, username, request)
|
||||
|
||||
return sample
|
||||
|
||||
|
||||
@router.post("/update")
|
||||
async def api_update_install_instance(request: Request, data: dict, step: int):
|
||||
installs = request.app.db["installs"]
|
||||
|
||||
# get latest created install
|
||||
install = await update_install_instance(request, data, step)
|
||||
|
||||
return install
|
||||
|
|
@ -133,7 +133,7 @@ async def check_user_role_org_with_element_org(
|
|||
# Check if The role belongs to the same organization as the element
|
||||
role_db = await roles.find_one({"role_id": role.role_id})
|
||||
role = RoleInDB(**role_db)
|
||||
if role.org_id == element_org["org_id"] or role.org_id == "*":
|
||||
if (role.org_id == element_org["org_id"]) or role.org_id == "*":
|
||||
# Check if user has the right role
|
||||
for role in roles_list:
|
||||
role_db = await roles.find_one({"role_id": role.role_id})
|
||||
|
|
|
|||
0
src/services/install/__init__.py
Normal file
0
src/services/install/__init__.py
Normal file
469
src/services/install/install.py
Normal file
469
src/services/install/install.py
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
from datetime import datetime
|
||||
import os
|
||||
from re import A
|
||||
from uuid import uuid4
|
||||
from fastapi import HTTPException, Request, status
|
||||
from pydantic import BaseModel
|
||||
import requests
|
||||
from src.security.security import security_hash_password
|
||||
from src.services.courses.activities.activities import Activity, create_activity
|
||||
from src.services.courses.chapters import create_coursechapter, CourseChapter
|
||||
from src.services.courses.courses import CourseInDB
|
||||
from src.services.orgs.orgs import create_org
|
||||
from src.services.courses.thumbnails import upload_thumbnail
|
||||
|
||||
from src.services.orgs.schemas.orgs import Organization, OrganizationInDB
|
||||
from faker import Faker
|
||||
|
||||
|
||||
from src.services.roles.schemas.roles import Elements, Permission, RoleInDB
|
||||
from src.services.users.schemas.users import (
|
||||
PublicUser,
|
||||
User,
|
||||
UserInDB,
|
||||
UserOrganization,
|
||||
UserRolesInOrganization,
|
||||
UserWithPassword,
|
||||
)
|
||||
|
||||
|
||||
class InstallInstance(BaseModel):
|
||||
install_id: str
|
||||
created_date: str
|
||||
updated_date: str
|
||||
step: int
|
||||
data: dict
|
||||
|
||||
|
||||
async def create_install_instance(request: Request, data: dict):
|
||||
installs = request.app.db["installs"]
|
||||
|
||||
# get install_id
|
||||
install_id = str(f"install_{uuid4()}")
|
||||
created_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
updated_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
step = 1
|
||||
|
||||
# create install
|
||||
install = InstallInstance(
|
||||
install_id=install_id,
|
||||
created_date=created_date,
|
||||
updated_date=updated_date,
|
||||
step=step,
|
||||
data=data,
|
||||
)
|
||||
|
||||
# insert install
|
||||
installs.insert_one(install.dict())
|
||||
|
||||
return install
|
||||
|
||||
|
||||
async def get_latest_install_instance(request: Request):
|
||||
installs = request.app.db["installs"]
|
||||
|
||||
# get latest created install instance using find_one
|
||||
install = await installs.find_one(
|
||||
sort=[("created_date", -1)], limit=1, projection={"_id": 0}
|
||||
)
|
||||
|
||||
if install is None:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="No install instance found",
|
||||
)
|
||||
|
||||
else:
|
||||
install = InstallInstance(**install)
|
||||
|
||||
return install
|
||||
|
||||
|
||||
async def update_install_instance(request: Request, data: dict, step: int):
|
||||
installs = request.app.db["installs"]
|
||||
|
||||
# get latest created install
|
||||
install = await installs.find_one(
|
||||
sort=[("created_date", -1)], limit=1, projection={"_id": 0}
|
||||
)
|
||||
|
||||
if install is None:
|
||||
return None
|
||||
|
||||
else:
|
||||
# update install
|
||||
install["data"] = data
|
||||
install["step"] = step
|
||||
install["updated_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# update install
|
||||
await installs.update_one(
|
||||
{"install_id": install["install_id"]}, {"$set": install}
|
||||
)
|
||||
|
||||
install = InstallInstance(**install)
|
||||
|
||||
return install
|
||||
|
||||
|
||||
############################################################################################################
|
||||
# Steps
|
||||
############################################################################################################
|
||||
|
||||
|
||||
# Install Default roles
|
||||
async def install_default_elements(request: Request, data: dict):
|
||||
roles = request.app.db["roles"]
|
||||
|
||||
# check if default roles ADMIN_ROLE and USER_ROLE already exist
|
||||
admin_role = await roles.find_one({"role_id": "role_super_admin"})
|
||||
user_role = await roles.find_one({"role_id": "role_user"})
|
||||
|
||||
if admin_role is not None or user_role is not None:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Default roles already exist",
|
||||
)
|
||||
|
||||
# get default roles
|
||||
SUPER_ADMIN_ROLE = RoleInDB(
|
||||
name="SuperAdmin Role",
|
||||
description="This role grants all permissions to the user",
|
||||
elements=Elements(
|
||||
courses=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
houses=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
collections=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
organizations=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
coursechapters=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
activities=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
),
|
||||
org_id="*",
|
||||
role_id="role_super_admin",
|
||||
created_at=str(datetime.now()),
|
||||
updated_at=str(datetime.now()),
|
||||
)
|
||||
|
||||
ADMIN_ROLE = RoleInDB(
|
||||
name="SuperAdmin Role",
|
||||
description="This role grants all permissions to the user",
|
||||
elements=Elements(
|
||||
courses=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
houses=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
collections=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
organizations=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
coursechapters=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
activities=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
),
|
||||
org_id="*",
|
||||
role_id="role_super_admin",
|
||||
created_at=str(datetime.now()),
|
||||
updated_at=str(datetime.now()),
|
||||
)
|
||||
|
||||
USER_ROLE = RoleInDB(
|
||||
name="User role",
|
||||
description="This role grants read-only permissions to the user",
|
||||
elements=Elements(
|
||||
courses=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
houses=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
collections=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
organizations=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
coursechapters=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
activities=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
),
|
||||
org_id="*",
|
||||
role_id="role_user",
|
||||
created_at=str(datetime.now()),
|
||||
updated_at=str(datetime.now()),
|
||||
)
|
||||
|
||||
try:
|
||||
# insert default roles
|
||||
await roles.insert_many(
|
||||
[ADMIN_ROLE.dict(), USER_ROLE.dict(), SUPER_ADMIN_ROLE.dict()]
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Error while inserting default roles",
|
||||
)
|
||||
|
||||
|
||||
# Organization creation
|
||||
async def install_create_organization(
|
||||
request: Request,
|
||||
org_object: Organization,
|
||||
):
|
||||
orgs = request.app.db["organizations"]
|
||||
user = request.app.db["users"]
|
||||
|
||||
# find if org already exists using name
|
||||
|
||||
isOrgAvailable = await orgs.find_one({"slug": org_object.slug.lower()})
|
||||
|
||||
if isOrgAvailable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Organization slug already exists",
|
||||
)
|
||||
|
||||
# generate org_id with uuid4
|
||||
org_id = str(f"org_{uuid4()}")
|
||||
|
||||
org = OrganizationInDB(org_id=org_id, **org_object.dict())
|
||||
|
||||
org_in_db = await orgs.insert_one(org.dict())
|
||||
|
||||
if not org_in_db:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail="Unavailable database",
|
||||
)
|
||||
|
||||
return org.dict()
|
||||
|
||||
|
||||
async def install_create_organization_user(
|
||||
request: Request, user_object: UserWithPassword, org_slug: str
|
||||
):
|
||||
users = request.app.db["users"]
|
||||
|
||||
isUsernameAvailable = await users.find_one({"username": user_object.username})
|
||||
isEmailAvailable = await users.find_one({"email": user_object.email})
|
||||
|
||||
if isUsernameAvailable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Username already exists"
|
||||
)
|
||||
|
||||
if isEmailAvailable:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Email already exists"
|
||||
)
|
||||
|
||||
# Generate user_id with uuid4
|
||||
user_id = str(f"user_{uuid4()}")
|
||||
|
||||
# Set the username & hash the password
|
||||
user_object.username = user_object.username.lower()
|
||||
user_object.password = await security_hash_password(user_object.password)
|
||||
|
||||
# Get org_id from org_slug
|
||||
orgs = request.app.db["organizations"]
|
||||
|
||||
# Check if the org exists
|
||||
isOrgExists = await orgs.find_one({"slug": org_slug})
|
||||
|
||||
# If the org does not exist, raise an error
|
||||
if not isOrgExists:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="You are trying to create a user in an organization that does not exist",
|
||||
)
|
||||
|
||||
org_id = isOrgExists["org_id"]
|
||||
|
||||
# Create initial orgs list with the org_id passed in
|
||||
orgs = [UserOrganization(org_id=org_id, org_role="owner")]
|
||||
|
||||
# Give role
|
||||
roles = [UserRolesInOrganization(role_id="role_super_admin", org_id=org_id)]
|
||||
|
||||
# Create the user
|
||||
user = UserInDB(
|
||||
user_id=user_id,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
orgs=orgs,
|
||||
roles=roles,
|
||||
**user_object.dict(),
|
||||
)
|
||||
|
||||
# Insert the user into the database
|
||||
await users.insert_one(user.dict())
|
||||
|
||||
return User(**user.dict())
|
||||
|
||||
|
||||
async def create_sample_data(org_slug: str, username: str, request: Request):
|
||||
fake = Faker(["en_US"])
|
||||
fake_multilang = Faker(
|
||||
["en_US", "de_DE", "ja_JP", "es_ES", "it_IT", "pt_BR", "ar_PS"]
|
||||
)
|
||||
|
||||
users = request.app.db["users"]
|
||||
orgs = request.app.db["organizations"]
|
||||
user = await users.find_one({"username": username})
|
||||
org = await orgs.find_one({"slug": org_slug.lower()})
|
||||
user_id = user["user_id"]
|
||||
org_id = org["org_id"]
|
||||
|
||||
current_user = PublicUser(**user)
|
||||
|
||||
print(current_user)
|
||||
for i in range(0, 5):
|
||||
# get image in BinaryIO format from unsplash and save it to disk
|
||||
image = requests.get("https://source.unsplash.com/random/800x600")
|
||||
with open("thumbnail.jpg", "wb") as f:
|
||||
f.write(image.content)
|
||||
|
||||
course_id = f"course_{uuid4()}"
|
||||
course = CourseInDB(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
description=fake_multilang.unique.text(),
|
||||
mini_description=fake_multilang.unique.text(),
|
||||
thumbnail="thumbnail",
|
||||
org_id=org_id,
|
||||
learnings=[fake_multilang.unique.sentence() for i in range(0, 5)],
|
||||
public=True,
|
||||
chapters=[],
|
||||
course_id=course_id,
|
||||
creationDate=str(datetime.now()),
|
||||
updateDate=str(datetime.now()),
|
||||
authors=[user_id],
|
||||
chapters_content=[],
|
||||
)
|
||||
|
||||
courses = request.app.db["courses"]
|
||||
name_in_disk = f"test_mock{course_id}.jpeg"
|
||||
|
||||
|
||||
|
||||
course = CourseInDB(**course.dict())
|
||||
await courses.insert_one(course.dict())
|
||||
|
||||
# create chapters
|
||||
for i in range(0, 5):
|
||||
coursechapter = CourseChapter(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
description=fake_multilang.unique.text(),
|
||||
activities=[],
|
||||
)
|
||||
coursechapter = await create_coursechapter(
|
||||
request, coursechapter, course_id, current_user
|
||||
)
|
||||
if coursechapter:
|
||||
# create activities
|
||||
for i in range(0, 5):
|
||||
activity = Activity(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
type="dynamic",
|
||||
content={},
|
||||
)
|
||||
activity = await create_activity(
|
||||
request,
|
||||
activity,
|
||||
org_id,
|
||||
coursechapter["coursechapter_id"],
|
||||
current_user,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue