Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

UniManageAPI

سیستم مدیریت دانشگاهی بر پایه FastAPI

نحوه انجام عملیات های CRUD

عملیات درج (Create)

ورودی: مشخصات دانشجو را به عنوان ورودی وارد میکنیم

{
"stid": "40211415035",
"fname": " میو ماو",
"lname": "احمد",
"father": "رضااحمدی",
"birth": "1401/1/30",
"ids": "ب/12 123456",
"address": "میو میو",
"postalcode": "1234567890",
"cphone": "09123456789",
"hphone": "06633223358",
"major": "مهندسی برق قدرت",
"married": true,
"id": "1850527296",
"scourseids": [12342],
"lids": [777335],
"department": "فنی و مهندسی",
"borncity": "سمنان"
}

روتر Post بر اساس Scheme مشخص شده ورودی را دریافت و پردازش میکند

داده ها قبل از ثبت در پایگاه داده طبق استاندارد های تایین شده صحت سنجی میشوند. صحت سنجی ها در فایل datavalidation.py تعبیه شده و در روتر مربوطه مورد استفاده قرار میگیرند

awaitDataValidation.duplicate_stid_check(student.stid)

در نمونه فوق صحت سنجی مربوط به تکراری نبودن شماره دانشجویی فراخوانی شده که بصورت زیر عمل میکند

asyncdefduplicate_stid_check(stid: str) ->None:
""" Check if a student with the given student ID already exists in the database. """student_stid=student_collection.find_one({"stid": stid})
ifstudent_stid:
raiseHTTPException(
status_code=409, detail="Duplicate student id. Student already exists"
)

همانطور که مشاهده میشود برای صحت سنجی در مثال بالا اگر شماره دانشجویی ای که میخواهیم ثبت کنیم در پایگاه داده وجود داشته باشد ارور مناسب را دریافت میکنیم

و درصورتی که به ارور بر نخوریم رکورد ورودی در پایگاه داده ثبت خواهد شد و طبق response model چهار فیلد اولیه بازگردانی خواهد شد

course_data=student.model_dump()
student_collection.insert_one(course_data)
returncourse_data
classStudentOut(BaseModel):
stid: strfname: strlname: strfather: str

عملیات ثبت (Read)

ورودی: شماره دانشجویی

روتر Get بر اساس شماره دانشجویی به دیتابیس درخواست فرستاده و رکورد مربوط به دانشجو را دریافت میکند

@router.get("/GetStu/{student_id}", response_model=schemas.StudentUpdate)asyncdefget_student(student_id: str) ->dict[str, Any]:
record=student_collection.find_one({"stid": student_id})
ifnotrecord:
raiseHTTPException(
status_code=404, detail="Invalid student id. Student not found"
)
returnrecord

عملیات آپدیت (Update)

ورودی: شماره دانشجویی و پارامتر هایی که میخواهیم آپدیت کنیم

پارامتر های مدنظر بعد از دریافت شدن توسط روتر Patch به json تبدیل میشوند و مقادیر None از json حذف میشود

student_data=student.model_dump(exclude_unset=True)

سپس صحت سنجی روی مقادیر ورودی انجام شده و درصورت نبود ارور مقادیر تمدید شده و به عنوان خروجی باز گردانده میشوند

{
"cid": "12342",
"Updated values": [
{
"credit": "2""department": "دامپزشکی",
}
]
}

عملیات حذف (Delete)

ورودی: شماره دانشجویی

شماره دانشجویی توسط روتر Delete دریافت شده و رکورد دانشجوی مدنظر از پایگاه داده حذف میشود و سپس پیام تایید به علاوه شماره دانشجوی مدنظر بازگردانده میشود

@router.delete("/DelStu/{student_id}", status_code=200)asyncdefdelete_student(student_id: str):
delete_record=student_collection.find_one_and_delete({"stid": student_id})
ifnotdelete_record:
raiseHTTPException(status_code=400, detail="Student was not deleted")
return {"Student ID": student_id, "Deleted": True}

داکرایز کردن پروژه (Containerization)

بخش Fastapi

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

1- کانتینر با آخرین ورژن پایتون آماده میشود

2- مسیر اصلی پروژه را مشخص میکنیم /code

3- فایل نیازمندی های پروژه را در مسیر پروژه کپی میکنیم /code/requirements.txt

4- نیازمندی های پروژه را درون کانتینر نصب میکنیم

5- پروژه را از روی سیستم خود به داخل کانتینر میریزیم

6- پروژه را توسط uvicorn با پارامتر های مناسب اجرا میکنیم

داکر کامپوز

فایل کامپوز پروژه رو برای اجرای همزمان دو سرویس mongodb و fastapi ایجاد میکنیم.

سرویس fastapi از فایل dockerfile پروژه کانتینر را ساخته و اجرا میکند و سپس پورت 80 را از هاست به کانتینر تخصیص میدهد

فایل کامپوز همچنین کانتینر شامل اخرین ورژن mongodb را دریافت کرده و اجرا میکند و ولوم mongodb را به مسیر /var/lib/mongodb/data درون کانتینر تخصیص میدهد

