sistemas_integrados_api/
│
├── app/
│ ├── __init__.py
│ ├── database.py
│ ├── models.py
│ ├── schemas.py
│ ├── auth.py
│ ├── email_utils.py
│ ├── routers/
│ │ ├── user_routes.py
│ │ └── product_routes.py
│
├── uploaded_images/ # Pasta para imagens dos produtos
├── main.py
├── requirements.txt
├── README.md
python -m venv venv source venv/bin/activate # Linux/Mac .\venv\Scripts\activate # Windows
pip install fastapi uvicorn sqlalchemy passlib[bcrypt] python-jose python-multipart
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./sistemas_integrados.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine)
Base = declarative_base()
from sqlalchemy import Column, Integer, String, Float, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from app.database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
email = Column(String, unique=True, index=True)
password = Column(String)
is_verified = Column(Boolean, default=False)
verification_code = Column(String)
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
price = Column(Float)
owner_id = Column(Integer, ForeignKey('users.id'))
owner = relationship("User")
from pydantic import BaseModel
class UserCreate(BaseModel):
username: str
email: str
password: str
class UserLogin(BaseModel):
username: str
password: str
class UserVerify(BaseModel):
username: str
verification_code: str
class ProductCreate(BaseModel):
name: str
price: float
class ProductOut(BaseModel):
id: int
name: str
price: float
owner_id: int
class Config:
orm_mode = True
from datetime import datetime, timedelta
from jose import jwt, JWTError
from passlib.context import CryptContext
SECRET_KEY = "secreta-chave-jwt"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
def create_refresh_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(days=7)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
import smtplib
from email.message import EmailMessage
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
SMTP_USERNAME = 'seuemail@gmail.com'
SMTP_PASSWORD = 'sua_senha_ou_senha_de_app'
def send_verification_email(to_email: str, verification_code: str):
msg = EmailMessage()
msg['Subject'] = 'Verificação de Conta - Sistemas Integrados API'
msg['From'] = SMTP_USERNAME
msg['To'] = to_email
msg.set_content(f"""
Olá!
Seu código de verificação é: {verification_code}
Por favor, insira este código para ativar sua conta.
Obrigado!
Equipe Sistemas Integrados
""")
try:
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as smtp:
smtp.starttls()
smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
smtp.send_message(msg)
print(f"E-mail enviado com sucesso para {to_email}")
except Exception as e:
print(f"Erro ao enviar e-mail: {e}")
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from fastapi.security import OAuth2PasswordBearer
from app import schemas, models, auth, email_utils
from app.database import SessionLocal
import random
router = APIRouter(prefix="/users", tags=["users"])
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.post("/register")
def register(user: schemas.UserCreate, db: Session = Depends(get_db)):
hashed_password = auth.get_password_hash(user.password)
verification_code = str(random.randint(100000, 999999))
db_user = models.User(username=user.username, email=user.email,
password=hashed_password, is_verified=False,
verification_code=verification_code)
db.add(db_user)
db.commit()
db.refresh(db_user)
email_utils.send_verification_email(user.email, verification_code)
return {"msg": "Usuário criado. Verifique seu e-mail para ativar a conta."}
@router.post("/verify")
def verify(user: schemas.UserVerify, db: Session = Depends(get_db)):
db_user = db.query(models.User).filter(models.User.username == user.username).first()
if not db_user or db_user.verification_code != user.verification_code:
raise HTTPException(status_code=400, detail="Código inválido")
db_user.is_verified = True
db_user.verification_code = None
db.commit()
return {"msg": "Usuário verificado com sucesso!"}
@router.post("/login")
def login(user: schemas.UserLogin, db: Session = Depends(get_db)):
db_user = db.query(models.User).filter(models.User.username == user.username).first()
if not db_user or not auth.verify_password(user.password, db_user.password):
raise HTTPException(status_code=400, detail="Credenciais inválidas")
if not db_user.is_verified:
raise HTTPException(status_code=403, detail="Conta não verificada. Verifique seu e-mail.")
access_token = auth.create_access_token({"sub": db_user.username})
refresh_token = auth.create_refresh_token({"sub": db_user.username})
return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}
@router.post("/refresh")
def refresh_token(token: str = Depends(oauth2_scheme)):
try:
payload = auth.jwt.decode(token, auth.SECRET_KEY, algorithms=[auth.ALGORITHM])
username = payload.get("sub")
except:
raise HTTPException(status_code=401, detail="Token inválido")
new_token = auth.create_access_token({"sub": username})
return {"access_token": new_token}
app/routers/product_routes.py
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from jose import jwt, JWTError
from app import models, schemas, auth, database
import shutil
import os
router = APIRouter(prefix="/products", tags=["products"])
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")
UPLOAD_FOLDER = "./uploaded_images"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
def get_db():
db = database.SessionLocal()
try:
yield db
finally:
db.close()
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Credenciais inválidas."
)
try:
payload = jwt.decode(token, auth.SECRET_KEY, algorithms=[auth.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = db.query(models.User).filter(models.User.username == username).first()
if user is None:
raise credentials_exception
return user
# -------------------- Rotas CRUD ----------------------
@router.post("/", response_model=schemas.ProductOut)
def create_product(product: schemas.ProductCreate, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
db_product = models.Product(name=product.name, price=product.price, owner_id=current_user.id)
db.add(db_product)
db.commit()
db.refresh(db_product)
return db_product
@router.get("/", response_model=list[schemas.ProductOut])
def list_products(db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
products = db.query(models.Product).filter(models.Product.owner_id == current_user.id).all()
return products
@router.put("/{product_id}", response_model=schemas.ProductOut)
def update_product(product_id: int, updated_product: schemas.ProductCreate, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
product = db.query(models.Product).filter(models.Product.id == product_id, models.Product.owner_id == current_user.id).first()
if not product:
raise HTTPException(status_code=404, detail="Produto não encontrado.")
product.name = updated_product.name
product.price = updated_product.price
db.commit()
db.refresh(product)
return product
@router.delete("/{product_id}")
def delete_product(product_id: int, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
product = db.query(models.Product).filter(models.Product.id == product_id, models.Product.owner_id == current_user.id).first()
if not product:
raise HTTPException(status_code=404, detail="Produto não encontrado.")
db.delete(product)
db.commit()
return {"msg": "Produto excluído com sucesso."}
# -------------------- Upload de Imagem ----------------------
@router.post("/{product_id}/upload-image")
def upload_image(product_id: int, file: UploadFile = File(...), db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
product = db.query(models.Product).filter(models.Product.id == product_id, models.Product.owner_id == current_user.id).first()
if not product:
raise HTTPException(status_code=404, detail="Produto não encontrado.")
file_path = f"{UPLOAD_FOLDER}/{file.filename}"
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {"msg": "Imagem enviada com sucesso.", "filename": file.filename}
product_routes.py
:from fastapi import FastAPI
from app.database import Base, engine
from app.routers import user_routes, product_routes
Base.metadata.create_all(bind=engine)
app = FastAPI()
app.include_router(user_routes.router)
app.include_router(product_routes.router)
uvicorn main:app --reload
frontend/
│
├── index.html # Página principal
├── cadastro.html # Cadastro de usuário
├── verificar.html # Verificação de e-mail
├── login.html # Login de usuário
├── produtos.html # Cadastro e listagem de produtos
├── upload.html # Upload de imagens
├── script.js # Lógica JavaScript
├── styles.css # Estilo opcional
index.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>API Sistemas Integrados</title>
</head>
<body>
<h1>Bem-vindo à API Sistemas Integrados</h1>
<ul>
<li><a href="cadastro.html">Cadastrar Usuário</a></li>
<li><a href="verificar.html">Verificar Conta</a></li>
<li><a href="login.html">Login</a></li>
<li><a href="produtos.html">Produtos</a></li>
<li><a href="upload.html">Upload de Imagem</a></li>
</ul>
</body>
</html>
cadastro.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Cadastro</title>
</head>
<body>
<h2>Cadastro de Usuário</h2>
<form id="cadastroForm">
<input type="text" id="username" placeholder="Username" required><br>
<input type="email" id="email" placeholder="Email" required><br>
<input type="password" id="password" placeholder="Senha" required><br>
<button type="submit">Cadastrar</button>
</form>
<script src="script.js"></script>
</body>
</html>
verificar.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Verificar Conta</title>
</head>
<body>
<h2>Verificação de Conta</h2>
<form id="verificarForm">
<input type="text" id="usernameVerify" placeholder="Username" required><br>
<input type="text" id="verificationCode" placeholder="Código de Verificação" required><br>
<button type="submit">Verificar</button>
</form>
<script src="script.js"></script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form id="loginForm">
<input type="text" id="loginUsername" placeholder="Username" required><br>
<input type="password" id="loginPassword" placeholder="Senha" required><br>
<button type="submit">Entrar</button>
</form>
<script src="script.js"></script>
</body>
</html>
produtos.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Produtos</title>
</head>
<body>
<h2>Cadastrar Produto</h2>
<form id="produtoForm">
<input type="text" id="productName" placeholder="Nome do Produto" required><br>
<input type="number" id="productPrice" placeholder="Preço" required><br>
<button type="submit">Cadastrar Produto</button>
</form>
<h2>Seus Produtos</h2>
<button onclick="listarProdutos()">Atualizar Lista</button>
<ul id="listaProdutos"></ul>
<script src="script.js"></script>
</body>
</html>
upload.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Upload de Imagem</title>
</head>
<body>
<h2>Upload de Imagem para Produto</h2>
<form id="uploadForm">
<input type="number" id="productId" placeholder="ID do Produto" required><br>
<input type="file" id="fileUpload" required><br>
<button type="submit">Enviar Imagem</button>
</form>
<script src="script.js"></script>
</body>
</html>
script.js
const API_BASE = "http://127.0.0.1:8000";
let accessToken = ""; // Armazena o token temporariamente
// Cadastro de usuário
const cadastroForm = document.getElementById("cadastroForm");
if (cadastroForm) {
cadastroForm.addEventListener("submit", async (e) => {
e.preventDefault();
const username = document.getElementById("username").value;
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
const response = await fetch(`${API_BASE}/users/register`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ username, email, password })
});
const data = await response.json();
alert(data.msg || data.detail);
});
}
// Verificação de usuário
const verificarForm = document.getElementById("verificarForm");
if (verificarForm) {
verificarForm.addEventListener("submit", async (e) => {
e.preventDefault();
const username = document.getElementById("usernameVerify").value;
const verification_code = document.getElementById("verificationCode").value;
const response = await fetch(`${API_BASE}/users/verify`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ username, verification_code })
});
const data = await response.json();
alert(data.msg || data.detail);
});
}
// Login
const loginForm = document.getElementById("loginForm");
if (loginForm) {
loginForm.addEventListener("submit", async (e) => {
e.preventDefault();
const username = document.getElementById("loginUsername").value;
const password = document.getElementById("loginPassword").value;
const response = await fetch(`${API_BASE}/users/login`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (data.access_token) {
accessToken = data.access_token;
alert("Login realizado com sucesso!");
} else {
alert(data.detail);
}
});
}
// Cadastrar produto
const produtoForm = document.getElementById("produtoForm");
if (produtoForm) {
produtoForm.addEventListener("submit", async (e) => {
e.preventDefault();
const name = document.getElementById("productName").value;
const price = parseFloat(document.getElementById("productPrice").value);
const response = await fetch(`${API_BASE}/products/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`
},
body: JSON.stringify({ name, price })
});
const data = await response.json();
alert(data.name ? "Produto cadastrado!" : data.detail);
});
}
// Listar produtos
async function listarProdutos() {
const response = await fetch(`${API_BASE}/products/`, {
method: "GET",
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
const data = await response.json();
const lista = document.getElementById("listaProdutos");
lista.innerHTML = "";
data.forEach(produto => {
const li = document.createElement("li");
li.textContent = `ID: ${produto.id} - ${produto.name} - R$${produto.price}`;
lista.appendChild(li);
});
}
// Upload de imagem
const uploadForm = document.getElementById("uploadForm");
if (uploadForm) {
uploadForm.addEventListener("submit", async (e) => {
e.preventDefault();
const productId = document.getElementById("productId").value;
const file = document.getElementById("fileUpload").files[0];
const formData = new FormData();
formData.append("file", file);
const response = await fetch(`${API_BASE}/products/${productId}/upload-image`, {
method: "POST",
headers: {
"Authorization": `Bearer ${accessToken}`
},
body: formData
});
const data = await response.json();
alert(data.msg || data.detail);
});
}