diff --git a/docker-compose.yml b/docker-compose.yml index e08ff00..5094e79 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -99,6 +99,8 @@ services: dockerfile: Dockerfile.dev container_name: oauth-backend restart: always + ports: + - "8000:8000" environment: - MONGODB_URL=mongodb://admin:admin123@mongodb:27017/oauth_db?authSource=admin - REDIS_URL=redis://redis:6379 diff --git a/oauth/backend/app/api/v1/endpoints/auth.py b/oauth/backend/app/api/v1/endpoints/auth.py index 7ffdece..837b1b7 100644 --- a/oauth/backend/app/api/v1/endpoints/auth.py +++ b/oauth/backend/app/api/v1/endpoints/auth.py @@ -1,8 +1,10 @@ from fastapi import APIRouter, HTTPException, Depends, status from fastapi.security import OAuth2PasswordRequestForm -from app.core.security import create_access_token, get_current_user -from app.models.user import User +from app.core.security import create_access_token, get_current_user, get_password_hash +from app.models.user import User, UserCreate from app.core.config import settings +from app.core.database import get_database +from datetime import datetime router = APIRouter() @@ -35,4 +37,49 @@ async def authorize(): @router.post("/token") async def token(): # TODO: Implement OAuth token endpoint - return {"message": "Token endpoint"} \ No newline at end of file + return {"message": "Token endpoint"} + +@router.post("/register", status_code=status.HTTP_201_CREATED) +async def register(user_data: UserCreate): + """Register a new user""" + # Get database + db = get_database() + # Check if user already exists + users_collection = db["users"] + existing_user = await users_collection.find_one({"email": user_data.email}) + + if existing_user: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Email already registered" + ) + + # Create new user + user_dict = { + "email": user_data.email, + "full_name": user_data.name, + "username": user_data.email.split("@")[0], # Use email prefix as username + "organization": user_data.organization, + "hashed_password": get_password_hash(user_data.password), + "role": "user", # Default role + "is_active": True, + "created_at": datetime.utcnow(), + "updated_at": datetime.utcnow() + } + + # Insert user into database + result = await users_collection.insert_one(user_dict) + + # Create access token for immediate login + access_token = create_access_token({"sub": user_data.email}) + + return { + "message": "User registered successfully", + "access_token": access_token, + "token_type": "bearer", + "user": { + "id": str(result.inserted_id), + "email": user_data.email, + "name": user_data.name + } + } \ No newline at end of file diff --git a/oauth/backend/app/db/__init__.py b/oauth/backend/app/db/__init__.py new file mode 100644 index 0000000..2f0b25d --- /dev/null +++ b/oauth/backend/app/db/__init__.py @@ -0,0 +1 @@ +# Database module \ No newline at end of file diff --git a/oauth/backend/app/db/mongodb.py b/oauth/backend/app/db/mongodb.py new file mode 100644 index 0000000..db096f2 --- /dev/null +++ b/oauth/backend/app/db/mongodb.py @@ -0,0 +1,20 @@ +import os +from motor.motor_asyncio import AsyncIOMotorClient +from typing import Optional + +mongodb_client: Optional[AsyncIOMotorClient] = None +database = None + +async def connect_to_mongo(): + global mongodb_client, database + mongodb_url = os.getenv("MONGODB_URL", "mongodb://localhost:27017") + mongodb_client = AsyncIOMotorClient(mongodb_url) + database = mongodb_client.oauth_db + +async def close_mongo_connection(): + global mongodb_client + if mongodb_client: + mongodb_client.close() + +async def get_database(): + return database \ No newline at end of file diff --git a/oauth/backend/app/models/user.py b/oauth/backend/app/models/user.py index 9bb6949..4a407c6 100644 --- a/oauth/backend/app/models/user.py +++ b/oauth/backend/app/models/user.py @@ -10,17 +10,21 @@ class UserRole(str, Enum): class UserBase(BaseModel): email: EmailStr - username: str - full_name: str + username: Optional[str] = None + full_name: Optional[str] = None role: UserRole = UserRole.USER is_active: bool = True phone_number: Optional[str] = None birth_date: Optional[str] = None gender: Optional[str] = None profile_picture: Optional[str] = None + organization: Optional[str] = None -class UserCreate(UserBase): +class UserCreate(BaseModel): + email: EmailStr password: str + name: str + organization: Optional[str] = None class UserUpdate(BaseModel): full_name: Optional[str] = None diff --git a/oauth/frontend/vite.config.ts b/oauth/frontend/vite.config.ts index 8b0f57b..bf958b4 100644 --- a/oauth/frontend/vite.config.ts +++ b/oauth/frontend/vite.config.ts @@ -4,4 +4,13 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + server: { + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true, + secure: false, + } + } + } })