سپس هر دو سرویس را درون یک نتورک به نام main گذاشته و اجرا میکند و همچنین هر دو سرویس تا وقتی که بصورت دستی متوقف نشوند در صورت کرش شدن خود به خود ری استارت میشوند

تست نویسی

تست ها درون فولر tests نوشته شده اند و به دو دسته تقسیم میشوند

تست های CRUD

بر روی هر روتر چهار عملیات CRUD انجام میشود

deftest_create_courses() ->None:
""" Test case for creating a new course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_get_course() ->None:
""" Test case for getting a course """response=client.get("/GetCou/12342")
assertresponse.status_code==200assertresponse.json() ==Course_sample
deftest_update_course() ->None:
""" Test case for updating a course """response=client.patch(
"/UpdCou/12342",
json={"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"},
)
assertresponse.status_code==200assertresponse.json() == {
"cid": "12342",
"Updated values:": [
{"cname": "میوععع", "department": "فنی و مهندسی", "credit": "2"}
],
}
deftest_delete_course() ->None:
""" Test case for deleting a course """response=client.delete("/DelCou/12342")
assertresponse.status_code==200assertresponse.json() == {"Course ID": "12342", "Deleted": True}

تست های صحت سنجی

تست های مرتبط با صحت سنجی روی داده های ورودی مرتبط با datavalidations.py

deftest_create_duplicate_course() ->None:
""" Test case for creating a duplicate course """response=client.post("/RegCou/", json=Course_sample)
assertresponse.status_code==409assertresponse.json() == {"detail": "Duplicate course id. Course already exists"}

راه اندازی روی سرور

در اینجا از سیستم عامل ubuntu 20 استفاده شده است ولی رویکرد در ورژن های دیگر اوبانتو و توضیع های مبتنی بر دبیان مشابه است

ابتدا وارد سرور شده و پکیچ های سیستم را اپدیت و آپگرید میکنیم

sudo apt update && apt upgrade -y

با استفاده از اسکریپت نصب اتوماتیک داکر اقدام به نصب آن میکنیم

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

پس از نصب داکر ابزار git را نصب میکنیم

sudo apt install git -y

سپس مخزن پروژه را از گیتهاب با استفاده از ابزار گیت کپی میکنیم

git clone https://github.com/meower1/UniManageAPI.git

به مسیر کپی شده رفته و فایل داکر کامپوز رو اجرا میکنیم

cd UniManageAPI
docker compose up -d

دستور اول (cd) به معنای change directory یا همان تغییر مسیر هست و برای تغییر مسیر از پوشه ای به پوشه دیگر استفاده میشود

دستور دوم فایل docker-compose.yaml را که درون مخزن پروژه قرار دارد را اجرا میکند این فایل شامل 3 سرویس میشود.

سرویس اول (MongoDB)

services:
mongo:
container_name: mongodbimage: mongo:latestrestart: unless-stoppednetworks:
- mainports:
- 27017:27017volumes:
- mongodb:/var/lib/mongodb/data

سرویس اول کانتینری با نام mongo اجرا میکند از این نام برای فرا خواندن این سرویس بجای ایپی محلی سرویس استفاده میشود. برای مثال در فایل database.py پروژه از این نام برای ادرس دهی به محل دیتابیس استفاده شده است اموزش و توضیحات بیشتر

سپس آن را درون شبکه main گذاشته و پورت 27017 را از دستگاه هاست یعنی vps فعلی که روی آن هستیم به کانتینر مپ میکنیم. در این حالت تمام درخواست ها به پورت 27107 ما به پورت 27017 کانتینر میرود

با دستور

restart: unless-stopped

به داکر میگوییم که تا وقتی بصورت دستی کانتینر را متوقف نکردیم آنرا استاپ نکن (یعنی در صورت کرش شدن بصورت خود به خود کانتینر رو ری استارت کند)

سپس در بخش

volumes:
- mongodb:/var/lib/mongodb/data

به داکر میگوییم که یک ولوم برای کانتینر mongo به اسم mongodb ساخته و آن را به مسیر /var/lib/mongodb/data متصل کند

در این صورت تمام اطلاعات مرتبط با این کانتینر درون ولوم mongodb ذخیره شده و درصورت تغییرات نظیر پاک شدن یا اپدیت شدن کانتینر این اطلاعات ثابت باقی میماند و حتی میتوانیم ولوم را به کانتینر های دیگر متصل کنیم. اجرای این عمل برای دیتابیس ها ضروری است

سرویس دوم (FastAPI)

fastapi:
build: .command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]ports:
- 8080:8080environment:
- PORT=8080volumes:
- ./app:/app/depends_on:
- mongonetworks:
- mainrestart: unless-stopped

در این سرویس fastapi با استفاده از فایل Dockerfile که در مسیر اصلی پروژه قرار دارد ساخته شده و پورت 8080 به آن تخصیص داده میشود

در اینجا (.) به معنای این است که فایل Dockerfile که کانتینر قرار است از روی آن ساخته شود هم در مسیر فعلی (یعنی مسیری که docker-compose.yaml در آن اجرا میشود) قرار دارد

محتوی Dockerfile:

FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
FROM python:latest

در اینجا کانتینری آماده شامل آخرین ورژن python را نصب میکنیم این کانتینر ابزار های پایه مورد استفاده مانند pip را بطور پیشفرض درون خود دارد

WORKDIR /app

در اینجا مسیر اصلی کاری پروژه را /app قرار میدهیم. با این کار داکر متوجه میشود که دستور های پروژه مانند COPY, RUN را در این مسیر اجرا کند.

COPY ./requirements.txt /app/requirements.txt

سپس فایل requirements.txt که شامل پکیج های پایتون مورد استفاده پروژه میشوند را از مسیر اصلی پروژه (root) به مسیر پروژه درون کانتینر /app/requirements.txt کپی میکنیم

  • پارامتر --no-cache-dir به pip میگوید که پکیج هایی را که نصب میکند را در cache نکند که به کوچک تر شدن فضای اشتغالی docker image کمک میکند

  • پارامتر --upgrade به pip میگوید پکیج هارا بعد از نصب به اخرین ورژن ارتقا دهد

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

در این مرحله نیازمندی های پروژه را که کپی کردیم داخل کانتینر نصب میکنیم

COPY ./app /app

در این مرحله تمامی فایل های مرتبط با پروژه که درون مسیر /app قرار دارند را به مسیر /app درون کانتینر کپی میکنیم

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

سپس پروژه را با استفاده از uvicorn اجرا میکنیم در اینجا به uvicorn میگوییم که app را که همان

app=Fastapi()

است را در فایل main پیدا کند سپس مسیر هاست را 0.0.0.0 مشخص میکنیم. در این حالت برنامه از هر آیپی ای قابل دسترس است درصورتی که این پارامتر را مشخص نکنیم این برنامه بطور پیشفرض از لوکال هاست (127.0.0.1) استفاده میکند و فقط از سیستم خودمان قابل دسترس خواهد بود

و همچنین پورت 8080 را به برنامه تخصیص میدهیم

در نتیجه تمامی درخواست ها به vps_ip:8080

به برنامه میرود. که البته جلو تر با تنظیمات nginx آدرس ورودی تغییر میکند

depends_on:
- mongo

در اینجا به داکر میگوییم که این کانتینر مبتکی به سرویس mongo است و تا وقتی که کانتینر آن اجرا نشده است این سرویس را اجرا نکن

سرویس سوم (Nginx)

nginx:
build: nginxports:
- 80:80
- 443:443depends_on:
- fastapinetworks:
- mainrestart: unless-stopped

در این سرویس یک کانتینر شامل nginx را نصب کرده و پورت 80,443 که بترتیب مربوط به https و http هستند را به آن تخصیص میدهیم

به آن میگوییم که مبتکی به سرویس fastapi هست و درون نتورک main قرار بگیرد و تا وقتی که بصورت دستی استاپ نشده است آنرا استاپ نکن

در نهایت درون فایل docker-compose.yaml مشخص میکنیم که سرویس های درون این فایل همگی درون شبکه main قرار دارند و این فایل شامل ولوم mongo میشود

networks:
main:
volumes:
mongodb:

Nginx

ابزاری با کاربرد های متنوع است که ما در اینجا از این ابزار برای Reverse proxy, Static file serving, SSL/TLS Management و HTTP to HTTPS Redirection استفاده میکنیم

Static File Serving

location /static/ {alias /app/static/;}

در این بخش فایل های استاتیک پروژه (فایل هایی که ثابت هستند و تغییر نمیکنند مثل کد های css/javascript و عکس ها) توسط nginx توضیع میشوند. Fastapi خود توانایی توضیع فایل های استاتیک را دارد ولی مزیت استفاده از nginx در سرعت و بهبود عملکردش در این حیطه است

در این بخش به nginx میگوییم که هروقت کاربر مسیر /static را فرا خواند به مسیر /app/static هدایت شود

Reverse Proxy

upstream app_server {server fastapi:8080 fail_timeout=0;}

در این بخش به nginx گفته میشود که درخواست هایی که به app می آید را به کانتینر fastapi پورت 8080 بفرستد

SSL/TLS Manamgement

server{listen443ssl;ssl_certificate /etc/letsencrypt/live/uk1.meower1.tech/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/uk1.meower1.tech/privkey.pem;include /etc/letsencrypt/options-ssl-nginx.conf;ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}

این بخش مربوط به مدیریت مجوز ssl است که بطور اتوماتیک توسط certbot گرفته شده است

HTTP to HTTPS Redirection

server{if($host = uk1.meower1.tech){return301 https://$host$request_uri;}listen80;server_name uk1.meower1.tech;return404;}

در این بخش nginx درخواست های http به سرور را به https منتقل میکند

About

Fastapi MongoDB application written for my internship project

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages