mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add custom organization logo feature
This commit is contained in:
parent
8c058db5c6
commit
91cb5740ef
16 changed files with 396 additions and 140 deletions
|
|
@ -28,7 +28,8 @@ const Organizations = () => {
|
||||||
const handleSubmit = async (e: any) => {
|
const handleSubmit = async (e: any) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log({ name, description, email });
|
console.log({ name, description, email });
|
||||||
const status = await createNewOrganization({ name, description, email, slug , default: false });
|
let logo = ''
|
||||||
|
const status = await createNewOrganization({ name, description, email, logo, slug, default: false });
|
||||||
alert(JSON.stringify(status));
|
alert(JSON.stringify(status));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,51 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { updateOrganization } from '@services/settings/org';
|
import { updateOrganization, uploadOrganizationLogo } from '@services/settings/org';
|
||||||
|
import { UploadCloud } from 'lucide-react';
|
||||||
|
import { revalidateTags } from '@services/utils/ts/requests';
|
||||||
|
|
||||||
|
|
||||||
interface OrganizationValues {
|
interface OrganizationValues {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
logo: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function OrganizationClient(props: any) {
|
function OrganizationClient(props: any) {
|
||||||
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
setSelectedFile(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadLogo = async () => {
|
||||||
|
if (selectedFile) {
|
||||||
|
let org_id = org.org_id;
|
||||||
|
await uploadOrganizationLogo(org_id, selectedFile);
|
||||||
|
setSelectedFile(null); // Reset the selected file
|
||||||
|
revalidateTags(['organizations']);
|
||||||
|
// reload the page
|
||||||
|
// terrible hack, it will fixed later
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const org = props.org;
|
const org = props.org;
|
||||||
let orgValues: OrganizationValues = {
|
let orgValues: OrganizationValues = {
|
||||||
name: org.name,
|
name: org.name,
|
||||||
description: org.description,
|
description: org.description,
|
||||||
slug: org.slug,
|
slug: org.slug,
|
||||||
|
logo: org.logo,
|
||||||
email: org.email
|
email: org.email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,6 +54,7 @@ function OrganizationClient(props: any) {
|
||||||
await updateOrganization(org_id, values);
|
await updateOrganization(org_id, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className='text-3xl font-bold'>Organization Settings</h1>
|
<h1 className='text-3xl font-bold'>Organization Settings</h1>
|
||||||
|
|
@ -62,6 +91,28 @@ function OrganizationClient(props: any) {
|
||||||
name="description"
|
name="description"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<label className="block mb-2 font-bold" htmlFor="slug">
|
||||||
|
Logo
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center w-full ">
|
||||||
|
<input
|
||||||
|
className="w-full px-4 py-2 mr-1 border rounded-lg bg-gray-200 cursor-not-allowed"
|
||||||
|
type="file"
|
||||||
|
name="logo"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={uploadLogo}
|
||||||
|
disabled={isSubmitting || selectedFile === null}
|
||||||
|
className="px-6 py-3 text-white bg-gray-500 rounded-lg hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-black"
|
||||||
|
>
|
||||||
|
<UploadCloud size={24} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<label className="block mb-2 font-bold" htmlFor="slug">
|
<label className="block mb-2 font-bold" htmlFor="slug">
|
||||||
Slug
|
Slug
|
||||||
</label>
|
</label>
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,17 @@ import React from "react";
|
||||||
import learnhouseLogo from "public/learnhouse_logo.png";
|
import learnhouseLogo from "public/learnhouse_logo.png";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { getUriWithOrg } from "@services/config/config";
|
import { getBackendUrl, getUriWithOrg } from "@services/config/config";
|
||||||
import { getOrganizationContextInfo, getOrganizationContextInfoNoAsync } from "@services/organizations/orgs";
|
import { getOrganizationContextInfo, getOrganizationContextInfoNoAsync } from "@services/organizations/orgs";
|
||||||
import ClientComponentSkeleton from "@components/UI/Utils/ClientComp";
|
import ClientComponentSkeleton from "@components/UI/Utils/ClientComp";
|
||||||
import { HeaderProfileBox } from "@components/Security/HeaderProfileBox";
|
import { HeaderProfileBox } from "@components/Security/HeaderProfileBox";
|
||||||
|
|
||||||
export const Menu = (props: any) => {
|
export const Menu = async (props: any) => {
|
||||||
const orgslug = props.orgslug;
|
const orgslug = props.orgslug;
|
||||||
const org = getOrganizationContextInfoNoAsync(orgslug, { revalidate: 1800, tags: ['organizations'] });
|
const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
|
||||||
console.log(org);
|
console.log(org);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="h-[60px]"></div>
|
<div className="h-[60px]"></div>
|
||||||
|
|
@ -20,8 +21,16 @@ export const Menu = (props: any) => {
|
||||||
<div className="logo flex ">
|
<div className="logo flex ">
|
||||||
<Link href={getUriWithOrg(orgslug, "/")}>
|
<Link href={getUriWithOrg(orgslug, "/")}>
|
||||||
<div className="flex w-auto h-9 rounded-md items-center m-auto justify-center" >
|
<div className="flex w-auto h-9 rounded-md items-center m-auto justify-center" >
|
||||||
<LearnHouseLogo></LearnHouseLogo>
|
{org?.logo ? (
|
||||||
|
<img
|
||||||
|
src={`${getBackendUrl()}content/uploads/logos/${org?.logo}`}
|
||||||
|
alt="Learnhouse"
|
||||||
|
style={{ width: "auto", height: "100%" }}
|
||||||
|
className="rounded-md"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<LearnHouseLogo></LearnHouseLogo>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default function middleware(req: NextRequest) {
|
||||||
|
|
||||||
// Organizations & Global settings
|
// Organizations & Global settings
|
||||||
if (pathname.startsWith("/organizations")) {
|
if (pathname.startsWith("/organizations")) {
|
||||||
return NextResponse.rewrite(new URL("/organizations", req.url));
|
return NextResponse.rewrite(new URL(pathname, req.url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Pages Editor
|
// Dynamic Pages Editor
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getAPIUrl } from "@services/config/config";
|
import { getAPIUrl } from "@services/config/config";
|
||||||
import { RequestBody, errorHandling } from "@services/utils/ts/requests";
|
import { RequestBody, errorHandling, RequestBodyForm } from "@services/utils/ts/requests";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file includes only POST, PUT, DELETE requests
|
This file includes only POST, PUT, DELETE requests
|
||||||
|
|
@ -11,3 +11,12 @@ export async function updateOrganization(org_id: string, data: any) {
|
||||||
const res = await errorHandling(result);
|
const res = await errorHandling(result);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadOrganizationLogo(org_id: string, logo_file: any) {
|
||||||
|
// Send file thumbnail as form data
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("logo_file", logo_file);
|
||||||
|
const result: any = await fetch(`${getAPIUrl()}orgs/` + org_id + "/logo", RequestBodyForm("PUT", formData, null));
|
||||||
|
const res = await errorHandling(result);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Request, UploadFile
|
||||||
from src.security.auth import get_current_user
|
from src.security.auth import get_current_user
|
||||||
from src.services.orgs import Organization, create_org, delete_org, get_organization, get_organization_by_slug, get_orgs_by_user, update_org
|
from src.services.orgs.orgs import Organization, create_org, delete_org, get_organization, get_organization_by_slug, get_orgs_by_user, update_org, update_org_logo
|
||||||
from src.services.users.users import PublicUser, User
|
from src.services.users.users import PublicUser, User
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,6 +31,12 @@ async def api_get_org_by_slug(request: Request, org_slug: str, current_user: Use
|
||||||
"""
|
"""
|
||||||
return await get_organization_by_slug(request, org_slug)
|
return await get_organization_by_slug(request, org_slug)
|
||||||
|
|
||||||
|
@router.put("/{org_id}/logo")
|
||||||
|
async def api_update_org_logo(request: Request, org_id: str, logo_file:UploadFile, current_user: PublicUser = Depends(get_current_user)):
|
||||||
|
"""
|
||||||
|
Get single Org by Slug
|
||||||
|
"""
|
||||||
|
return await update_org_logo(request=request,logo_file=logo_file, org_id=org_id, current_user=current_user)
|
||||||
|
|
||||||
@router.get("/user/page/{page}/limit/{limit}")
|
@router.get("/user/page/{page}/limit/{limit}")
|
||||||
async def api_user_orgs(request: Request, page: int, limit: int, current_user: PublicUser = Depends(get_current_user)):
|
async def api_user_orgs(request: Request, page: int, limit: int, current_user: PublicUser = Depends(get_current_user)):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from passlib.context import CryptContext
|
||||||
from passlib.hash import pbkdf2_sha256
|
from passlib.hash import pbkdf2_sha256
|
||||||
from src.services.roles.schemas.roles import RoleInDB
|
from src.services.roles.schemas.roles import RoleInDB
|
||||||
|
|
||||||
from src.services.users.schemas.users import UserInDB
|
from src.services.users.schemas.users import UserInDB, UserRolesInOrganization
|
||||||
|
|
||||||
### 🔒 JWT ##############################################################
|
### 🔒 JWT ##############################################################
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ async def check_element_type(element_id):
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")
|
status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")
|
||||||
|
|
||||||
|
|
||||||
async def check_user_role_org_with_element_org(request: Request, element_id: str, roles_list: list[str]):
|
async def check_user_role_org_with_element_org(request: Request, element_id: str, roles_list: list[UserRolesInOrganization]):
|
||||||
|
|
||||||
element_type = await check_element_type(element_id)
|
element_type = await check_element_type(element_id)
|
||||||
element = request.app.db[element_type]
|
element = request.app.db[element_type]
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,5 @@ async def upload_file_and_return_file_object(request: Request, file: UploadFile,
|
||||||
f.write(file_binary)
|
f.write(file_binary)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# TODO: do some error handling here
|
|
||||||
|
|
||||||
return uploadable_file
|
return uploadable_file
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class CourseChapterInDB(CourseChapter):
|
||||||
creationDate: str
|
creationDate: str
|
||||||
updateDate: str
|
updateDate: str
|
||||||
|
|
||||||
|
|
||||||
#### Classes ####################################################
|
#### Classes ####################################################
|
||||||
|
|
||||||
# TODO : Add courses photo & cover upload and delete
|
# TODO : Add courses photo & cover upload and delete
|
||||||
|
|
@ -55,6 +56,7 @@ class CourseChapterInDB(CourseChapter):
|
||||||
# CRUD
|
# CRUD
|
||||||
####################################################
|
####################################################
|
||||||
|
|
||||||
|
|
||||||
async def get_course(request: Request, course_id: str, current_user: PublicUser):
|
async def get_course(request: Request, course_id: str, current_user: PublicUser):
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
|
|
||||||
|
|
@ -65,7 +67,8 @@ async def get_course(request: Request, course_id: str, current_user: PublicUser)
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
course = Course(**course)
|
course = Course(**course)
|
||||||
return course
|
return course
|
||||||
|
|
@ -83,10 +86,12 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
coursechapters = await courses.find_one({"course_id": course_id}, {
|
coursechapters = await courses.find_one(
|
||||||
"chapters_content": 1, "_id": 0})
|
{"course_id": course_id}, {"chapters_content": 1, "_id": 0}
|
||||||
|
)
|
||||||
|
|
||||||
# activities
|
# activities
|
||||||
coursechapter_activityIds_global = []
|
coursechapter_activityIds_global = []
|
||||||
|
|
@ -103,42 +108,66 @@ async def get_course_meta(request: Request, course_id: str, current_user: Public
|
||||||
coursechapter_activityIds_global.append(activity)
|
coursechapter_activityIds_global.append(activity)
|
||||||
|
|
||||||
chapters[coursechapter.coursechapter_id] = {
|
chapters[coursechapter.coursechapter_id] = {
|
||||||
"id": coursechapter.coursechapter_id, "name": coursechapter.name, "activityIds": coursechapter_activityIds
|
"id": coursechapter.coursechapter_id,
|
||||||
|
"name": coursechapter.name,
|
||||||
|
"activityIds": coursechapter_activityIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
# activities
|
# activities
|
||||||
activities_list = {}
|
activities_list = {}
|
||||||
for activity in await activities.find({"activity_id": {"$in": coursechapter_activityIds_global}}).to_list(length=100):
|
for activity in await activities.find(
|
||||||
|
{"activity_id": {"$in": coursechapter_activityIds_global}}
|
||||||
|
).to_list(length=100):
|
||||||
activity = ActivityInDB(**activity)
|
activity = ActivityInDB(**activity)
|
||||||
activities_list[activity.activity_id] = {
|
activities_list[activity.activity_id] = {
|
||||||
"id": activity.activity_id, "name": activity.name, "type": activity.type, "content": activity.content
|
"id": activity.activity_id,
|
||||||
|
"name": activity.name,
|
||||||
|
"type": activity.type,
|
||||||
|
"content": activity.content,
|
||||||
}
|
}
|
||||||
|
|
||||||
chapters_list_with_activities = []
|
chapters_list_with_activities = []
|
||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
chapters_list_with_activities.append(
|
chapters_list_with_activities.append(
|
||||||
{"id": chapters[chapter]["id"], "name": chapters[chapter]["name"], "activities": [activities_list[activity] for activity in chapters[chapter]["activityIds"]]})
|
{
|
||||||
|
"id": chapters[chapter]["id"],
|
||||||
|
"name": chapters[chapter]["name"],
|
||||||
|
"activities": [
|
||||||
|
activities_list[activity]
|
||||||
|
for activity in chapters[chapter]["activityIds"]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
course = CourseInDB(**course)
|
course = CourseInDB(**course)
|
||||||
|
|
||||||
# Get activity by user
|
# Get activity by user
|
||||||
trail = await trails.find_one(
|
trail = await trails.find_one(
|
||||||
{"courses.course_id": course_id, "user_id": current_user.user_id})
|
{"courses.course_id": course_id, "user_id": current_user.user_id}
|
||||||
|
)
|
||||||
print(trail)
|
print(trail)
|
||||||
if trail:
|
if trail:
|
||||||
# get only the course where course_id == course_id
|
# get only the course where course_id == course_id
|
||||||
trail_course = next(
|
trail_course = next(
|
||||||
(course for course in trail["courses"] if course["course_id"] == course_id), None)
|
(course for course in trail["courses"] if course["course_id"] == course_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
trail_course = ""
|
trail_course = ""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"course": course,
|
"course": course,
|
||||||
"chapters": chapters_list_with_activities,
|
"chapters": chapters_list_with_activities,
|
||||||
"trail": trail_course
|
"trail": trail_course,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def create_course(request: Request, course_object: Course, org_id: str, current_user: PublicUser, thumbnail_file: UploadFile | None = None):
|
async def create_course(
|
||||||
|
request: Request,
|
||||||
|
course_object: Course,
|
||||||
|
org_id: str,
|
||||||
|
current_user: PublicUser,
|
||||||
|
thumbnail_file: UploadFile | None = None,
|
||||||
|
):
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
|
|
||||||
# generate course_id with uuid4
|
# generate course_id with uuid4
|
||||||
|
|
@ -147,27 +176,42 @@ async def create_course(request: Request, course_object: Course, org_id: str, cu
|
||||||
# TODO(fix) : the implementation here is clearly not the best one (this entire function)
|
# TODO(fix) : the implementation here is clearly not the best one (this entire function)
|
||||||
course_object.org_id = org_id
|
course_object.org_id = org_id
|
||||||
course_object.chapters_content = []
|
course_object.chapters_content = []
|
||||||
await verify_user_rights_with_roles(request, "create", current_user.user_id, course_id, org_id)
|
await verify_user_rights_with_roles(
|
||||||
|
request, "create", current_user.user_id, course_id, org_id
|
||||||
|
)
|
||||||
|
|
||||||
if thumbnail_file:
|
if thumbnail_file and thumbnail_file.filename:
|
||||||
name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
name_in_disk = (
|
||||||
|
f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
||||||
|
)
|
||||||
await upload_thumbnail(thumbnail_file, name_in_disk)
|
await upload_thumbnail(thumbnail_file, name_in_disk)
|
||||||
course_object.thumbnail = name_in_disk
|
course_object.thumbnail = name_in_disk
|
||||||
|
|
||||||
course = CourseInDB(course_id=course_id, authors=[
|
course = CourseInDB(
|
||||||
current_user.user_id], creationDate=str(datetime.now()), updateDate=str(datetime.now()), **course_object.dict())
|
course_id=course_id,
|
||||||
|
authors=[current_user.user_id],
|
||||||
|
creationDate=str(datetime.now()),
|
||||||
|
updateDate=str(datetime.now()),
|
||||||
|
**course_object.dict(),
|
||||||
|
)
|
||||||
|
|
||||||
course_in_db = await courses.insert_one(course.dict())
|
course_in_db = await courses.insert_one(course.dict())
|
||||||
|
|
||||||
if not course_in_db:
|
if not course_in_db:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="Unavailable database",
|
||||||
|
)
|
||||||
|
|
||||||
return course.dict()
|
return course.dict()
|
||||||
|
|
||||||
|
|
||||||
async def update_course_thumbnail(request: Request, course_id: str, current_user: PublicUser, thumbnail_file: UploadFile | None = None):
|
async def update_course_thumbnail(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
current_user: PublicUser,
|
||||||
|
thumbnail_file: UploadFile | None = None,
|
||||||
|
):
|
||||||
# verify course rights
|
# verify course rights
|
||||||
await verify_rights(request, course_id, current_user, "update")
|
await verify_rights(request, course_id, current_user, "update")
|
||||||
|
|
||||||
|
|
@ -178,26 +222,34 @@ async def update_course_thumbnail(request: Request, course_id: str, current_user
|
||||||
if course:
|
if course:
|
||||||
creationDate = course["creationDate"]
|
creationDate = course["creationDate"]
|
||||||
authors = course["authors"]
|
authors = course["authors"]
|
||||||
if thumbnail_file:
|
if thumbnail_file and thumbnail_file.filename:
|
||||||
name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
|
||||||
course = Course(**course).copy(update={"thumbnail": name_in_disk})
|
course = Course(**course).copy(update={"thumbnail": name_in_disk})
|
||||||
await upload_thumbnail(thumbnail_file, name_in_disk)
|
await upload_thumbnail(thumbnail_file, name_in_disk)
|
||||||
|
|
||||||
updated_course = CourseInDB(course_id=course_id, creationDate=creationDate,
|
updated_course = CourseInDB(
|
||||||
authors=authors, updateDate=str(datetime.now()), **course.dict())
|
course_id=course_id,
|
||||||
|
creationDate=creationDate,
|
||||||
|
authors=authors,
|
||||||
|
updateDate=str(datetime.now()),
|
||||||
|
**course.dict(),
|
||||||
|
)
|
||||||
|
|
||||||
await courses.update_one({"course_id": course_id}, {
|
await courses.update_one(
|
||||||
"$set": updated_course.dict()})
|
{"course_id": course_id}, {"$set": updated_course.dict()}
|
||||||
|
)
|
||||||
|
|
||||||
return CourseInDB(**updated_course.dict())
|
return CourseInDB(**updated_course.dict())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def update_course(request: Request, course_object: Course, course_id: str, current_user: PublicUser):
|
async def update_course(
|
||||||
|
request: Request, course_object: Course, course_id: str, current_user: PublicUser
|
||||||
|
):
|
||||||
# verify course rights
|
# verify course rights
|
||||||
await verify_rights(request, course_id, current_user, "update")
|
await verify_rights(request, course_id, current_user, "update")
|
||||||
|
|
||||||
|
|
@ -213,20 +265,26 @@ async def update_course(request: Request, course_object: Course, course_id: str,
|
||||||
datetime_object = datetime.now()
|
datetime_object = datetime.now()
|
||||||
|
|
||||||
updated_course = CourseInDB(
|
updated_course = CourseInDB(
|
||||||
course_id=course_id, creationDate=creationDate, authors=authors, updateDate=str(datetime_object), **course_object.dict())
|
course_id=course_id,
|
||||||
|
creationDate=creationDate,
|
||||||
|
authors=authors,
|
||||||
|
updateDate=str(datetime_object),
|
||||||
|
**course_object.dict(),
|
||||||
|
)
|
||||||
|
|
||||||
await courses.update_one({"course_id": course_id}, {
|
await courses.update_one(
|
||||||
"$set": updated_course.dict()})
|
{"course_id": course_id}, {"$set": updated_course.dict()}
|
||||||
|
)
|
||||||
|
|
||||||
return CourseInDB(**updated_course.dict())
|
return CourseInDB(**updated_course.dict())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def delete_course(request: Request, course_id: str, current_user: PublicUser):
|
async def delete_course(request: Request, course_id: str, current_user: PublicUser):
|
||||||
|
|
||||||
# verify course rights
|
# verify course rights
|
||||||
await verify_rights(request, course_id, current_user, "delete")
|
await verify_rights(request, course_id, current_user, "delete")
|
||||||
|
|
||||||
|
|
@ -236,7 +294,8 @@ async def delete_course(request: Request, course_id: str, current_user: PublicUs
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
isDeleted = await courses.delete_one({"course_id": course_id})
|
isDeleted = await courses.delete_one({"course_id": course_id})
|
||||||
|
|
||||||
|
|
@ -244,24 +303,38 @@ async def delete_course(request: Request, course_id: str, current_user: PublicUs
|
||||||
return {"detail": "Course deleted"}
|
return {"detail": "Course deleted"}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="Unavailable database",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# Misc
|
# Misc
|
||||||
####################################################
|
####################################################
|
||||||
|
|
||||||
|
|
||||||
async def get_courses(request: Request, page: int = 1, limit: int = 10, org_id: str | None = None):
|
async def get_courses(
|
||||||
|
request: Request, page: int = 1, limit: int = 10, org_id: str | None = None
|
||||||
|
):
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
# TODO : Get only courses that user is admin/has roles of
|
# TODO : Get only courses that user is admin/has roles of
|
||||||
# get all courses from database
|
# get all courses from database
|
||||||
all_courses = courses.find({"org_id": org_id}).sort(
|
all_courses = (
|
||||||
"name", 1).skip(10 * (page - 1)).limit(limit)
|
courses.find({"org_id": org_id})
|
||||||
|
.sort("name", 1)
|
||||||
|
.skip(10 * (page - 1))
|
||||||
|
.limit(limit)
|
||||||
|
)
|
||||||
|
|
||||||
return [json.loads(json.dumps(course, default=str)) for course in await all_courses.to_list(length=100)]
|
return [
|
||||||
|
json.loads(json.dumps(course, default=str))
|
||||||
|
for course in await all_courses.to_list(length=100)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def get_courses_orgslug(request: Request, page: int = 1, limit: int = 10, org_slug: str | None = None):
|
async def get_courses_orgslug(
|
||||||
|
request: Request, page: int = 1, limit: int = 10, org_slug: str | None = None
|
||||||
|
):
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
orgs = request.app.db["organizations"]
|
orgs = request.app.db["organizations"]
|
||||||
# TODO : Get only courses that user is admin/has roles of
|
# TODO : Get only courses that user is admin/has roles of
|
||||||
|
|
@ -271,37 +344,61 @@ async def get_courses_orgslug(request: Request, page: int = 1, limit: int = 10,
|
||||||
|
|
||||||
if not org:
|
if not org:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
# get all courses from database
|
# get all courses from database
|
||||||
all_courses = courses.find({"org_id": org['org_id']}).sort(
|
all_courses = (
|
||||||
"name", 1).skip(10 * (page - 1)).limit(limit)
|
courses.find({"org_id": org["org_id"]})
|
||||||
|
.sort("name", 1)
|
||||||
|
.skip(10 * (page - 1))
|
||||||
|
.limit(limit)
|
||||||
|
)
|
||||||
|
|
||||||
return [json.loads(json.dumps(course, default=str)) for course in await all_courses.to_list(length=100)]
|
return [
|
||||||
|
json.loads(json.dumps(course, default=str))
|
||||||
|
for course in await all_courses.to_list(length=100)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
#### Security ####################################################
|
#### Security ####################################################
|
||||||
|
|
||||||
|
|
||||||
async def verify_rights(request: Request, course_id: str, current_user: PublicUser | AnonymousUser, action: str):
|
async def verify_rights(
|
||||||
|
request: Request,
|
||||||
|
course_id: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
action: str,
|
||||||
|
):
|
||||||
courses = request.app.db["courses"]
|
courses = request.app.db["courses"]
|
||||||
|
|
||||||
course = await courses.find_one({"course_id": course_id})
|
course = await courses.find_one({"course_id": course_id})
|
||||||
|
|
||||||
if current_user.user_id == "anonymous" and course["public"] is True and action == "read":
|
if (
|
||||||
|
current_user.user_id == "anonymous"
|
||||||
|
and course["public"] is True
|
||||||
|
and action == "read"
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not course:
|
if not course:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Course/CourseChapter does not exist")
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail="Course/CourseChapter does not exist",
|
||||||
|
)
|
||||||
|
|
||||||
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, course_id, course["org_id"])
|
hasRoleRights = await verify_user_rights_with_roles(
|
||||||
|
request, action, current_user.user_id, course_id, course["org_id"]
|
||||||
|
)
|
||||||
isAuthor = current_user.user_id in course["authors"]
|
isAuthor = current_user.user_id in course["authors"]
|
||||||
|
|
||||||
if not hasRoleRights and not isAuthor:
|
if not hasRoleRights and not isAuthor:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail="Roles/Ownership : Insufficient rights to perform this action")
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Roles/Ownership : Insufficient rights to perform this action",
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
#### Security ####################################################
|
#### Security ####################################################
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from src.services.courses.chapters import CourseChapter, create_coursechapter
|
||||||
from src.services.courses.activities.activities import Activity, create_activity
|
from src.services.courses.activities.activities import Activity, create_activity
|
||||||
from src.services.users.users import PublicUser, UserInDB
|
from src.services.users.users import PublicUser, UserInDB
|
||||||
|
|
||||||
from src.services.orgs import Organization, create_org
|
from src.services.orgs.orgs import Organization, create_org
|
||||||
from src.services.roles.schemas.roles import Permission, Elements, RoleInDB
|
from src.services.roles.schemas.roles import Permission, Elements, RoleInDB
|
||||||
from src.services.courses.courses import CourseInDB
|
from src.services.courses.courses import CourseInDB
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
|
|
@ -133,6 +133,7 @@ async def create_initial_data(request: Request):
|
||||||
description=fake.unique.text(),
|
description=fake.unique.text(),
|
||||||
email=fake.unique.email(),
|
email=fake.unique.email(),
|
||||||
slug=slug,
|
slug=slug,
|
||||||
|
logo="",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
organizations.append(org)
|
organizations.append(org)
|
||||||
|
|
|
||||||
0
src/services/orgs/__init__.py
Normal file
0
src/services/orgs/__init__.py
Normal file
22
src/services/orgs/logos.py
Normal file
22
src/services/orgs/logos.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import os
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
|
async def upload_org_logo(logo_file):
|
||||||
|
contents = logo_file.file.read()
|
||||||
|
name_in_disk = f"{uuid4()}.{logo_file.filename.split('.')[-1]}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not os.path.exists("content/uploads/logos"):
|
||||||
|
os.makedirs("content/uploads/logos")
|
||||||
|
|
||||||
|
with open(f"content/uploads/logos/{name_in_disk}", "wb") as f:
|
||||||
|
f.write(contents)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return {"message": "There was an error uploading the file"}
|
||||||
|
finally:
|
||||||
|
logo_file.file.close()
|
||||||
|
|
||||||
|
return name_in_disk
|
||||||
|
|
@ -1,40 +1,16 @@
|
||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from click import Option
|
from src.services.orgs.logos import upload_org_logo
|
||||||
from pydantic import BaseModel
|
from src.services.orgs.schemas.orgs import (
|
||||||
|
Organization,
|
||||||
|
OrganizationInDB,
|
||||||
|
PublicOrganization,
|
||||||
|
)
|
||||||
from src.services.users.schemas.users import UserOrganization
|
from src.services.users.schemas.users import UserOrganization
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
from src.security.security import *
|
from src.security.security import *
|
||||||
from fastapi import HTTPException, status, Request
|
from fastapi import HTTPException, UploadFile, status, Request
|
||||||
|
|
||||||
#### Classes ####################################################
|
|
||||||
|
|
||||||
|
|
||||||
class Organization(BaseModel):
|
|
||||||
name: str
|
|
||||||
description: str
|
|
||||||
email: str
|
|
||||||
slug: str
|
|
||||||
default: Optional[bool]
|
|
||||||
|
|
||||||
|
|
||||||
class OrganizationInDB(Organization):
|
|
||||||
org_id: str
|
|
||||||
|
|
||||||
|
|
||||||
class PublicOrganization(Organization):
|
|
||||||
name: str
|
|
||||||
description: str
|
|
||||||
email: str
|
|
||||||
slug: str
|
|
||||||
org_id: str
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
return getattr(self, item)
|
|
||||||
|
|
||||||
|
|
||||||
#### Classes ####################################################
|
|
||||||
|
|
||||||
|
|
||||||
async def get_organization(request: Request, org_id: str):
|
async def get_organization(request: Request, org_id: str):
|
||||||
|
|
@ -44,7 +20,8 @@ async def get_organization(request: Request, org_id: str):
|
||||||
|
|
||||||
if not org:
|
if not org:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
org = PublicOrganization(**org)
|
org = PublicOrganization(**org)
|
||||||
return org
|
return org
|
||||||
|
|
@ -57,13 +34,16 @@ async def get_organization_by_slug(request: Request, org_slug: str):
|
||||||
|
|
||||||
if not org:
|
if not org:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
org = PublicOrganization(**org)
|
org = PublicOrganization(**org)
|
||||||
return org
|
return org
|
||||||
|
|
||||||
|
|
||||||
async def create_org(request: Request, org_object: Organization, current_user: PublicUser):
|
async def create_org(
|
||||||
|
request: Request, org_object: Organization, current_user: PublicUser
|
||||||
|
):
|
||||||
orgs = request.app.db["organizations"]
|
orgs = request.app.db["organizations"]
|
||||||
user = request.app.db["users"]
|
user = request.app.db["users"]
|
||||||
|
|
||||||
|
|
@ -72,7 +52,9 @@ async def create_org(request: Request, org_object: Organization, current_user: P
|
||||||
|
|
||||||
if isOrgAvailable:
|
if isOrgAvailable:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization slug already exists")
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail="Organization slug already exists",
|
||||||
|
)
|
||||||
|
|
||||||
# generate org_id with uuid4
|
# generate org_id with uuid4
|
||||||
org_id = str(f"org_{uuid4()}")
|
org_id = str(f"org_{uuid4()}")
|
||||||
|
|
@ -82,25 +64,33 @@ async def create_org(request: Request, org_object: Organization, current_user: P
|
||||||
org_in_db = await orgs.insert_one(org.dict())
|
org_in_db = await orgs.insert_one(org.dict())
|
||||||
|
|
||||||
user_organization: UserOrganization = UserOrganization(
|
user_organization: UserOrganization = UserOrganization(
|
||||||
org_id=org_id, org_role="owner")
|
org_id=org_id, org_role="owner"
|
||||||
|
)
|
||||||
|
|
||||||
# add org to user
|
# add org to user
|
||||||
await user.update_one({"user_id": current_user.user_id}, {
|
await user.update_one(
|
||||||
"$addToSet": {"orgs": user_organization.dict()}})
|
{"user_id": current_user.user_id},
|
||||||
|
{"$addToSet": {"orgs": user_organization.dict()}},
|
||||||
# add role admin to org
|
)
|
||||||
await user.update_one({"user_id": current_user.user_id}, {
|
|
||||||
"$addToSet": {"roles": {"org_id": org_id, "role_id": "role_admin"}}})
|
# add role admin to org
|
||||||
|
await user.update_one(
|
||||||
|
{"user_id": current_user.user_id},
|
||||||
|
{"$addToSet": {"roles": {"org_id": org_id, "role_id": "role_admin"}}},
|
||||||
|
)
|
||||||
|
|
||||||
if not org_in_db:
|
if not org_in_db:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="Unavailable database",
|
||||||
|
)
|
||||||
|
|
||||||
return org.dict()
|
return org.dict()
|
||||||
|
|
||||||
|
|
||||||
async def update_org(request: Request, org_object: Organization, org_id: str, current_user: PublicUser):
|
async def update_org(
|
||||||
|
request: Request, org_object: Organization, org_id: str, current_user: PublicUser
|
||||||
|
):
|
||||||
# verify org rights
|
# verify org rights
|
||||||
await verify_org_rights(request, org_id, current_user, "update")
|
await verify_org_rights(request, org_id, current_user, "update")
|
||||||
|
|
||||||
|
|
@ -108,21 +98,38 @@ async def update_org(request: Request, org_object: Organization, org_id: str, cu
|
||||||
|
|
||||||
org = await orgs.find_one({"org_id": org_id})
|
org = await orgs.find_one({"org_id": org_id})
|
||||||
|
|
||||||
if not org:
|
updated_org = OrganizationInDB(org_id=org_id, **org_object.dict())
|
||||||
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
|
||||||
|
|
||||||
updated_org = OrganizationInDB(
|
|
||||||
org_id=org_id, **org_object.dict())
|
|
||||||
|
|
||||||
|
# update org
|
||||||
await orgs.update_one({"org_id": org_id}, {"$set": updated_org.dict()})
|
await orgs.update_one({"org_id": org_id}, {"$set": updated_org.dict()})
|
||||||
|
|
||||||
return Organization(**updated_org.dict())
|
|
||||||
|
return updated_org.dict()
|
||||||
|
|
||||||
|
|
||||||
|
async def update_org_logo(
|
||||||
|
request: Request, logo_file: UploadFile, org_id: str, current_user: PublicUser
|
||||||
|
):
|
||||||
|
# verify org rights
|
||||||
|
await verify_org_rights(request, org_id, current_user, "update")
|
||||||
|
|
||||||
|
orgs = request.app.db["organizations"]
|
||||||
|
|
||||||
|
org = await orgs.find_one({"org_id": org_id})
|
||||||
|
|
||||||
|
|
||||||
|
name_in_disk = await upload_org_logo(logo_file)
|
||||||
|
|
||||||
|
# update org
|
||||||
|
org = await orgs.update_one({"org_id": org_id}, {"$set": {"logo": name_in_disk}})
|
||||||
|
|
||||||
|
return {"detail": "Logo updated"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_org(request: Request, org_id: str, current_user: PublicUser):
|
async def delete_org(request: Request, org_id: str, current_user: PublicUser):
|
||||||
|
|
||||||
await verify_org_rights(request, org_id, current_user, "delete")
|
await verify_org_rights(request, org_id, current_user, "delete")
|
||||||
|
|
||||||
orgs = request.app.db["organizations"]
|
orgs = request.app.db["organizations"]
|
||||||
|
|
@ -131,7 +138,8 @@ async def delete_org(request: Request, org_id: str, current_user: PublicUser):
|
||||||
|
|
||||||
if not org:
|
if not org:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
isDeleted = await orgs.delete_one({"org_id": org_id})
|
isDeleted = await orgs.delete_one({"org_id": org_id})
|
||||||
|
|
||||||
|
|
@ -143,56 +151,80 @@ async def delete_org(request: Request, org_id: str, current_user: PublicUser):
|
||||||
return {"detail": "Org deleted"}
|
return {"detail": "Org deleted"}
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="Unavailable database",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def get_orgs_by_user(request: Request, user_id: str, page: int = 1, limit: int = 10):
|
async def get_orgs_by_user(
|
||||||
|
request: Request, user_id: str, page: int = 1, limit: int = 10
|
||||||
|
):
|
||||||
orgs = request.app.db["organizations"]
|
orgs = request.app.db["organizations"]
|
||||||
user = request.app.db["users"]
|
user = request.app.db["users"]
|
||||||
|
|
||||||
if user_id == "anonymous":
|
if user_id == "anonymous":
|
||||||
|
# raise error
|
||||||
# raise error
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="User not logged in")
|
status_code=status.HTTP_409_CONFLICT, detail="User not logged in"
|
||||||
|
)
|
||||||
|
|
||||||
# get user orgs
|
# get user orgs
|
||||||
user_orgs = await user.find_one({"user_id": user_id})
|
user_orgs = await user.find_one({"user_id": user_id})
|
||||||
|
|
||||||
org_ids: list[UserOrganization] = []
|
org_ids: list[UserOrganization] = []
|
||||||
|
|
||||||
for org in user_orgs["orgs"]:
|
for org in user_orgs["orgs"]:
|
||||||
if org["org_role"] == "owner" or org["org_role"] == "editor" or org["org_role"] == "member":
|
if (
|
||||||
|
org["org_role"] == "owner"
|
||||||
|
or org["org_role"] == "editor"
|
||||||
|
or org["org_role"] == "member"
|
||||||
|
):
|
||||||
org_ids.append(org["org_id"])
|
org_ids.append(org["org_id"])
|
||||||
|
|
||||||
# find all orgs where org_id is in org_ids array
|
# find all orgs where org_id is in org_ids array
|
||||||
|
|
||||||
all_orgs = orgs.find({"org_id": {"$in": org_ids}}).sort(
|
all_orgs = (
|
||||||
"name", 1).skip(10 * (page - 1)).limit(100)
|
orgs.find({"org_id": {"$in": org_ids}})
|
||||||
|
.sort("name", 1)
|
||||||
|
.skip(10 * (page - 1))
|
||||||
|
.limit(100)
|
||||||
|
)
|
||||||
|
|
||||||
return [json.loads(json.dumps(org, default=str)) for org in await all_orgs.to_list(length=100)]
|
return [
|
||||||
|
json.loads(json.dumps(org, default=str))
|
||||||
|
for org in await all_orgs.to_list(length=100)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
#### Security ####################################################
|
#### Security ####################################################
|
||||||
|
|
||||||
async def verify_org_rights(request: Request, org_id: str, current_user: PublicUser, action: str,):
|
|
||||||
|
async def verify_org_rights(
|
||||||
|
request: Request,
|
||||||
|
org_id: str,
|
||||||
|
current_user: PublicUser,
|
||||||
|
action: str,
|
||||||
|
):
|
||||||
orgs = request.app.db["organizations"]
|
orgs = request.app.db["organizations"]
|
||||||
|
|
||||||
org = await orgs.find_one({"org_id": org_id})
|
org = await orgs.find_one({"org_id": org_id})
|
||||||
|
|
||||||
if not org:
|
if not org:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
# check if is owner of org
|
hasRoleRights = await verify_user_rights_with_roles(
|
||||||
# todo check if is admin of org
|
request, action, current_user.user_id, org_id, org_id
|
||||||
|
)
|
||||||
|
|
||||||
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, org_id, org_id)
|
if not hasRoleRights:
|
||||||
|
raise HTTPException(
|
||||||
# if not hasRoleRights and not isOwner:
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
# raise HTTPException(
|
detail="You do not have rights to this organization",
|
||||||
# status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this organization")
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
#### Security ####################################################
|
#### Security ####################################################
|
||||||
0
src/services/orgs/schemas/__init__.py
Normal file
0
src/services/orgs/schemas/__init__.py
Normal file
29
src/services/orgs/schemas/orgs.py
Normal file
29
src/services/orgs/schemas/orgs.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
from typing import Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from src.security.security import *
|
||||||
|
|
||||||
|
#### Classes ####################################################
|
||||||
|
|
||||||
|
|
||||||
|
class Organization(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
email: str
|
||||||
|
slug: str
|
||||||
|
logo: Optional[str]
|
||||||
|
default: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationInDB(Organization):
|
||||||
|
org_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class PublicOrganization(Organization):
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
email: str
|
||||||
|
slug: str
|
||||||
|
org_id: str
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return getattr(self, item)
|
||||||
|
|
@ -4,7 +4,7 @@ from uuid import uuid4
|
||||||
from fastapi import HTTPException, Request, status
|
from fastapi import HTTPException, Request, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from src.services.courses.chapters import get_coursechapters_meta
|
from src.services.courses.chapters import get_coursechapters_meta
|
||||||
from src.services.orgs import PublicOrganization
|
from src.services.orgs.orgs import PublicOrganization
|
||||||
|
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue