From c52f1eb47874efc54f32eb02f046dcc3b67e71bb Mon Sep 17 00:00:00 2001 From: George Date: Fri, 17 Jul 2026 17:09:16 +0530 Subject: [PATCH] feat: --FastAPI added --- .gitignore | 2 +- __pycache__/database.cpython-312.pyc | Bin 0 -> 1118 bytes __pycache__/main.cpython-312.pyc | Bin 0 -> 6593 bytes __pycache__/models.cpython-312.pyc | Bin 0 -> 1478 bytes __pycache__/schemas.cpython-312.pyc | Bin 0 -> 1181 bytes database.py | 36 +++ experimental/main.py | 6 + screenCapt.py => experimental/screenCapt.py | 0 .../screenReciever.py | 0 .../screenSender.py | 2 +- server.py => experimental/server.py | 0 server1.log => experimental/server1.log | 0 main.py | 214 ++++++++++++++++++ models.py | 52 +++++ requirements.txt | 7 + schemas.py | 31 +++ static/index.html | 0 17 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 __pycache__/database.cpython-312.pyc create mode 100644 __pycache__/main.cpython-312.pyc create mode 100644 __pycache__/models.cpython-312.pyc create mode 100644 __pycache__/schemas.cpython-312.pyc create mode 100644 database.py create mode 100644 experimental/main.py rename screenCapt.py => experimental/screenCapt.py (100%) rename screenReciever.py => experimental/screenReciever.py (100%) rename screenSender.py => experimental/screenSender.py (98%) rename server.py => experimental/server.py (100%) rename server1.log => experimental/server1.log (100%) create mode 100644 main.py create mode 100644 models.py create mode 100644 requirements.txt create mode 100644 schemas.py create mode 100644 static/index.html diff --git a/.gitignore b/.gitignore index 8040210..b142766 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ venv/ - +.env # Ignore VSCode settings (optional) diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..166c3b50479774e7a72453098c059b551746038d GIT binary patch literal 1118 zcmah{J&Y4a6n?Y2Uhl5gKb+w}D6%X;5jVJqqF73l=uQVIKvw`MvdwDGGiUGJ?v9yR zb8(bN7P{g90TM-yo(dh5^y$+e(Kwq#;S@zmI*_kGh$3&iHX&6;+V|e~=FQvh{rpre zJAkKH^0q$J0RB*5aSC%|`Xd_qK!K77Jf*dW#G0oOW$Te18=f(Ubse6dKY#{#TbTecN~+HAC46g&;f z(K17ETgPmc?4u?sBmWj_!|vQ2|D*uebf1|w0%mosr14XL(b7bb=!mKOg1P&Uwe2ES>F;vx{F zoi56N#XfuwHhc#^X#0eq-kQ%4AG|w@NCG2F_kS@v@Qwa8?2sq=4r!5f?2daSpV=ke z4Wg{jjkshu3yp3_#il9hC^NIiQlD;!QwSRP$rg&g;Pn`OwNKn`j8|V8dZ9k4)NWZX zEa#qe)4Jol-qUj9tvrs=H%c9H*Ej?Bk9EkyrH}QedWW>CnZ{*i<~t@-d6^@xMM2c< zvv?#_kFv_Yj&oeZLN!*H>PE4`-4sOy+{?ox4P$m*2re=+-%@cx1?}V!`N?yq^UUZR+RGE8Hd$IeD(H?jHeWeV`(R>MCzbl7digCR3XIP`{D>t LO)ZnuXJq~Xa7PA; literal 0 HcmV?d00001 diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0786a23c17e4c71958617ce040a7d5fdefa7410a GIT binary patch literal 6593 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 Hr5^qRJKSo( literal 0 HcmV?d00001 diff --git a/__pycache__/schemas.cpython-312.pyc b/__pycache__/schemas.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0da248be4a14dda0db7302436bbc8e34eee5c79d GIT binary patch literal 1181 zcmY*YOKTKC5bk-;&TbyMNlc7E5K#t+E51M!!B_O45j44sk7nHN?2gW3?VhOXNm&Jt z-uw$n@=rYKVQugr9wfIw){7^rX0jpa!`EMTRee)kRr7UZ#3QiIRfDJJbwYmQVYsYq zvb6-{6JdmDF-b^3G~{}$C3>JIMqngnU?x^zX+$SC2{YaiW^#SaDB}aWxV7Nc-gWDA z9pBwR7hh8@3v-@F3HMD^xf)7-Cu2NT-nA@UiCWjAX0F^DNf^cVbD^w-h{vp1s+YKv zDueSU!bzYp66nj25I2~{bY`rZI|-TG;x@A`fiHJ}*~kh{hq>#OH7EqGz%Bedw6bbY z;U4(b@YEggN0qlMxws}cWWIq<-8c>Kdf(#*;r#0>^TDb43-sjtqZ3DcLwQM*(wL{M zybU~jHRMhw%yTYM<*tHO8YWy>9G|)2qTm|>Gr<;=8%Y{6&|UPa z%B2*8DODb&Nyb)V9%ZSdRM=>*BTS%^`6SEY zFJuci`FFa}&Jx~e@l3RM<2skm@~qQ%xzeVxDL7B5Y=eshE@Zt9+{d2U-sQR zC=LyU_e{X{u#v5YVBS3e+~6kDuxrpwlj#Msff)sJfSEh2oCz21hBHxN&bqs%K{%|y z>sEYE)v)vTxa>gXxY8q5)K!dwhpJ#-ntAksmv!`@KrkVUhWQc<`q+xd65zRro~-6v zLifZZ#EB_{1E^p`X)b2)IE_&B None: + if x_admin_key != ADMIN_API_KEY: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid or missing admin API key", + ) + + +AdminDependency = Annotated[None, Depends(verify_admin_key)] +DatabaseDependency = Annotated[Session, Depends(get_db)] + + +@app.get("/") +def root(): + return { + "message": "FastAPI admin service is running", + "admin_docs": "/docs", + } + + +@app.post( + "/admin/users", + response_model=UserResponse, + status_code=status.HTTP_201_CREATED, +) +def create_user( + user_data: UserCreate, + db: DatabaseDependency, + _: AdminDependency, +): + existing_user = db.scalar( + select(User).where( + or_( + User.username == user_data.username, + User.email == user_data.email, + ) + ) + ) + + if existing_user: + if existing_user.username == user_data.username: + detail = "Username already exists" + else: + detail = "Email address already exists" + + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=detail, + ) + + new_user = User( + username=user_data.username, + email=user_data.email, + hashed_password=password_hash.hash(user_data.password), + is_admin=user_data.is_admin, + ) + + db.add(new_user) + + try: + db.commit() + db.refresh(new_user) + except IntegrityError: + db.rollback() + + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Username or email already exists", + ) + + return new_user + + +@app.get( + "/admin/users", + response_model=list[UserResponse], +) +def list_users( + db: DatabaseDependency, + _: AdminDependency, +): + users = db.scalars( + select(User).order_by(User.id) + ).all() + + return list(users) + + +@app.get( + "/admin/users/{user_id}", + response_model=UserResponse, +) +def get_user( + user_id: int, + db: DatabaseDependency, + _: AdminDependency, +): + user = db.get(User, user_id) + + if user is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", + ) + + return user + + +@app.patch( + "/admin/users/{user_id}/disable", + response_model=UserResponse, +) +def disable_user( + user_id: int, + db: DatabaseDependency, + _: AdminDependency, +): + user = db.get(User, user_id) + + if user is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", + ) + + user.is_active = False + db.commit() + db.refresh(user) + + return user + + +@app.patch( + "/admin/users/{user_id}/enable", + response_model=UserResponse, +) +def enable_user( + user_id: int, + db: DatabaseDependency, + _: AdminDependency, +): + user = db.get(User, user_id) + + if user is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", + ) + + user.is_active = True + db.commit() + db.refresh(user) + + return user + + +@app.delete( + "/admin/users/{user_id}", + status_code=status.HTTP_204_NO_CONTENT, +) +def delete_user( + user_id: int, + db: DatabaseDependency, + _: AdminDependency, +): + user = db.get(User, user_id) + + if user is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="User not found", + ) + + db.delete(user) + db.commit() + + return Response(status_code=status.HTTP_204_NO_CONTENT) \ No newline at end of file diff --git a/models.py b/models.py new file mode 100644 index 0000000..7976a50 --- /dev/null +++ b/models.py @@ -0,0 +1,52 @@ +from datetime import datetime + +from sqlalchemy import Boolean, DateTime, String, func +from sqlalchemy.orm import Mapped, mapped_column + +from database import Base + + +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column( + primary_key=True, + index=True, + ) + + username: Mapped[str] = mapped_column( + String(50), + unique=True, + nullable=False, + index=True, + ) + + email: Mapped[str] = mapped_column( + String(255), + unique=True, + nullable=False, + index=True, + ) + + hashed_password: Mapped[str] = mapped_column( + String(255), + nullable=False, + ) + + is_active: Mapped[bool] = mapped_column( + Boolean, + default=True, + nullable=False, + ) + + is_admin: Mapped[bool] = mapped_column( + Boolean, + default=False, + nullable=False, + ) + + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + nullable=False, + ) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5f8a526 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +fastapi +uvicorn[standard] +sqlalchemy +psycopg[binary] +python-dotenv +pwdlib[argon2] +email-validator \ No newline at end of file diff --git a/schemas.py b/schemas.py new file mode 100644 index 0000000..5eeba79 --- /dev/null +++ b/schemas.py @@ -0,0 +1,31 @@ +from datetime import datetime + +from pydantic import BaseModel, ConfigDict, EmailStr, Field + + +class UserCreate(BaseModel): + username: str = Field( + min_length=3, + max_length=50, + pattern=r"^[a-zA-Z0-9_.-]+$", + ) + + email: EmailStr + + password: str = Field( + min_length=8, + max_length=128, + ) + + is_admin: bool = False + + +class UserResponse(BaseModel): + id: int + username: str + email: EmailStr + is_active: bool + is_admin: bool + created_at: datetime + + model_config = ConfigDict(from_attributes=True) \ No newline at end of file diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..e69de29