Initial commit for resumeformatter project

This commit is contained in:
Laxmi Khilnani
2025-10-14 19:51:35 +05:30
commit ee030b70bc
43 changed files with 1668 additions and 0 deletions

0
backend/app/__init__.py Normal file
View File

View File

6
backend/app/api/api.py Normal file
View File

@@ -0,0 +1,6 @@
from fastapi import APIRouter
from app.api.endpoints import people
api_router = APIRouter()
api_router.include_router(people.router, prefix="/people", tags=["people"])

View File

View File

@@ -0,0 +1,35 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from app.db.session import get_db
from app.schemas.person import Person, PersonCreate
from app.crud import person
router = APIRouter()
@router.get("/", response_model=List[Person])
def get_all_people(db: Session = Depends(get_db)):
"""
Get all people
"""
return person.get_all(db)
@router.post("/", response_model=Person, status_code=201)
def create_person(
*,
db: Session = Depends(get_db),
person_in: PersonCreate
):
"""
Create a new person
"""
try:
return person.create(db=db, obj_in=person_in)
except Exception as e:
raise HTTPException(
status_code=500,
detail="Failed to create the person."
)

View File

@@ -0,0 +1,25 @@
from typing import Optional, List
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Simple settings class without using BaseSettings
class Settings:
"""
Application settings
"""
APP_NAME: str = os.getenv("APP_NAME", "ResumeFormatter")
API_V1_STR: str = f"/{APP_NAME}/api"
PROJECT_NAME: str = "Profile Linker API"
# CORS settings
BACKEND_CORS_ORIGINS: List[str] = ["*"]
# Database settings - using in-memory database by default
# In a production environment, you would use a real database connection string
DATABASE_URL: Optional[str] = None
settings = Settings()

View File

@@ -0,0 +1 @@
from . import person

View File

