Skip to content

Commit 94f64fa

Browse files
committed
logger added, tier related models and schemas, user model and schemas updated
1 parent f14e552 commit 94f64fa

File tree

9 files changed

+225
-8
lines changed

9 files changed

+225
-8
lines changed

src/app/api/dependencies.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
from typing import Annotated
22

33
from app.core.security import SECRET_KEY, ALGORITHM, oauth2_scheme
4+
from app.core.config import RedisRateLimiterSettings, settings
45

56
from sqlalchemy.ext.asyncio import AsyncSession
67
from jose import JWTError, jwt
7-
from fastapi import Depends, HTTPException
8+
from fastapi import (
9+
Depends,
10+
HTTPException,
11+
Request,
12+
status
13+
)
814

915
from app.core.database import async_get_db
1016
from app.core.models import TokenData

src/app/core/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ class RedisQueueSettings(BaseSettings):
8585

8686

8787
class RedisRateLimiterSettings(BaseSettings):
88-
REDIS_RATE_LIMITER_HOST: str = config("REDIS_RATE_LIMITER_HOST", default="localhost")
89-
REDIS_RATE_LIMITER_PORT: int = config("REDIS_RATE_LIMITER_PORT", default=6379)
90-
REDIS_RATE_LIMITER_URL: str = f"redis://{REDIS_RATE_LIMITER_HOST}:{REDIS_RATE_LIMITER_PORT}"
88+
REDIS_RATE_LIMIT_HOST: str = config("REDIS_RATE_LIMIT_HOST", default="localhost")
89+
REDIS_RATE_LIMIT_PORT: int = config("REDIS_RATE_LIMIT_PORT", default=6379)
90+
REDIS_RATE_LIMIT_URL: str = f"redis://{REDIS_RATE_LIMIT_HOST}:{REDIS_RATE_LIMIT_PORT}"
9191

9292

9393
class EnvironmentOption(Enum):

src/app/core/logger.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import logging
2+
from logging.handlers import RotatingFileHandler
3+
import os
4+
5+
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs')
6+
if not os.path.exists(LOG_DIR):
7+
os.makedirs(LOG_DIR)
8+
9+
LOG_FILE_PATH = os.path.join(LOG_DIR, 'app.log')
10+
11+
LOGGING_LEVEL = logging.INFO
12+
LOGGING_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
13+
14+
logging.basicConfig(level=LOGGING_LEVEL, format=LOGGING_FORMAT)
15+
16+
file_handler = RotatingFileHandler(LOG_FILE_PATH, maxBytes=10485760, backupCount=5)
17+
file_handler.setLevel(LOGGING_LEVEL)
18+
file_handler.setFormatter(logging.Formatter(LOGGING_FORMAT))
19+
20+
logging.getLogger('').addHandler(file_handler)

src/app/core/rate_limit.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import time
1+
from datetime import datetime
2+
from typing import Union, Dict
23

34
from fastapi import HTTPException, Request, Depends, status
45
from redis.asyncio import Redis, ConnectionPool
6+
from sqlalchemy.ext.asyncio import AsyncSession
57

6-
from app.api.dependencies import get_current_user
8+
from app.core.logger import logging
9+
from app.schemas.user import UserRead
10+
from app.schemas.tier import TierRead
11+
12+
logger = logging.getLogger(__name__)
713

814
pool: ConnectionPool | None = None
915
client: Redis | None = None

src/app/logs/README.MD

Whitespace-only changes.

src/app/models/tier.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Optional, List
2+
from datetime import datetime
3+
4+
from sqlalchemy import String, DateTime, ForeignKey, Integer
5+
from sqlalchemy.orm import Mapped, mapped_column, relationship
6+
7+
from app.core.database import Base
8+
9+
class RateLimit(Base):
10+
__tablename__ = "rate_limit"
11+
12+
id: Mapped[int] = mapped_column(
13+
"id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False
14+
)
15+
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
16+
path: Mapped[str] = mapped_column(String, nullable=False)
17+
limit: Mapped[int] = mapped_column(Integer, nullable=False)
18+
period: Mapped[int] = mapped_column(Integer, nullable=False)
19+
20+
tier_rate_limits: Mapped["TierRateLimit"] = relationship(back_populates="rate_limit", cascade="all, delete", lazy="selectin", default_factory=list)
21+
22+
23+
class Tier(Base):
24+
__tablename__ = "tier"
25+
26+
id: Mapped[int] = mapped_column(
27+
"id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False
28+
)
29+
name: Mapped[str] = mapped_column(String, nullable=False, unique=True)
30+
31+
users: Mapped[List["User"]] = relationship(back_populates="tier", cascade="save-update, merge", lazy="selectin", default_factory=list)
32+
tier_rate_limits: Mapped["TierRateLimit"] = relationship(back_populates="tier", cascade="all, delete", lazy="selectin", default_factory=list)
33+
created_at: Mapped[datetime] = mapped_column(
34+
DateTime, default_factory=datetime.utcnow
35+
)
36+
updated_at: Mapped[Optional[datetime]] = mapped_column(default=None)
37+
38+
39+
class TierRateLimit(Base):
40+
__tablename__ = "tier_rate_limit"
41+
42+
id: Mapped[int] = mapped_column(
43+
"id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False
44+
)
45+
46+
rate_limit_id: Mapped[int] = mapped_column(ForeignKey("rate_limit.id"), index=True)
47+
tier_id: Mapped[int] = mapped_column(ForeignKey("tier.id"), index=True)
48+
49+
created_at: Mapped[datetime] = mapped_column(
50+
DateTime, default_factory=datetime.utcnow
51+
)
52+
53+
tier: Mapped[Tier] = relationship(back_populates="tier_rate_limits", init=False)
54+
rate_limit: Mapped[RateLimit] = relationship(back_populates="tier_rate_limits", init=False)

