add project structure
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Settings:
|
||||
database_url: str = os.getenv("DATABASE_URL", "sqlite:///./data/app.db")
|
||||
host: str = os.getenv("HOST", "0.0.0.0")
|
||||
port: int = int(os.getenv("PORT", "10000"))
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
return Settings()
|
||||
@@ -0,0 +1,44 @@
|
||||
from typing import Generator
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
engine = None
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
def _build_engine(database_url: str):
|
||||
connect_args = {"check_same_thread": False} if database_url.startswith("sqlite") else {}
|
||||
return create_engine(database_url, connect_args=connect_args)
|
||||
|
||||
|
||||
def configure_database(database_url: str | None = None) -> None:
|
||||
global engine
|
||||
|
||||
settings = get_settings()
|
||||
resolved_database_url = database_url or settings.database_url
|
||||
engine = _build_engine(resolved_database_url)
|
||||
SessionLocal.configure(bind=engine)
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def init_db(database_url: str | None = None) -> None:
|
||||
from app import models
|
||||
|
||||
if engine is None or database_url is not None:
|
||||
configure_database(database_url)
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from app.db import init_db
|
||||
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
init_db()
|
||||
yield
|
||||
|
||||
app = FastAPI(title="Moving Helper", lifespan=lifespan)
|
||||
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
def root() -> RedirectResponse:
|
||||
return RedirectResponse(url="/boxes", status_code=302)
|
||||
|
||||
@app.get("/boxes")
|
||||
def boxes_page(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request=request,
|
||||
name="boxes.html",
|
||||
context={"page_title": "Boxes"},
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
@@ -0,0 +1,15 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class Box(Base):
|
||||
__tablename__ = "boxes"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(100), nullable=False, default="Sample Box")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f4f4f4;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 48px auto;
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ page_title or "Moving Helper" }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Boxes page</h1>
|
||||
<p>This is the minimal starter page for the boxes module.</p>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user