Compare commits
2 Commits
49bcb38261
...
0b3c755e5c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b3c755e5c | ||
|
|
6f4dc2244a |
12
.env.sample
12
.env.sample
@@ -6,12 +6,12 @@ ENV=dev # dev, prod
|
|||||||
# =========================
|
# =========================
|
||||||
# DATABASE CONFIGURATION
|
# DATABASE CONFIGURATION
|
||||||
# =========================
|
# =========================
|
||||||
# URL de connexion SQLAlchemy
|
# connexion SQLAlchemy
|
||||||
# Pour PyMySQL (synchrone)
|
DATABASE_HOST=localhost
|
||||||
DATABASE_URL=mysql+pymysql://user:password@localhost:3306/db_name
|
DATABASE_PORT=3306
|
||||||
|
DATABASE_USER=user
|
||||||
# Pour aiomysql (async)
|
DATABASE_PASSWORD=password
|
||||||
# DATABASE_URL=mysql+aiomysql://user:password@localhost:3306/mokpyo
|
DATABASE_NAME=mokpyo
|
||||||
|
|
||||||
# Pooling / Options SQLAlchemy (optionnel)
|
# Pooling / Options SQLAlchemy (optionnel)
|
||||||
# MAX_CONNECTIONS=10
|
# MAX_CONNECTIONS=10
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -179,3 +179,6 @@ cython_debug/
|
|||||||
|
|
||||||
# git
|
# git
|
||||||
.git/
|
.git/
|
||||||
|
|
||||||
|
# custom tests files
|
||||||
|
*.test.py
|
||||||
@@ -13,32 +13,3 @@ router = APIRouter()
|
|||||||
@router.get("/ping", tags=["Test"])
|
@router.get("/ping", tags=["Test"])
|
||||||
def ping():
|
def ping():
|
||||||
return {"message": "pong"}
|
return {"message": "pong"}
|
||||||
|
|
||||||
# =========================
|
|
||||||
# EXEMPLE UTILISATEUR
|
|
||||||
# =========================
|
|
||||||
@router.get("/me", tags=["User"])
|
|
||||||
def read_current_user(current_user: dict = Depends(get_current_user)):
|
|
||||||
"""
|
|
||||||
Retourne les infos de l'utilisateur connecté
|
|
||||||
"""
|
|
||||||
return {"user": current_user}
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# EXEMPLE AUTH
|
|
||||||
# =========================
|
|
||||||
@router.post("/hash-password", tags=["Auth"])
|
|
||||||
def test_hash_password(password: str):
|
|
||||||
"""
|
|
||||||
Exemple simple pour hasher un mot de passe
|
|
||||||
"""
|
|
||||||
hashed = hash_password(password)
|
|
||||||
return {"password": password, "hashed": hashed}
|
|
||||||
|
|
||||||
@router.post("/verify-password", tags=["Auth"])
|
|
||||||
def test_verify_password(password: str, hashed: str):
|
|
||||||
"""
|
|
||||||
Vérifie qu'un mot de passe correspond à un hash
|
|
||||||
"""
|
|
||||||
valid = verify_password(password, hashed)
|
|
||||||
return {"valid": valid}
|
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ class Settings(BaseSettings):
|
|||||||
# =========================
|
# =========================
|
||||||
# DATABASE
|
# DATABASE
|
||||||
# =========================
|
# =========================
|
||||||
DATABASE_URL: str
|
DATABASE_HOST: str
|
||||||
|
DATABASE_PORT: int
|
||||||
|
DATABASE_USER: str
|
||||||
|
DATABASE_PASSWORD: str
|
||||||
|
DATABASE_NAME: str
|
||||||
|
DATABASE_DRIVER: str
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# FASTAPI
|
# FASTAPI
|
||||||
@@ -61,6 +66,10 @@ class Settings(BaseSettings):
|
|||||||
# =========================
|
# =========================
|
||||||
ORJSON_STRICT: bool = True
|
ORJSON_STRICT: bool = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def database_url(self) -> str:
|
||||||
|
return f"mysql+<driver>://{self.DATABASE_USER}:{self.DATABASE_PASSWORD}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def access_token_expire(self) -> timedelta:
|
def access_token_expire(self) -> timedelta:
|
||||||
return timedelta(minutes=self.ACCESS_TOKEN_EXPIRE_MINUTES)
|
return timedelta(minutes=self.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
@@ -71,5 +80,3 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
# Instance globale
|
# Instance globale
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
||||||
print("test")
|
|
||||||
@@ -8,3 +8,4 @@ __version__ = "1.0.0"
|
|||||||
|
|
||||||
from .engine import engine, async_engine
|
from .engine import engine, async_engine
|
||||||
from .session import SessionLocal, AsyncSessionLocal, get_db, get_async_db
|
from .session import SessionLocal, AsyncSessionLocal, get_db, get_async_db
|
||||||
|
from .models import Base
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine, Engine
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# SYNCHRONOUS ENGINE
|
# SYNCHRONOUS ENGINE
|
||||||
# =========================
|
# =========================
|
||||||
# Pour opérations sync classiques
|
# Pour opérations sync classiques
|
||||||
engine = create_engine(
|
engine: Engine = create_engine(
|
||||||
settings.DATABASE_URL.replace("+aiomysql", ""), # remove async part if present
|
settings.database_url.replace('<driver>', "pymysql"),
|
||||||
echo=settings.DEBUG,
|
echo=settings.DEBUG,
|
||||||
future=True
|
future=True
|
||||||
)
|
)
|
||||||
@@ -17,7 +17,7 @@ engine = create_engine(
|
|||||||
# =========================
|
# =========================
|
||||||
# Pour opérations async avec async SQLAlchemy
|
# Pour opérations async avec async SQLAlchemy
|
||||||
async_engine: AsyncEngine = create_async_engine(
|
async_engine: AsyncEngine = create_async_engine(
|
||||||
settings.DATABASE_URL if "+aiomysql" in settings.DATABASE_URL else settings.DATABASE_URL.replace("mysql+pymysql", "mysql+aiomysql"),
|
settings.database_url.replace('<driver>', "aiomysql"),
|
||||||
echo=settings.DEBUG,
|
echo=settings.DEBUG,
|
||||||
future=True
|
future=True
|
||||||
)
|
)
|
||||||
|
|||||||
110
app/db/models.py
Normal file
110
app/db/models.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
from datetime import date
|
||||||
|
from sqlalchemy import (
|
||||||
|
sa,
|
||||||
|
Column,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
Boolean,
|
||||||
|
Date,
|
||||||
|
Enum,
|
||||||
|
ForeignKey
|
||||||
|
)
|
||||||
|
from sqlalchemy.orm import DeclarativeBase, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = "users"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
email = Column(String, unique=True, index=True)
|
||||||
|
username = Column(String, unique=True, index=True)
|
||||||
|
hashed_password = Column(String)
|
||||||
|
stats = Column(String)
|
||||||
|
is_active = Column(Boolean, default=True)
|
||||||
|
|
||||||
|
tasks = relationship("Item", back_populates="owner")
|
||||||
|
events = relationship("Event", back_populates="owner")
|
||||||
|
goals = relationship("Goal", back_populates="owner")
|
||||||
|
sub_goals = relationship("SubGoal", back_populates="owner")
|
||||||
|
notes = relationship("Note", back_populates="owner")
|
||||||
|
|
||||||
|
|
||||||
|
class Task(Base):
|
||||||
|
__tablename__ = "tasks"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
description = Column(String, index=True, nullable=False)
|
||||||
|
frequency = Column(Integer, default=4223, nullable=False)
|
||||||
|
reward = Column(Integer, nullable=True)
|
||||||
|
goal_id = Column(Integer, ForeignKey("goals.id"), default=None, nullable=True)
|
||||||
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
|
||||||
|
start_date = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
|
||||||
|
next_date = Column(Date, nullable=True)
|
||||||
|
|
||||||
|
owner = relationship("User", back_populates="tasks")
|
||||||
|
goal = relationship("Goal", back_populates="tasks")
|
||||||
|
|
||||||
|
|
||||||
|
class Event(Base):
|
||||||
|
__tablename__ = "events"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
name = Column(String, index=True, nullable=False)
|
||||||
|
description = Column(String, index=True, nullable=True)
|
||||||
|
frequency = Column(Integer, default=4223, nullable=False)
|
||||||
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
|
||||||
|
owner = relationship("User", back_populates="events")
|
||||||
|
|
||||||
|
|
||||||
|
class Goal(Base):
|
||||||
|
__tablename__ = "goals"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
title = Column(String, index=True, nullable=False)
|
||||||
|
description = Column(String, index=True, nullable=True)
|
||||||
|
state = Column(Enum("in_progress", "successful", "failed", "aborted"), default="in_progress", nullable=False)
|
||||||
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
|
||||||
|
tasks = relationship("Task", back_populates="goal")
|
||||||
|
notes = relationship("Note", back_populates="goal")
|
||||||
|
sub_goals = relationship("SubGoal", back_populates="goal")
|
||||||
|
owner = relationship("User", back_populates="goals")
|
||||||
|
|
||||||
|
|
||||||
|
class SubGoal(Base):
|
||||||
|
__tablename__ = "sub_goals"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
title = Column(String, index=True, nullable=False)
|
||||||
|
description = Column(String, index=True, nullable=True)
|
||||||
|
state = Column(Enum("in_progress", "successful", "failed", "aborted"), default="in_progress", nullable=False)
|
||||||
|
reward = Column(Integer, nullable=True)
|
||||||
|
goal_id = Column(Integer, ForeignKey("goals.id"), nullable=False)
|
||||||
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
|
||||||
|
goal = relationship("Goal", back_populates="sub_goals")
|
||||||
|
owner = relationship("User", back_populates="sub_goals")
|
||||||
|
|
||||||
|
|
||||||
|
class Note(Base):
|
||||||
|
__tablename__ = "notes"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
title = Column(String, index=True, nullable=False)
|
||||||
|
description = Column(String, index=True, nullable=True)
|
||||||
|
content = Column(String, index=True, nullable=True)
|
||||||
|
goal_id = Column(Integer, ForeignKey("goals.id"), nullable=True)
|
||||||
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
|
||||||
|
created_at = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
|
||||||
|
updated_at = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
|
||||||
|
|
||||||
|
goal = relationship("Goal", back_populates="notes")
|
||||||
|
owner = relationship("User", back_populates="notes")
|
||||||
|
|
||||||
Reference in New Issue
Block a user