diff --git a/apps/api/src/db/courses.py b/apps/api/src/db/courses.py index 7cc950b1..fade9b48 100644 --- a/apps/api/src/db/courses.py +++ b/apps/api/src/db/courses.py @@ -40,7 +40,7 @@ class CourseUpdate(CourseBase): class CourseRead(CourseBase): id: int org_id: int = Field(default=None, foreign_key="organization.id") - authors: List[UserRead] + authors: List[UserRead] course_uuid: str creation_date: str update_date: str @@ -49,22 +49,22 @@ class CourseRead(CourseBase): class FullCourseRead(CourseBase): id: int - course_uuid: str - creation_date: str - update_date: str + course_uuid: Optional[str] + creation_date: Optional[str] + update_date: Optional[str] # Chapters, Activities chapters: List[ChapterRead] - authors: List[UserRead] + authors: List[UserRead] pass class FullCourseReadWithTrail(CourseBase): id: int - course_uuid: str - creation_date: str - update_date: str + course_uuid: Optional[str] + creation_date: Optional[str] + update_date: Optional[str] org_id: int = Field(default=None, foreign_key="organization.id") - authors: List[UserRead] + authors: List[UserRead] # Chapters, Activities chapters: List[ChapterRead] # Trail diff --git a/apps/api/src/db/trail_runs.py b/apps/api/src/db/trail_runs.py index e160a790..26bfec8f 100644 --- a/apps/api/src/db/trail_runs.py +++ b/apps/api/src/db/trail_runs.py @@ -47,10 +47,10 @@ class TrailRunRead(BaseModel): org_id: int = Field(default=None, foreign_key="organization.id") user_id: int = Field(default=None, foreign_key="user.id") # course object - course: dict + course: Optional[dict] # timestamps - creation_date: str - update_date: str + creation_date: Optional[str] + update_date: Optional[str] # number of activities in course course_total_steps: int steps: list[TrailStep] diff --git a/apps/api/src/db/trails.py b/apps/api/src/db/trails.py index c59697ef..e29f241f 100644 --- a/apps/api/src/db/trails.py +++ b/apps/api/src/db/trails.py @@ -23,11 +23,11 @@ class TrailCreate(TrailBase): # trick because Lists are not supported in SQLModel (runs: list[TrailRun] ) class TrailRead(BaseModel): id: Optional[int] = Field(default=None, primary_key=True) - trail_uuid: str + trail_uuid: Optional[str] org_id: int = Field(default=None, foreign_key="organization.id") user_id: int = Field(default=None, foreign_key="user.id") - creation_date: str - update_date: str + creation_date: Optional[str] + update_date: Optional[str] runs: list[TrailRunRead] class Config: diff --git a/apps/api/src/services/courses/chapters.py b/apps/api/src/services/courses/chapters.py index 1e166d74..bb128c44 100644 --- a/apps/api/src/services/courses/chapters.py +++ b/apps/api/src/services/courses/chapters.py @@ -207,6 +207,10 @@ async def get_course_chapters( page: int = 1, limit: int = 10, ) -> List[ChapterRead]: + + statement = select(Course).where(Course.id == course_id) + course = db_session.exec(statement).first() + statement = ( select(Chapter) .join(CourseChapter, Chapter.id == CourseChapter.chapter_id) @@ -220,7 +224,7 @@ async def get_course_chapters( chapters = [ChapterRead(**chapter.dict(), activities=[]) for chapter in chapters] # RBAC check - await rbac_check(request, "chapter_x", current_user, "read", db_session) + await rbac_check(request, course.course_uuid, current_user, "read", db_session) # Get activities for each chapter for chapter in chapters: @@ -532,7 +536,7 @@ async def reorder_chapters_and_activities( async def rbac_check( request: Request, - course_id: str, + course_uuid: str, current_user: PublicUser | AnonymousUser, action: Literal["create", "read", "update", "delete"], db_session: Session, @@ -543,7 +547,7 @@ async def rbac_check( request, current_user.id, action, - course_id, + course_uuid, db_session, ) diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py index d7366503..eabc85c2 100644 --- a/apps/api/src/services/courses/courses.py +++ b/apps/api/src/services/courses/courses.py @@ -96,11 +96,16 @@ async def get_course_meta( chapters = await get_course_chapters(request, course.id, db_session, current_user) # Trail - trail = await get_user_trail_with_orgid( - request, current_user, course.org_id, db_session - ) + trail = None + + if isinstance(current_user, AnonymousUser): + trail = None + else: + trail = await get_user_trail_with_orgid( + request, current_user, course.org_id, db_session + ) + trail = TrailRead.from_orm(trail) - trail = TrailRead.from_orm(trail) return FullCourseReadWithTrail( **course.dict(), diff --git a/apps/api/src/services/trail/trail.py b/apps/api/src/services/trail/trail.py index 973575a7..ca9f666c 100644 --- a/apps/api/src/services/trail/trail.py +++ b/apps/api/src/services/trail/trail.py @@ -8,7 +8,7 @@ from src.db.courses import Course from src.db.trail_runs import TrailRun, TrailRunRead from src.db.trail_steps import TrailStep from src.db.trails import Trail, TrailCreate, TrailRead -from src.db.users import PublicUser +from src.db.users import AnonymousUser, PublicUser async def create_user_trail( @@ -122,9 +122,15 @@ async def check_trail_presence( async def get_user_trail_with_orgid( - request: Request, user: PublicUser, org_id: int, db_session: Session + request: Request, user: PublicUser | AnonymousUser, org_id: int, db_session: Session ) -> TrailRead: + if isinstance(user, AnonymousUser): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Anonymous users cannot access this endpoint", + ) + trail = await check_trail_presence( org_id=org_id, user_id=user.id, diff --git a/apps/web/app/orgs/[orgslug]/signup/signup.tsx b/apps/web/app/orgs/[orgslug]/signup/signup.tsx index 102af86c..b22454f0 100644 --- a/apps/web/app/orgs/[orgslug]/signup/signup.tsx +++ b/apps/web/app/orgs/[orgslug]/signup/signup.tsx @@ -7,7 +7,7 @@ import FormLayout, { ButtonBlack, FormField, FormLabel, FormLabelAndMessage, For import Image from 'next/image'; import * as Form from '@radix-ui/react-form'; import { getOrgLogoMediaDirectory } from '@services/media/media'; -import { AlertTriangle } from 'lucide-react'; +import { AlertTriangle, Check, User } from 'lucide-react'; import Link from 'next/link'; import { signup } from '@services/auth/auth'; import { getUriWithOrg } from '@services/config/config'; @@ -44,10 +44,6 @@ const validate = (values: any) => { errors.username = 'Username must be at least 4 characters'; } - if (!values.full_name) { - errors.full_name = 'Required'; - } - if (!values.bio) { errors.bio = 'Required'; } @@ -61,6 +57,7 @@ function SignUpClient(props: SignUpClientProps) { const [isSubmitting, setIsSubmitting] = React.useState(false); const router = useRouter(); const [error, setError] = React.useState(''); + const [message, setMessage] = React.useState(''); const formik = useFormik({ initialValues: { org_slug: props.org?.slug, @@ -68,7 +65,8 @@ function SignUpClient(props: SignUpClientProps) { password: '', username: '', bio: '', - full_name: '', + first_name: '', + last_name: '', }, validate, onSubmit: async values => { @@ -76,7 +74,8 @@ function SignUpClient(props: SignUpClientProps) { let res = await signup(values); let message = await res.json(); if (res.status == 200) { - router.push(`/`); + //router.push(`/login`); + setMessage('Your account was successfully created') setIsSubmitting(false); } else if (res.status == 401 || res.status == 400 || res.status == 404 || res.status == 409) { @@ -126,6 +125,16 @@ function SignUpClient(props: SignUpClientProps) {
{error}
)} + {message && ( +
+
+ +
{message}
+
+
+
Login
+
+ )} @@ -150,16 +159,6 @@ function SignUpClient(props: SignUpClientProps) { - {/* for full name */} - - - - - - - - - {/* for bio */}