init
This commit is contained in:
commit
c88e74b029
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
DATABASE_URL=sqlite:///./data/app.db
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.db
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
alembic/env.py
|
||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
40
alembic.ini
Normal file
40
alembic.ini
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
[alembic]
|
||||||
|
script_location = alembic
|
||||||
|
prepend_sys_path = .
|
||||||
|
sqlalchemy.url = sqlite:///./data/app.db
|
||||||
|
|
||||||
|
[post_write_hooks]
|
||||||
|
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
class = logging.Formatter
|
||||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
5
app/core/config.py
Normal file
5
app/core/config.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./data/app.db")
|
||||||
|
|
||||||
14
app/database/session.py
Normal file
14
app/database/session.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from app.core.config import DATABASE_URL
|
||||||
|
|
||||||
|
connect_args = {"check_same_thread": False} if "sqlite" in DATABASE_URL else {}
|
||||||
|
engine = create_engine(DATABASE_URL, connect_args=connect_args)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
12
app/main.py
Normal file
12
app/main.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from app.database.session import engine
|
||||||
|
from app.models.user import Base
|
||||||
|
from routes import web
|
||||||
|
|
||||||
|
# Создаём таблицы (для демо; в продакшене — Alembic)
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
app = FastAPI(title="FastAPI + SQLite Boilerplate")
|
||||||
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
|
app.include_router(web.router)
|
||||||
0
app/models/__init__.py
Normal file
0
app/models/__init__.py
Normal file
9
app/models/user.py
Normal file
9
app/models/user.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = "users"
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
name = Column(String, index=True)
|
||||||
1
data/.gitignore
vendored
Normal file
1
data/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!.gitignore
|
||||||
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
- ./app:/app/app
|
||||||
|
- ./templates:/app/templates
|
||||||
|
- ./static:/app/static
|
||||||
|
- ./data:/app/data
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=sqlite:///./data/app.db
|
||||||
|
restart: unless-stopped
|
||||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fastapi==0.115.0
|
||||||
|
uvicorn[standard]==0.32.0
|
||||||
|
sqlalchemy==2.0.36
|
||||||
|
alembic==1.13.0
|
||||||
|
jinja2==3.1.4
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
pytest==8.3.0
|
||||||
|
httpx==0.27.0
|
||||||
1
routes/__init__.py
Normal file
1
routes/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import web
|
||||||
31
routes/web.py
Normal file
31
routes/web.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# routes/web.py
|
||||||
|
from fastapi import APIRouter, Request
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from sqlalchemy import text
|
||||||
|
from app.database.session import engine
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
def home(request: Request):
|
||||||
|
db_info = {"status": "❌ Не удалось подключиться", "url": ""}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Пробуем выполнить простой запрос
|
||||||
|
with engine.connect() as conn:
|
||||||
|
result = conn.execute(text("SELECT 1"))
|
||||||
|
if result.fetchone():
|
||||||
|
db_info["status"] = "✅ Подключено"
|
||||||
|
db_info["url"] = str(engine.url)
|
||||||
|
except Exception as e:
|
||||||
|
db_info["status"] = f"❌ Ошибка: {str(e)[:60]}..." # Обрезаем длинные ошибки
|
||||||
|
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"index.html",
|
||||||
|
{
|
||||||
|
"request": request,
|
||||||
|
"title": "FastAPI + SQLite",
|
||||||
|
"db_info": db_info
|
||||||
|
}
|
||||||
|
)
|
||||||
47
static/style.css
Normal file
47
static/style.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* static/style.css */
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
background: #f9fafb;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
background: white;
|
||||||
|
padding: 2.5rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 10px 25px rgba(0,0,0,0.08);
|
||||||
|
max-width: 700px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #1e40af;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
color: #374151;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.db-status {
|
||||||
|
background: #f0f9ff;
|
||||||
|
padding: 1.2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
margin-top: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: #f3f4f6;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
27
templates/index.html
Normal file
27
templates/index.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!-- templates/index.html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🧱 FastAPI + SQLite</h1>
|
||||||
|
|
||||||
|
<div class="db-status">
|
||||||
|
<h2>🗄 Состояние базы данных</h2>
|
||||||
|
<p><strong>Статус:</strong> {{ db_info.status | safe }}</p>
|
||||||
|
{% if db_info.url %}
|
||||||
|
<p><strong>База:</strong> <code>{{ db_info.url }}</code></p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<p>📁 Архитектура: всё по папкам (как в Laravel)</p>
|
||||||
|
<p>🐳 Запущено в Docker без зависимостей</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
tests/test_web.py
Normal file
9
tests/test_web.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from app.main import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
def test_home_page():
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "FastAPI + SQLite" in response.text
|
||||||
Loading…
x
Reference in New Issue
Block a user