- Python 41.7%
- JavaScript 21.7%
- CSS 21.4%
- HTML 15.2%
| .idea | ||
| static | ||
| CUSTOMIZATION.md | ||
| env.example | ||
| main.py | ||
| PYCHARM.md | ||
| QUICKSTART.md | ||
| README.md | ||
| requirements.txt | ||
| STRUCTURE.md | ||
| test_main.http | ||
| test_main.py | ||
📝 Formularz z FastAPI i Discord Webhooks
Prosty projekt formularza kontaktowego z backendem FastAPI i integracją Discord webhooks.
📋 Spis treści
- Funkcje
- Struktura projektu
- Instalacja
- Konfiguracja Discord Webhook
- Uruchomienie
- Jak to działa
- API Endpoints
- Rozwijanie projektu
- Troubleshooting
✨ Funkcje
- ✅ Prosty formularz HTML z walidacją
- ✅ Backend FastAPI z obsługą CORS
- ✅ Integracja z Discord webhooks
- ✅ Automatyczna walidacja email
- ✅ Responsywny design
- ✅ Obsługa błędów
- ✅ Loading states
📁 Struktura projektu
fastapi-form-project/
├── main.py # Backend FastAPI
├── requirements.txt # Zależności Python
├── .env # Konfiguracja (NIE commitować!)
├── .env.example # Przykładowa konfiguracja
├── .gitignore # Ignorowane pliki
├── README.md # Dokumentacja
└── static/ # Pliki frontend
├── index.html # Strona HTML
├── style.css # Style CSS
└── script.js # JavaScript
🚀 Instalacja
Wymagania
- Python 3.8+
- pip
- Konto Discord (do webhooków)
Krok 1: Klonowanie/pobranie projektu
cd fastapi-form-project
Krok 2: Utworzenie wirtualnego środowiska
Windows:
python -m venv venv
venv\Scripts\activate
Linux/Mac:
python3 -m venv venv
source venv/bin/activate
Krok 3: Instalacja zależności
pip install -r requirements.txt
🔧 Konfiguracja Discord Webhook
1. Utwórz webhook w Discord
- Otwórz Discord i przejdź do swojego serwera
- Kliknij prawym przyciskiem na kanał tekstowy
- Wybierz Edytuj kanał → Integracje → Webhooki
- Kliknij Nowy Webhook
- Nadaj mu nazwę (np. "Formularz Kontaktowy")
- Skopiuj URL webhooka
2. Skonfiguruj .env
Skopiuj .env.example jako .env:
cp .env.example .env
Edytuj .env i wklej swój URL webhook:
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/1234567890/abcdefghijklmnop
▶️ Uruchomienie
Uruchomienie serwera
python main.py
Lub z uvicorn:
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Dostęp do aplikacji
Otwórz przeglądarkę i przejdź do:
- Aplikacja: http://localhost:8000
- Dokumentacja API: http://localhost:8000/docs
- Health check: http://localhost:8000/api/health
🔍 Jak to działa
Backend (main.py)
- FastAPI Application - główna aplikacja
- Static Files - serwowanie plików HTML/CSS/JS
- Pydantic Models - walidacja danych
- Discord Integration - wysyłanie danych do Discord
Frontend (HTML/CSS/JS)
- Użytkownik wypełnia formularz
- JavaScript przechwytuje submit
- Dane są wysyłane do
/api/submit-form(POST) - Backend waliduje i wysyła do Discord
- Użytkownik otrzymuje potwierdzenie
Przepływ danych
Formularz HTML → JavaScript (walidacja) → FastAPI (/api/submit-form)
↓
Walidacja Pydantic
↓
Discord Webhook
↓
Powiadomienie na Discord
🛣️ API Endpoints
GET /
Zwraca stronę główną z formularzem.
POST /api/submit-form
Przyjmuje dane z formularza i wysyła do Discord.
Request Body:
{
"name": "Jan Kowalski",
"email": "jan@example.com",
"message": "Witam, mam pytanie..."
}
Response (Success):
{
"success": true,
"message": "Formularz został pomyślnie wysłany!"
}
POST /api/discord-webhook
Endpoint do odbierania webhooków z Discord (opcjonalny).
GET /api/health
Sprawdza stan aplikacji.
Response:
{
"status": "healthy",
"discord_configured": true
}
🛠️ Rozwijanie projektu
1. Dodanie nowych pól do formularza
a) Dodaj pole w HTML (index.html):
<div class="form-group">
<label for="phone">
<span class="icon">📱</span>
Telefon
</label>
<input
type="tel"
id="phone"
name="phone"
placeholder="+48 123 456 789"
>
</div>
b) Zaktualizuj model w main.py:
class FormData(BaseModel):
name: str
email: EmailStr
message: str
phone: Optional[str] = None # Nowe pole
c) Dodaj pole do Discord embed:
{
"name": "📱 Telefon",
"value": data.phone or "Nie podano",
"inline": True
}
d) Zaktualizuj JavaScript (script.js):
const formData = {
name: document.getElementById('name').value.trim(),
email: document.getElementById('email').value.trim(),
message: document.getElementById('message').value.trim(),
phone: document.getElementById('phone').value.trim() // Nowe pole
};
2. Dodanie bazy danych
Zainstaluj SQLAlchemy:
pip install sqlalchemy
Utwórz database.py:
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
SQLALCHEMY_DATABASE_URL = "sqlite:///./submissions.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Submission(Base):
__tablename__ = "submissions"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
email = Column(String)
message = Column(String)
created_at = Column(DateTime, default=datetime.utcnow)
Base.metadata.create_all(bind=engine)
W main.py dodaj zapis do bazy:
from database import SessionLocal, Submission
@app.post("/api/submit-form")
async def submit_form(data: FormData):
# Zapis do bazy
db = SessionLocal()
submission = Submission(
name=data.name,
email=data.email,
message=data.message
)
db.add(submission)
db.commit()
db.close()
# Wysłanie do Discord
await send_to_discord(data)
return {"success": True, "message": "Zapisano!"}
3. Dodanie CAPTCHA (reCAPTCHA)
a) Zarejestruj się na: https://www.google.com/recaptcha/admin
b) Dodaj w HTML przed przyciskiem submit:
<div class="g-recaptcha" data-sitekey="TWÓJ_SITE_KEY"></div>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
c) W JavaScript, przed wysłaniem:
const recaptchaResponse = grecaptcha.getResponse();
if (!recaptchaResponse) {
showStatus('Proszę zaznaczyć reCAPTCHA', false);
return;
}
d) Weryfikacja w backend (main.py):
import httpx
async def verify_recaptcha(response_token: str) -> bool:
secret_key = os.getenv("RECAPTCHA_SECRET_KEY")
async with httpx.AsyncClient() as client:
response = await client.post(
'https://www.google.com/recaptcha/api/siteverify',
data={'secret': secret_key, 'response': response_token}
)
result = response.json()
return result.get('success', False)
4. Dodanie wysyłania emaili
Zainstaluj:
pip install fastapi-mail
Przykładowa konfiguracja:
from fastapi_mail import FastMail, MessageSchema, ConnectionConfig
conf = ConnectionConfig(
MAIL_USERNAME="twoj@email.com",
MAIL_PASSWORD="twoje-haslo",
MAIL_FROM="twoj@email.com",
MAIL_PORT=587,
MAIL_SERVER="smtp.gmail.com",
MAIL_STARTTLS=True,
MAIL_SSL_TLS=False
)
async def send_email_notification(data: FormData):
message = MessageSchema(
subject="Nowe zgłoszenie",
recipients=["admin@example.com"],
body=f"Od: {data.name}\nEmail: {data.email}\n\n{data.message}",
)
fm = FastMail(conf)
await fm.send_message(message)
5. Dodanie rate limiting
Zainstaluj:
pip install slowapi
W main.py:
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/api/submit-form")
@limiter.limit("5/minute") # Max 5 zapytań na minutę
async def submit_form(request: Request, data: FormData):
# ... reszta kodu
6. Dodanie logowania
Dodaj na początku main.py:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# Użycie w funkcjach:
logger.info(f"Otrzymano formularz od: {data.email}")
7. Dodanie testów
Utwórz test_main.py:
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health_check():
response = client.get("/api/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
def test_submit_form():
response = client.post("/api/submit-form", json={
"name": "Test User",
"email": "test@example.com",
"message": "Test message"
})
assert response.status_code == 200
Uruchom testy:
pip install pytest
pytest test_main.py
8. Deploy na produkcję
Opcja A: Heroku
- Utwórz
Procfile:
web: uvicorn main:app --host 0.0.0.0 --port $PORT
- Deploy:
heroku create
git push heroku main
Opcja B: Docker
Utwórz Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Build i uruchom:
docker build -t fastapi-form .
docker run -p 8000:8000 --env-file .env fastapi-form
Opcja C: VPS (Ubuntu)
# Zainstaluj zależności
sudo apt update
sudo apt install python3-pip python3-venv nginx
# Utwórz venv i zainstaluj
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Utwórz systemd service
sudo nano /etc/systemd/system/fastapi-form.service
Zawartość service file:
[Unit]
Description=FastAPI Form Application
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/fastapi-form-project
Environment="PATH=/home/ubuntu/fastapi-form-project/venv/bin"
ExecStart=/home/ubuntu/fastapi-form-project/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
[Install]
WantedBy=multi-user.target
Uruchom:
sudo systemctl start fastapi-form
sudo systemctl enable fastapi-form
🐛 Troubleshooting
Problem: "Module not found" błąd
Rozwiązanie:
# Upewnij się, że venv jest aktywowane
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Zainstaluj ponownie zależności
pip install -r requirements.txt
Problem: Discord webhook nie działa
Rozwiązanie:
- Sprawdź czy URL jest poprawny w
.env - Sprawdź czy webhook istnieje na Discord
- Sprawdź logi w konsoli:
print(webhook_url)
Problem: CORS errors
Rozwiązanie:
Dodaj CORS middleware w main.py:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Problem: Port już w użyciu
Rozwiązanie:
# Zmień port w uruchomieniu
uvicorn main:app --port 8001
# Lub zabij proces na porcie 8000 (Linux/Mac)
lsof -ti:8000 | xargs kill -9
# Windows
netstat -ano | findstr :8000
taskkill /PID <PID> /F
📚 Przydatne linki
📄 Licencja
Ten projekt jest wolny do użytku w celach edukacyjnych i komercyjnych.
🤝 Contributing
Pull requesty są mile widziane! Dla większych zmian, najpierw otwórz issue.
Autor: Twoje Imię
Data: 2024
Kontakt: twoj@email.com