src/app/models/user.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
import uuid as uuid_pkg
33
from datetime import datetime
44

5-
from sqlalchemy import String, DateTime
5+
from sqlalchemy import String, DateTime, ForeignKey
66
from sqlalchemy.orm import Mapped, mapped_column, relationship
77

88
from app.core.database import Base
99
from app.models.post import Post
10+
from app.models.tier import Tier
1011

1112
class User(Base):
1213
__tablename__ = "user"
@@ -32,4 +33,7 @@ class User(Base):
3233
is_deleted: Mapped[bool] = mapped_column(default=False, index=True)
3334
is_superuser: Mapped[bool] = mapped_column(default=False)
3435

36+
tier_id: Mapped[int | None] = mapped_column(ForeignKey("tier.id"), index=True, default=None)
37+
3538
posts: Mapped[List[Post]] = relationship(back_populates="user", cascade="all, delete", lazy="selectin", default_factory=list)
39+
tier: Mapped[Tier | None] = relationship(back_populates="users", lazy="selectin", default=None)

src/app/schemas/tier.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
from typing import Annotated
2+
from datetime import datetime
3+
4+
from pydantic import BaseModel, Field, ConfigDict
5+
6+
from app.core.models import TimestampModel
7+
8+
# -------------- Rate Limit --------------
9+
class RateLimitBase(BaseModel):
10+
path: Annotated[
11+
str,
12+
Field(examples=["/users"])
13+
]
14+
limit: Annotated[
15+
int,
16+
Field(examples=[5])
17+
]
18+
period: Annotated[
19+
int,
20+
Field(example=[60])
21+
]
22+
name: Annotated[
23+
str | None,
24+
Field(
25+
default=None,
26+
examples=["/users:5:60"]
27+
)
28+
]
29+
30+
31+
class RateLimit(TimestampModel, RateLimitBase):
32+
pass
33+
34+
35+
class RateLimitRead(RateLimitBase):
36+
id: int
37+
38+
39+
class RateLimitCreate(RateLimitBase):
40+
model_config = ConfigDict(extra='forbid')
41+
42+
43+
class RateLimitCreateInternal(RateLimitCreate):
44+
pass
45+
46+
47+
class RateLimitUpdate(BaseModel):
48+
pass
49+
50+
51+
class RateLimitDelete(BaseModel):
52+
pass
53+
54+
# -------------- Tier --------------
55+
class TierBase(BaseModel):
56+
name: Annotated[
57+
str,
58+
Field(examples=["free"])
59+
]
60+
61+
62+
class Tier(TimestampModel, TierBase):
63+
pass
64+
65+
66+
class TierRead(TierBase):
67+
id: int
68+
created_at: datetime
69+
70+
71+
class TierCreate(TierBase):
72+
pass
73+
74+
75+
class TierCreateInternal(TierCreate):
76+
pass
77+
78+
79+
class TierUpdate(BaseModel):
80+
name: str | None = None
81+
82+
83+
class TierUpdateInternal(TierUpdate):
84+
updated_at: datetime
85+
86+
87+
class TierDelete(BaseModel):
88+
pass
89+
90+
91+
# -------------- Tier Rate Limit --------------
92+
class TierRateLimitBase(BaseModel):
93+
rate_limit_id: int
94+
tier_id: int
95+
96+
97+
class TierRateLimit(TimestampModel, TierRateLimitBase):
98+
id: int
99+
created_at: datetime
100+
101+
102+
class TierRateLimitRead(TierRateLimitBase):
103+
pass
104+
105+
106+
class TierRateLimitCreate(TierRateLimitBase):
107+
pass
108+
109+
110+
class TierRateLimitCreateInternal(TierRateLimitCreate):
111+
pass
112+
113+
114+
class TierRateLimitUpdate(BaseModel):
115+
pass
116+
117+
118+
class TierRateLimitUpdateInternal(TierRateLimitUpdate):
119+
pass
120+
121+
122+
class TierRateLimitDelete(BaseModel):
123+
pass

src/app/schemas/user.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Annotated, Optional
22
from datetime import datetime
33

4-
from pydantic import BaseModel, EmailStr, Field, HttpUrl, ConfigDict
4+
from pydantic import BaseModel, EmailStr, Field, ConfigDict
55

66
from app.core.models import UUIDModel, TimestampModel, PersistentDeletion
77

@@ -27,10 +27,13 @@ class User(TimestampModel, UserBase, UUIDModel, PersistentDeletion):
2727
]
2828
hashed_password: str
2929
is_superuser: bool = False
30+
tier_id: int | None = None
3031

3132

3233
class UserRead(BaseModel):
3334
id: int
35+
tier_id: int
36+
3437
name: Annotated[
3538
str,
3639
Field(min_length=2, max_length=30, examples=["User Userson"])
@@ -44,6 +47,7 @@ class UserRead(BaseModel):
4447
Field(examples=["[email protected]"])
4548
]
4649
profile_image_url: str
50+
tier_id: int | None
4751

4852

4953
class UserCreate(UserBase):

0 commit comments

Comments
 (0)