Skip to content

Commit eb8aa68

Browse files
Khushi YadavKhushi Yadav
authored andcommitted
Stop tracking Alembic and app.db files
1 parent 9afdb68 commit eb8aa68

File tree

7 files changed

+95
-98
lines changed

7 files changed

+95
-98
lines changed

backend/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ app.egg-info
66
htmlcov
77
.cache
88
.venv
9+
10+
app/alembic/*
11+
alembic.ini
12+
app.db
13+
app/alembic/versions*

backend/app/api/deps.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
from app.core.db import engine
1414
from app.models import TokenPayload, User
1515

16+
# OAuth2 scheme
1617
reusable_oauth2 = OAuth2PasswordBearer(
1718
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
1819
)
1920

2021

22+
# Dependency for DB session
2123
def get_db() -> Generator[Session, None, None]:
2224
with Session(engine) as session:
2325
yield session
@@ -27,6 +29,7 @@ def get_db() -> Generator[Session, None, None]:
2729
TokenDep = Annotated[str, Depends(reusable_oauth2)]
2830

2931

32+
# ✅ FIXED: ensure token_data.sub (user_id) is used as string
3033
def get_current_user(session: SessionDep, token: TokenDep) -> User:
3134
try:
3235
payload = jwt.decode(
@@ -38,17 +41,24 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:
3841
status_code=status.HTTP_403_FORBIDDEN,
3942
detail="Could not validate credentials",
4043
)
41-
user = session.get(User, token_data.sub)
44+
45+
# ✅ Ensure we query using string ID
46+
user_id = str(token_data.sub)
47+
user = session.get(User, user_id)
48+
4249
if not user:
4350
raise HTTPException(status_code=404, detail="User not found")
4451
if not user.is_active:
4552
raise HTTPException(status_code=400, detail="Inactive user")
53+
4654
return user
4755

4856

57+
# Dependency for current user
4958
CurrentUser = Annotated[User, Depends(get_current_user)]
5059

5160

61+
# Superuser check
5262
def get_current_active_superuser(current_user: CurrentUser) -> User:
5363
if not current_user.is_superuser:
5464
raise HTTPException(

backend/app/api/routes/items.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import uuid
21
from typing import Any
3-
42
from fastapi import APIRouter, HTTPException
53
from sqlmodel import func, select
64

@@ -17,7 +15,6 @@ def read_items(
1715
"""
1816
Retrieve items.
1917
"""
20-
2118
if current_user.is_superuser:
2219
count_statement = select(func.count()).select_from(Item)
2320
count = session.exec(count_statement).one()
@@ -42,7 +39,7 @@ def read_items(
4239

4340

4441
@router.get("/{id}", response_model=ItemPublic)
45-
def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
42+
def read_item(session: SessionDep, current_user: CurrentUser, id: str) -> Any:
4643
"""
4744
Get item by ID.
4845
"""
@@ -73,7 +70,7 @@ def update_item(
7370
*,
7471
session: SessionDep,
7572
current_user: CurrentUser,
76-
id: uuid.UUID,
73+
id: str,
7774
item_in: ItemUpdate,
7875
) -> Any:
7976
"""
@@ -92,9 +89,9 @@ def update_item(
9289
return item
9390

9491

95-
@router.delete("/{id}")
92+
@router.delete("/{id}", response_model=Message)
9693
def delete_item(
97-
session: SessionDep, current_user: CurrentUser, id: uuid.UUID
94+
session: SessionDep, current_user: CurrentUser, id: str
9895
) -> Message:
9996
"""
10097
Delete an item.

backend/app/api/routes/users.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import uuid
21
from typing import Any
3-
42
from fastapi import APIRouter, Depends, HTTPException
53
from sqlmodel import col, delete, func, select
64

@@ -38,7 +36,6 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
3836
"""
3937
Retrieve users.
4038
"""
41-
4239
count_statement = select(func.count()).select_from(User)
4340
count = session.exec(count_statement).one()
4441

@@ -82,7 +79,6 @@ def update_user_me(
8279
"""
8380
Update own user.
8481
"""
85-
8682
if user_in.email:
8783
existing_user = crud.get_user_by_email(session=session, email=user_in.email)
8884
if existing_user and existing_user.id != current_user.id:
@@ -157,7 +153,7 @@ def register_user(session: SessionDep, user_in: UserRegister) -> Any:
157153

158154
@router.get("/{user_id}", response_model=UserPublic)
159155
def read_user_by_id(
160-
user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
156+
user_id: str, session: SessionDep, current_user: CurrentUser
161157
) -> Any:
162158
"""
163159
Get a specific user by id.
@@ -181,13 +177,12 @@ def read_user_by_id(
181177
def update_user(
182178
*,
183179
session: SessionDep,
184-
user_id: uuid.UUID,
180+
user_id: str,
185181
user_in: UserUpdate,
186182
) -> Any:
187183
"""
188184
Update a user.
189185
"""
190-
191186
db_user = session.get(User, user_id)
192187
if not db_user:
193188
raise HTTPException(
@@ -207,7 +202,7 @@ def update_user(
207202

208203
@router.delete("/{user_id}", dependencies=[Depends(get_current_active_superuser)])
209204
def delete_user(
210-
session: SessionDep, current_user: CurrentUser, user_id: uuid.UUID
205+
session: SessionDep, current_user: CurrentUser, user_id: str
211206
) -> Message:
212207
"""
213208
Delete a user.

backend/app/core/config.py

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,66 @@
11
import secrets
22
import warnings
3-
from typing import Annotated, Any, Literal
4-
5-
from pydantic import (
6-
AnyUrl,
7-
BeforeValidator,
8-
EmailStr,
9-
HttpUrl,
10-
PostgresDsn,
11-
computed_field,
12-
model_validator,
13-
)
3+
from typing import Any, Annotated, Literal
4+
5+
from pydantic import AnyUrl, BeforeValidator, EmailStr, HttpUrl, computed_field, model_validator
146
from pydantic_settings import BaseSettings, SettingsConfigDict
157
from typing_extensions import Self
8+
from pydantic import AnyUrl, HttpUrl, EmailStr
169

1710

1811
def parse_cors(v: Any) -> list[str] | str:
12+
"""Parses CORS origins from env or defaults."""
1913
if isinstance(v, str) and not v.startswith("["):
2014
return [i.strip() for i in v.split(",") if i.strip()]
21-
elif isinstance(v, list | str):
15+
elif isinstance(v, (list, str)):
2216
return v
2317
raise ValueError(v)
2418

2519

2620
class Settings(BaseSettings):
21+
"""
22+
Application settings (no .env required).
23+
Uses SQLite instead of PostgreSQL.
24+
"""
25+
2726
model_config = SettingsConfigDict(
28-
# Use top level .env file (one level above ./backend/)
29-
env_file="../.env",
3027
env_ignore_empty=True,
3128
extra="ignore",
3229
)
30+
31+
# --- Core app settings ---
3332
API_V1_STR: str = "/api/v1"
33+
PROJECT_NAME: str = "My SQLite App"
3434
SECRET_KEY: str = secrets.token_urlsafe(32)
35-
# 60 minutes * 24 hours * 8 days = 8 days
36-
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
35+
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 # 8 days
3736
FRONTEND_HOST: str = "http://localhost:5173"
3837
ENVIRONMENT: Literal["local", "staging", "production"] = "local"
38+
SENTRY_DSN: HttpUrl | None = None # ✅ Added this line
3939

40+
41+
# --- CORS ---
4042
BACKEND_CORS_ORIGINS: Annotated[
4143
list[AnyUrl] | str, BeforeValidator(parse_cors)
4244
] = []
4345

44-
@computed_field # type: ignore[prop-decorator]
46+
@computed_field
4547
@property
4648
def all_cors_origins(self) -> list[str]:
49+
"""Combine backend and frontend origins for CORS."""
4750
return [str(origin).rstrip("/") for origin in self.BACKEND_CORS_ORIGINS] + [
4851
self.FRONTEND_HOST
4952
]
5053

51-
PROJECT_NAME: str
52-
SENTRY_DSN: HttpUrl | None = None
53-
POSTGRES_SERVER: str
54-
POSTGRES_PORT: int = 5432
55-
POSTGRES_USER: str
56-
POSTGRES_PASSWORD: str = ""
57-
POSTGRES_DB: str = ""
54+
# --- Database (SQLite only) ---
55+
SQLITE_DB_PATH: str = "./app.db"
5856

59-
@computed_field # type: ignore[prop-decorator]
57+
@computed_field
6058
@property
61-
def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
62-
return PostgresDsn.build(
63-
scheme="postgresql+psycopg",
64-
username=self.POSTGRES_USER,
65-
password=self.POSTGRES_PASSWORD,
66-
host=self.POSTGRES_SERVER,
67-
port=self.POSTGRES_PORT,
68-
path=self.POSTGRES_DB,
69-
)
59+
def SQLALCHEMY_DATABASE_URI(self) -> str:
60+
"""SQLAlchemy connection string for SQLite."""
61+
return f"sqlite:///{self.SQLITE_DB_PATH}"
7062

63+
# --- Email (optional) ---
7164
SMTP_TLS: bool = True
7265
SMTP_SSL: bool = False
7366
SMTP_PORT: int = 587
@@ -85,21 +78,19 @@ def _set_default_emails_from(self) -> Self:
8578

8679
EMAIL_RESET_TOKEN_EXPIRE_HOURS: int = 48
8780

88-
@computed_field # type: ignore[prop-decorator]
81+
@computed_field
8982
@property
9083
def emails_enabled(self) -> bool:
9184
return bool(self.SMTP_HOST and self.EMAILS_FROM_EMAIL)
9285

86+
# --- Superuser ---
9387
EMAIL_TEST_USER: EmailStr = "[email protected]"
94-
FIRST_SUPERUSER: EmailStr
95-
FIRST_SUPERUSER_PASSWORD: str
88+
FIRST_SUPERUSER: EmailStr = "[email protected]"
89+
FIRST_SUPERUSER_PASSWORD: str = "changeme"
9690

9791
def _check_default_secret(self, var_name: str, value: str | None) -> None:
98-
if value == "changethis":
99-
message = (
100-
f'The value of {var_name} is "changethis", '
101-
"for security, please change it, at least for deployments."
102-
)
92+
if value == "changeme":
93+
message = f'The value of {var_name} is "changeme", please change it.'
10394
if self.ENVIRONMENT == "local":
10495
warnings.warn(message, stacklevel=1)
10596
else:
@@ -108,12 +99,9 @@ def _check_default_secret(self, var_name: str, value: str | None) -> None:
10899
@model_validator(mode="after")
109100
def _enforce_non_default_secrets(self) -> Self:
110101
self._check_default_secret("SECRET_KEY", self.SECRET_KEY)
111-
self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD)
112-
self._check_default_secret(
113-
"FIRST_SUPERUSER_PASSWORD", self.FIRST_SUPERUSER_PASSWORD
114-
)
115-
102+
self._check_default_secret("FIRST_SUPERUSER_PASSWORD", self.FIRST_SUPERUSER_PASSWORD)
116103
return self
117104

118105

106+
# Instantiate settings immediately
119107
settings = Settings() # type: ignore

backend/app/core/db.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,31 @@
44
from app.core.config import settings
55
from app.models import User, UserCreate
66

7-
engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI))
8-
9-
10-
# make sure all SQLModel models are imported (app.models) before initializing DB
11-
# otherwise, SQLModel might fail to initialize relationships properly
12-
# for more details: https://github.com/fastapi/full-stack-fastapi-template/issues/28
7+
# SQLite engine (no env, no Postgres)
8+
engine = create_engine(
9+
settings.SQLALCHEMY_DATABASE_URI,
10+
connect_args={"check_same_thread": False}
11+
)
1312

1413

1514
def init_db(session: Session) -> None:
16-
# Tables should be created with Alembic migrations
17-
# But if you don't want to use migrations, create
18-
# the tables un-commenting the next lines
19-
# from sqlmodel import SQLModel
20-
21-
# This works because the models are already imported and registered from app.models
22-
# SQLModel.metadata.create_all(engine)
15+
# If not using Alembic, uncomment this to auto-create tables
16+
from sqlmodel import SQLModel
17+
SQLModel.metadata.create_all(engine)
2318

24-
user = session.exec(
19+
user = session.exec(
2520
select(User).where(User.email == settings.FIRST_SUPERUSER)
2621
).first()
27-
if not user:
22+
if not user:
2823
user_in = UserCreate(
2924
email=settings.FIRST_SUPERUSER,
3025
password=settings.FIRST_SUPERUSER_PASSWORD,
3126
is_superuser=True,
3227
)
33-
user = crud.create_user(session=session, user_create=user_in)
28+
crud.create_user(session=session, user_create=user_in)
29+
30+
31+
32+
33+
34+

0 commit comments

Comments
 (0)