@@ -0,0 +1,27 @@
from sqlalchemy.orm import Session
from app.models.person import Person
from app.schemas.person import PersonCreate
import uuid
def get_all(db: Session) -> list[Person]:
"""
Get all people from the database
"""
return db.query(Person).all()
def create(db: Session, *, obj_in: PersonCreate) -> Person:
"""
Create a new person in the database
"""
db_obj = Person(
id=str(uuid.uuid4()),
firstName=obj_in.firstName,
lastName=obj_in.lastName,
linkedinUrl=obj_in.linkedinUrl
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj

3
backend/app/db/base.py Normal file
View File

@@ -0,0 +1,3 @@
# Import all the models, so that Base has them before being imported by Alembic
from app.db.base_class import Base # noqa
from app.models.person import Person # noqa

View File

@@ -0,0 +1,13 @@
from typing import Any
from sqlalchemy.ext.declarative import as_declarative, declared_attr
@as_declarative()
class Base:
id: Any
__name__: str
# Generate __tablename__ automatically based on class name
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()

19
backend/app/db/session.py Normal file
View File

@@ -0,0 +1,19 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.db.base_class import Base
# Use a file-based SQLite database instead of in-memory
SQLALCHEMY_DATABASE_URL = "sqlite:///./ResumeFormatter.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Dependency to get DB session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

195
backend/app/main.py Normal file
View File

@@ -0,0 +1,195 @@
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
import os
import pathlib
from dotenv import load_dotenv
from app.api.api import api_router
from app.core.config import settings
from app.db.session import engine
from app.db.base import Base
# Load environment variables
load_dotenv()
# Create tables in the database
Base.metadata.create_all(bind=engine)
app = FastAPI(
title=settings.PROJECT_NAME,
openapi_url=f"{settings.API_V1_STR}/openapi.json",
docs_url=f"{settings.API_V1_STR}/docs",
redoc_url=f"{settings.API_V1_STR}/redoc"
)
# Set up CORS
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API router
app.include_router(api_router, prefix=settings.API_V1_STR)
# Check if dist directory exists - use absolute path
dist_path = pathlib.Path("/app/dist")
if dist_path.exists():
# Mount static files from the dist directory if it exists
assets_path = dist_path / "assets"
if assets_path.exists():
# Mount assets with APP_NAME prefix
app.mount(f"/{settings.APP_NAME}/assets", StaticFiles(directory=str(assets_path)), name="assets")
print(f"Successfully mounted assets from {assets_path} at /{settings.APP_NAME}/assets")
else:
print(f"Warning: Dist directory {dist_path} does not exist")
# Try local development path with frontend folder
local_dist_path = pathlib.Path("frontend/dist")
if local_dist_path.exists():
local_assets_path = local_dist_path / "assets"
if local_assets_path.exists():
# Mount assets with APP_NAME prefix
app.mount(f"/{settings.APP_NAME}/assets", StaticFiles(directory=str(local_assets_path)), name="assets")
print(f"Successfully mounted assets from {local_assets_path} at /{settings.APP_NAME}/assets")
@app.get("/api/health")
async def health_check():
return {"status": "ok"}
# Redirect root to APP_NAME
@app.get("/")
async def redirect_to_app():
return RedirectResponse(f"/{settings.APP_NAME}")
# Handle index.css request
@app.get("/index.css")
async def serve_css():
# Try to serve from /app/dist (container path)
css_path = pathlib.Path("/app/dist/index.css")
if css_path.exists():
return FileResponse(css_path)
# Try to serve from frontend/dist (local development path)
local_css_path = pathlib.Path("frontend/dist/index.css")
if local_css_path.exists():
return FileResponse(local_css_path)
return {"error": "CSS file not found"}
# Handle vite.svg request
@app.get("/vite.svg")
async def serve_favicon():
# Try to serve from /app/dist (container path)
favicon_path = pathlib.Path("/app/dist/vite.svg")
if favicon_path.exists():
return FileResponse(favicon_path)
# Try to serve from frontend/dist (local development path)
local_favicon_path = pathlib.Path("frontend/dist/vite.svg")
if local_favicon_path.exists():
return FileResponse(local_favicon_path)
return {"error": "Favicon not found"}
# Serve index.html for the APP_NAME route
@app.get("/{app_name}")
async def serve_app(app_name: str):
if app_name != settings.APP_NAME:
return {"error": "App not found"}
# Try to serve from /app/dist (container path)
index_path = pathlib.Path("/app/dist/index.html")
if index_path.exists():
return FileResponse(index_path)
# Try to serve from frontend/dist (local development path)
local_index_path = pathlib.Path("frontend/dist/index.html")
if local_index_path.exists():
return FileResponse(local_index_path)
# If neither exists, return a simple HTML response
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>Profile Linker API</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }
h1 { color: #333; }
.container { max-width: 800px; margin: 0 auto; }
.message { background-color: #f8f9fa; border-left: 4px solid #007bff; padding: 15px; }
code { background-color: #f1f1f1; padding: 2px 5px; border-radius: 3px; }
</style>
</head>
<body>
<div class="container">
<h1>FastAPI Server Running</h1>
<div class="message">
<p>The FastAPI server is running correctly, but the frontend build files are not available.</p>
<p>To see the frontend, make sure to build it first with <code>npm run build</code>.</p>
<p>API endpoints are available at <a href="/docs">/docs</a>.</p>
</div>
</div>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
# Serve index.html for all other routes to support SPA routing
@app.get("/{app_name}/{full_path:path}")
async def serve_spa(app_name: str, full_path: str):
if app_name != settings.APP_NAME:
return {"error": "App not found"}
# Check if the path is an API route
if full_path.startswith("api/"):
return {"error": "API route not found"}
# Try to serve from /app/dist (container path)
index_path = pathlib.Path("/app/dist/index.html")
if index_path.exists():
return FileResponse(index_path)
# Try to serve from frontend/dist (local development path)
local_index_path = pathlib.Path("frontend/dist/index.html")
if local_index_path.exists():
return FileResponse(local_index_path)
# If neither exists, return a simple HTML response
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>Profile Linker API</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }
h1 { color: #333; }
.container { max-width: 800px; margin: 0 auto; }
.message { background-color: #f8f9fa; border-left: 4px solid #007bff; padding: 15px; }
code { background-color: #f1f1f1; padding: 2px 5px; border-radius: 3px; }
</style>
</head>
<body>
<div class="container">
<h1>FastAPI Server Running</h1>
<div class="message">
<p>The FastAPI server is running correctly, but the frontend build files are not available.</p>
<p>To see the frontend, make sure to build it first with <code>npm run build</code>.</p>
<p>API endpoints are available at <a href="/docs">/docs</a>.</p>
</div>
</div>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)

View File

View File

@@ -0,0 +1,12 @@
from sqlalchemy import Column, String
from app.db.base_class import Base
class Person(Base):
"""
Database model for a person
"""
id = Column(String, primary_key=True, index=True)
firstName = Column(String, nullable=False)
lastName = Column(String, nullable=False)
linkedinUrl = Column(String, nullable=False)

View File

View File

@@ -0,0 +1,34 @@
from pydantic import BaseModel, Field, HttpUrl
class PersonBase(BaseModel):
"""
Base schema for a person
"""
firstName: str
lastName: str
linkedinUrl: str
class PersonCreate(PersonBase):
"""
Schema for creating a new person
"""
pass
class PersonInDBBase(PersonBase):
"""
Base schema for a person in the database
"""
id: str
class Config:
from_attributes = True
class Person(PersonInDBBase):
"""
Schema for returning a person
"""
pass