Login ainda nao funciona mas esta quase
This commit is contained in:
311
app.py
311
app.py
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, request, render_template, redirect, url_for, flash
|
from flask import Flask, request, render_template, redirect, url_for, flash, session
|
||||||
from functions.database import (
|
from functions.database import (
|
||||||
Base,
|
Base,
|
||||||
Militante,
|
Militante,
|
||||||
@@ -12,34 +12,90 @@ from functions.database import (
|
|||||||
AssinaturaAnual,
|
AssinaturaAnual,
|
||||||
RelatorioCotasMensais,
|
RelatorioCotasMensais,
|
||||||
RelatorioVendasMateriais,
|
RelatorioVendasMateriais,
|
||||||
engine,
|
Usuario,
|
||||||
|
get_db_connection,
|
||||||
)
|
)
|
||||||
from sqlalchemy import create_engine, and_
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask_bootstrap import Bootstrap5
|
from flask_bootstrap import Bootstrap5
|
||||||
from routes.cota import cota_bp
|
from routes.cota import cota_bp
|
||||||
from functions.validations import validar_cpf
|
from functions.validations import validar_cpf
|
||||||
|
from functools import wraps
|
||||||
Session = sessionmaker(bind=engine)
|
from pathlib import Path
|
||||||
session = Session()
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = 'sua_chave_secreta_aqui'
|
app.secret_key = 'sua_chave_secreta_aqui' # Necessário para sessões do Flask
|
||||||
bootstrap = Bootstrap5(app)
|
bootstrap = Bootstrap5(app)
|
||||||
|
|
||||||
|
# Configuração da sessão do SQLAlchemy
|
||||||
|
db_session = get_db_connection()
|
||||||
|
|
||||||
def session_run(model):
|
# Decorator para verificar se o usuário está logado
|
||||||
session.add(model)
|
def login_required(f):
|
||||||
try:
|
@wraps(f)
|
||||||
session.commit()
|
def decorated_function(*args, **kwargs):
|
||||||
except Exception as e:
|
if 'user_id' not in session:
|
||||||
print(e)
|
flash('Por favor, faça login para acessar esta página.', 'warning')
|
||||||
session.rollback()
|
return redirect(url_for('login', next=request.url))
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
# Rota raiz - redireciona para login se não estiver autenticado
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
if 'user_id' not in session:
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
return redirect(url_for('home'))
|
||||||
|
|
||||||
|
# Rota de login
|
||||||
|
@app.route("/login", methods=["GET", "POST"])
|
||||||
|
def login():
|
||||||
|
if 'user_id' in session:
|
||||||
|
return redirect(url_for('home'))
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
username = request.form.get("username")
|
||||||
|
password = request.form.get("password")
|
||||||
|
otp = request.form.get("otp")
|
||||||
|
|
||||||
|
user = db_session.query(Usuario).filter_by(username=username).first()
|
||||||
|
|
||||||
|
if user and user.check_password(password) and user.verify_otp(otp):
|
||||||
|
session['user_id'] = user.id # Usando a sessão do Flask
|
||||||
|
session['is_admin'] = user.is_admin
|
||||||
|
next_page = request.args.get('next')
|
||||||
|
flash('Login realizado com sucesso!', 'success')
|
||||||
|
return redirect(next_page or url_for('home'))
|
||||||
|
else:
|
||||||
|
flash('Credenciais inválidas', 'danger')
|
||||||
|
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
# Rota de logout
|
||||||
|
@app.route("/logout")
|
||||||
|
def logout():
|
||||||
|
session.clear() # Limpa a sessão do Flask
|
||||||
|
flash('Você foi desconectado com sucesso.', 'info')
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# Rota home (protegida)
|
||||||
|
@app.route("/home")
|
||||||
|
@login_required
|
||||||
|
def home():
|
||||||
|
"""Página inicial do sistema"""
|
||||||
|
links = []
|
||||||
|
for rule in app.url_map.iter_rules():
|
||||||
|
if "GET" in rule.methods and has_no_empty_params(rule):
|
||||||
|
url = url_for(rule.endpoint, **(rule.defaults or {}))
|
||||||
|
endpoint_name = 'Início' if rule.endpoint == 'home' else rule.endpoint
|
||||||
|
links.append((url, endpoint_name))
|
||||||
|
|
||||||
|
return render_template('home.html', links=links)
|
||||||
|
|
||||||
# Rota para criar um novo militante
|
# Rota para criar um novo militante
|
||||||
@app.route("/militantes/novo", methods=["GET", "POST"])
|
@app.route("/militantes/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def novo_militante():
|
def novo_militante():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
cpf = request.form["cpf"]
|
cpf = request.form["cpf"]
|
||||||
@@ -58,22 +114,30 @@ def novo_militante():
|
|||||||
filiado=bool(request.form.get("filiado", False))
|
filiado=bool(request.form.get("filiado", False))
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(novo_militante)
|
db_session.add(novo_militante)
|
||||||
flash('Militante cadastrado com sucesso!', 'success')
|
try:
|
||||||
return redirect(url_for("listar_militantes"))
|
db_session.commit()
|
||||||
|
flash('Militante cadastrado com sucesso!', 'success')
|
||||||
|
return redirect(url_for("listar_militantes"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar militante. Verifique se o CPF ou email já não estão cadastrados.', 'error')
|
||||||
|
return render_template("novo_militante.html",
|
||||||
|
dados_anteriores=request.form)
|
||||||
|
|
||||||
return render_template("novo_militante.html")
|
return render_template("novo_militante.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar militantes
|
# Rota para listar militantes
|
||||||
@app.route("/militantes")
|
@app.route("/militantes")
|
||||||
|
@login_required
|
||||||
def listar_militantes():
|
def listar_militantes():
|
||||||
militantes = session.query(Militante).all()
|
militantes = db_session.query(Militante).all()
|
||||||
return render_template("listar_militantes.html", militantes=militantes)
|
return render_template("listar_militantes.html", militantes=militantes)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar uma nova cota mensal
|
# Rota para criar uma nova cota mensal
|
||||||
@app.route("/cotas/novo", methods=["GET", "POST"])
|
@app.route("/cotas/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def nova_cota():
|
def nova_cota():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
cotas_mensais = CotaMensal(
|
cotas_mensais = CotaMensal(
|
||||||
@@ -83,21 +147,28 @@ def nova_cota():
|
|||||||
data_alteracao=datetime.strptime(request.form["data_alteracao"], "%Y-%m-%d")
|
data_alteracao=datetime.strptime(request.form["data_alteracao"], "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(cotas_mensais)
|
db_session.add(cotas_mensais)
|
||||||
return redirect(url_for("listar_cotas"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_cotas"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar cota mensal. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("nova_cota.html")
|
||||||
|
|
||||||
return render_template("nova_cota.html")
|
return render_template("nova_cota.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar cotas mensais
|
# Rota para listar cotas mensais
|
||||||
@app.route("/cotas")
|
@app.route("/cotas")
|
||||||
|
@login_required
|
||||||
def listar_cotas():
|
def listar_cotas():
|
||||||
cotas = session.query(CotaMensal).all()
|
cotas = db_session.query(CotaMensal).all()
|
||||||
return render_template("listar_cotas.html", cotas=cotas)
|
return render_template("listar_cotas.html", cotas=cotas)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar um novo pagamento
|
# Rota para criar um novo pagamento
|
||||||
@app.route("/pagamentos/novo", methods=["GET", "POST"])
|
@app.route("/pagamentos/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def novo_pagamento():
|
def novo_pagamento():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
pagamentos = Pagamento(
|
pagamentos = Pagamento(
|
||||||
@@ -107,21 +178,28 @@ def novo_pagamento():
|
|||||||
data_pagamento=datetime.strptime(request.form["data_pagamento"], "%Y-%m-%d")
|
data_pagamento=datetime.strptime(request.form["data_pagamento"], "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(pagamentos)
|
db_session.add(pagamentos)
|
||||||
return redirect(url_for("listar_pagamentos"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_pagamentos"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar pagamento. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("novo_pagamento.html")
|
||||||
|
|
||||||
return render_template("novo_pagamento.html")
|
return render_template("novo_pagamento.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar pagamentos
|
# Rota para listar pagamentos
|
||||||
@app.route("/pagamentos")
|
@app.route("/pagamentos")
|
||||||
|
@login_required
|
||||||
def listar_pagamentos():
|
def listar_pagamentos():
|
||||||
pagamentos = session.query(Pagamento).all()
|
pagamentos = db_session.query(Pagamento).all()
|
||||||
return render_template("listar_pagamentos.html", pagamentos=pagamentos)
|
return render_template("listar_pagamentos.html", pagamentos=pagamentos)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar um novo material vendido
|
# Rota para criar um novo material vendido
|
||||||
@app.route("/materiais/novo", methods=["GET", "POST"])
|
@app.route("/materiais/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def novo_material():
|
def novo_material():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
materiais_vendidos = MaterialVendido(
|
materiais_vendidos = MaterialVendido(
|
||||||
@@ -132,21 +210,28 @@ def novo_material():
|
|||||||
data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
|
data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(materiais_vendidos)
|
db_session.add(materiais_vendidos)
|
||||||
return redirect(url_for("listar_materiais"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_materiais"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar material vendido. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("novo_material.html")
|
||||||
|
|
||||||
return render_template("novo_material.html")
|
return render_template("novo_material.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar materiais vendidos
|
# Rota para listar materiais vendidos
|
||||||
@app.route("/materiais")
|
@app.route("/materiais")
|
||||||
|
@login_required
|
||||||
def listar_materiais():
|
def listar_materiais():
|
||||||
materiais = session.query(MaterialVendido).all()
|
materiais = db_session.query(MaterialVendido).all()
|
||||||
return render_template("listar_materiais.html", materiais=materiais)
|
return render_template("listar_materiais.html", materiais=materiais)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar uma nova venda de jornais avulsos
|
# Rota para criar uma nova venda de jornais avulsos
|
||||||
@app.route("/jornais/novo", methods=["GET", "POST"])
|
@app.route("/jornais/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def nova_venda_jornal():
|
def nova_venda_jornal():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
vendas_jornais_avulsos = VendaJornalAvulso(
|
vendas_jornais_avulsos = VendaJornalAvulso(
|
||||||
@@ -156,21 +241,28 @@ def nova_venda_jornal():
|
|||||||
data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
|
data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"),
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(vendas_jornais_avulsos)
|
db_session.add(vendas_jornais_avulsos)
|
||||||
return redirect(url_for("listar_vendas_jornal"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_vendas_jornal"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar venda de jornal avulso. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("nova_venda_jornal.html")
|
||||||
|
|
||||||
return render_template("nova_venda_jornal.html")
|
return render_template("nova_venda_jornal.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar vendas de jornais avulsos
|
# Rota para listar vendas de jornais avulsos
|
||||||
@app.route("/jornais")
|
@app.route("/jornais")
|
||||||
|
@login_required
|
||||||
def listar_vendas_jornal():
|
def listar_vendas_jornal():
|
||||||
vendas = session.query(VendaJornalAvulso).all()
|
vendas = db_session.query(VendaJornalAvulso).all()
|
||||||
return render_template("listar_vendas_jornal.html", vendas=vendas)
|
return render_template("listar_vendas_jornal.html", vendas=vendas)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar uma nova assinatura anual
|
# Rota para criar uma nova assinatura anual
|
||||||
@app.route("/assinaturas/novo", methods=["GET", "POST"])
|
@app.route("/assinaturas/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def nova_assinatura():
|
def nova_assinatura():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
assinaturas_anuais = AssinaturaAnual(
|
assinaturas_anuais = AssinaturaAnual(
|
||||||
@@ -182,21 +274,28 @@ def nova_assinatura():
|
|||||||
data_fim=datetime.strptime(request.form["data_fim"], "%Y-%m-%d")
|
data_fim=datetime.strptime(request.form["data_fim"], "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(assinaturas_anuais)
|
db_session.add(assinaturas_anuais)
|
||||||
return redirect(url_for("listar_assinaturas"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_assinaturas"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar assinatura anual. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("nova_assinatura.html")
|
||||||
|
|
||||||
return render_template("nova_assinatura.html")
|
return render_template("nova_assinatura.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar assinaturas anuais
|
# Rota para listar assinaturas anuais
|
||||||
@app.route("/assinaturas")
|
@app.route("/assinaturas")
|
||||||
|
@login_required
|
||||||
def listar_assinaturas():
|
def listar_assinaturas():
|
||||||
assinaturas = session.query(AssinaturaAnual).all()
|
assinaturas = db_session.query(AssinaturaAnual).all()
|
||||||
return render_template("listar_assinaturas.html", assinaturas=assinaturas)
|
return render_template("listar_assinaturas.html", assinaturas=assinaturas)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar um novo relatório de cotas mensais
|
# Rota para criar um novo relatório de cotas mensais
|
||||||
@app.route("/relatorios/cotas/novo", methods=["GET", "POST"])
|
@app.route("/relatorios/cotas/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def novo_relatorio_cotas():
|
def novo_relatorio_cotas():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
relatorio_cotas_mensais = RelatorioCotasMensais(
|
relatorio_cotas_mensais = RelatorioCotasMensais(
|
||||||
@@ -206,21 +305,28 @@ def novo_relatorio_cotas():
|
|||||||
data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
|
data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(relatorio_cotas_mensais)
|
db_session.add(relatorio_cotas_mensais)
|
||||||
return redirect(url_for("listar_relatorios_cotas"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_relatorios_cotas"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar relatório de cotas mensais. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("novo_relatorio_cotas.html")
|
||||||
|
|
||||||
return render_template("novo_relatorio_cotas.html")
|
return render_template("novo_relatorio_cotas.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar relatórios de cotas mensais
|
# Rota para listar relatórios de cotas mensais
|
||||||
@app.route("/relatorios/cotas")
|
@app.route("/relatorios/cotas")
|
||||||
|
@login_required
|
||||||
def listar_relatorios_cotas():
|
def listar_relatorios_cotas():
|
||||||
relatorios = session.query(RelatorioCotasMensais).all()
|
relatorios = db_session.query(RelatorioCotasMensais).all()
|
||||||
return render_template("listar_relatorios_cotas.html", relatorios=relatorios)
|
return render_template("listar_relatorios_cotas.html", relatorios=relatorios)
|
||||||
|
|
||||||
|
|
||||||
# Rota para criar um novo relatório de vendas de materiais
|
# Rota para criar um novo relatório de vendas de materiais
|
||||||
@app.route("/relatorios/vendas/novo", methods=["GET", "POST"])
|
@app.route("/relatorios/vendas/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def novo_relatorio_vendas():
|
def novo_relatorio_vendas():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
relatorio_vendas_materiais = RelatorioVendasMateriais(
|
relatorio_vendas_materiais = RelatorioVendasMateriais(
|
||||||
@@ -230,42 +336,29 @@ def novo_relatorio_vendas():
|
|||||||
data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
|
data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
|
|
||||||
session_run(relatorio_vendas_materiais)
|
db_session.add(relatorio_vendas_materiais)
|
||||||
return redirect(url_for("listar_relatorios_vendas"))
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return redirect(url_for("listar_relatorios_vendas"))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao cadastrar relatório de vendas de materiais. Verifique se os dados fornecidos são válidos.', 'error')
|
||||||
|
return render_template("novo_relatorio_vendas.html")
|
||||||
|
|
||||||
return render_template("novo_relatorio_vendas.html")
|
return render_template("novo_relatorio_vendas.html")
|
||||||
|
|
||||||
|
|
||||||
# Rota para listar relatórios de vendas de materiais
|
# Rota para listar relatórios de vendas de materiais
|
||||||
@app.route("/relatorios/vendas")
|
@app.route("/relatorios/vendas")
|
||||||
|
@login_required
|
||||||
def listar_relatorios_vendas():
|
def listar_relatorios_vendas():
|
||||||
relatorios = session.query(RelatorioVendasMateriais).all()
|
relatorios = db_session.query(RelatorioVendasMateriais).all()
|
||||||
return render_template("listar_relatorios_vendas.html", relatorios=relatorios)
|
return render_template("listar_relatorios_vendas.html", relatorios=relatorios)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
|
||||||
def home():
|
|
||||||
"""Página inicial do sistema"""
|
|
||||||
links = []
|
|
||||||
for rule in app.url_map.iter_rules():
|
|
||||||
if "GET" in rule.methods and has_no_empty_params(rule):
|
|
||||||
url = url_for(rule.endpoint, **(rule.defaults or {}))
|
|
||||||
# Substituindo 'home' por 'início' no menu
|
|
||||||
endpoint_name = 'Início' if rule.endpoint == 'home' else rule.endpoint
|
|
||||||
links.append((url, endpoint_name))
|
|
||||||
|
|
||||||
return render_template('home.html', links=links)
|
|
||||||
|
|
||||||
|
|
||||||
def has_no_empty_params(rule):
|
|
||||||
defaults = rule.defaults if rule.defaults is not None else ()
|
|
||||||
arguments = rule.arguments if rule.arguments is not None else ()
|
|
||||||
return len(defaults) >= len(arguments)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/militantes/editar/<int:id>", methods=["GET", "POST"])
|
@app.route("/militantes/editar/<int:id>", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
def editar_militante(id):
|
def editar_militante(id):
|
||||||
militante = session.query(Militante).get(id)
|
militante = db_session.query(Militante).get(id)
|
||||||
if not militante:
|
if not militante:
|
||||||
flash('Militante não encontrado.', 'error')
|
flash('Militante não encontrado.', 'error')
|
||||||
return redirect(url_for('listar_militantes'))
|
return redirect(url_for('listar_militantes'))
|
||||||
@@ -284,16 +377,77 @@ def editar_militante(id):
|
|||||||
militante.endereco = request.form["endereco"]
|
militante.endereco = request.form["endereco"]
|
||||||
militante.filiado = bool(request.form.get("filiado", False))
|
militante.filiado = bool(request.form.get("filiado", False))
|
||||||
|
|
||||||
session.commit()
|
db_session.commit()
|
||||||
flash('Militante atualizado com sucesso!', 'success')
|
flash('Militante atualizado com sucesso!', 'success')
|
||||||
return redirect(url_for('listar_militantes'))
|
return redirect(url_for('listar_militantes'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
session.rollback()
|
db_session.rollback()
|
||||||
flash('Erro ao atualizar militante. Verifique se o CPF ou email já não estão cadastrados.', 'error')
|
flash('Erro ao atualizar militante. Verifique se o CPF ou email já não estão cadastrados.', 'error')
|
||||||
return render_template("editar_militante.html", militante=militante)
|
return render_template("editar_militante.html", militante=militante)
|
||||||
|
|
||||||
return render_template("editar_militante.html", militante=militante)
|
return render_template("editar_militante.html", militante=militante)
|
||||||
|
|
||||||
|
# Rota para criar novo usuário
|
||||||
|
@app.route("/usuarios/novo", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def novo_usuario():
|
||||||
|
if not session.get('is_admin'):
|
||||||
|
flash('Acesso negado. Apenas administradores podem criar novos usuários.', 'danger')
|
||||||
|
return redirect(url_for('home'))
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
username = request.form.get("username")
|
||||||
|
email = request.form.get("email")
|
||||||
|
password = request.form.get("password")
|
||||||
|
confirm_password = request.form.get("confirm_password")
|
||||||
|
is_admin = bool(request.form.get("is_admin"))
|
||||||
|
|
||||||
|
# Validações
|
||||||
|
if password != confirm_password:
|
||||||
|
flash('As senhas não conferem.', 'danger')
|
||||||
|
return render_template('novo_usuario.html')
|
||||||
|
|
||||||
|
# Verificar se usuário já existe
|
||||||
|
if db_session.query(Usuario).filter_by(username=username).first():
|
||||||
|
flash('Nome de usuário já existe.', 'danger')
|
||||||
|
return render_template('novo_usuario.html')
|
||||||
|
|
||||||
|
if db_session.query(Usuario).filter_by(email=email).first():
|
||||||
|
flash('E-mail já cadastrado.', 'danger')
|
||||||
|
return render_template('novo_usuario.html')
|
||||||
|
|
||||||
|
# Criar novo usuário
|
||||||
|
try:
|
||||||
|
novo_usuario = Usuario(
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
is_admin=is_admin
|
||||||
|
)
|
||||||
|
novo_usuario.email = email
|
||||||
|
|
||||||
|
db_session.add(novo_usuario)
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
# Gerar QR code para configuração do OTP
|
||||||
|
qr_uri = novo_usuario.get_otp_uri()
|
||||||
|
flash('Usuário criado com sucesso!', 'success')
|
||||||
|
|
||||||
|
# Modificação alternativa para salvar no diretório atual
|
||||||
|
qr_path = Path('admin_qr.png')
|
||||||
|
img = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
img.add_data(qr_uri)
|
||||||
|
img.make(fit=True)
|
||||||
|
img.save(str(qr_path))
|
||||||
|
|
||||||
|
return render_template('mostrar_qr_code.html', qr_uri=qr_uri)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db_session.rollback()
|
||||||
|
flash('Erro ao criar usuário. Por favor, tente novamente.', 'danger')
|
||||||
|
print(f"Erro: {e}")
|
||||||
|
return render_template('novo_usuario.html')
|
||||||
|
|
||||||
|
return render_template('novo_usuario.html')
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@@ -304,7 +458,6 @@ def create_app():
|
|||||||
# ... existing code ...
|
# ... existing code ...
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
# Iniciar o servidor Flask
|
# Iniciar o servidor Flask
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
|
|||||||
78
create_admin.py
Normal file
78
create_admin.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from functions.database import Usuario, Base, engine, get_db_connection
|
||||||
|
import qrcode
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def create_admin_user():
|
||||||
|
try:
|
||||||
|
# Criar as tabelas se não existirem
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
# Obter conexão com o banco
|
||||||
|
db_session = get_db_connection()
|
||||||
|
|
||||||
|
# Verificar se já existe um admin
|
||||||
|
admin = db_session.query(Usuario).filter_by(username="admin").first()
|
||||||
|
|
||||||
|
if not admin:
|
||||||
|
# Criar usuário admin
|
||||||
|
admin = Usuario(
|
||||||
|
username="admin",
|
||||||
|
password="admin123",
|
||||||
|
is_admin=True
|
||||||
|
)
|
||||||
|
admin.email = "admin@example.com"
|
||||||
|
|
||||||
|
db_session.add(admin)
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
print("\n=== Usuário Admin Criado com Sucesso ===")
|
||||||
|
else:
|
||||||
|
print("\n=== Usuário Admin Encontrado ===")
|
||||||
|
|
||||||
|
print(f"Username: admin")
|
||||||
|
print(f"Senha: admin123")
|
||||||
|
print(f"Segredo OTP: {admin.otp_secret}")
|
||||||
|
|
||||||
|
# Gerar QR Code
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||||
|
qr.add_data(admin.get_otp_uri())
|
||||||
|
qr.make(fit=True)
|
||||||
|
|
||||||
|
# Encontrar o diretório raiz do projeto (onde está o app.py)
|
||||||
|
current_dir = Path(__file__).resolve().parent
|
||||||
|
project_root = current_dir.parent # Volta um nível para a raiz do projeto
|
||||||
|
static_dir = project_root / 'static'
|
||||||
|
|
||||||
|
# Criar diretório static se não existir
|
||||||
|
os.makedirs(static_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Caminho completo para o arquivo QR code
|
||||||
|
qr_path = static_dir / 'admin_qr.png'
|
||||||
|
|
||||||
|
# Salvar QR Code como imagem
|
||||||
|
img = qr.make_image(fill_color="black", back_color="white")
|
||||||
|
img.save(str(qr_path))
|
||||||
|
|
||||||
|
print(f"\nQR Code salvo em: {qr_path}")
|
||||||
|
print(f"Diretório atual: {os.getcwd()}")
|
||||||
|
print(f"O arquivo existe? {os.path.exists(qr_path)}")
|
||||||
|
|
||||||
|
print("\nPasso a passo para configurar o OTP:")
|
||||||
|
print("1. Instale um aplicativo autenticador no seu celular")
|
||||||
|
print(" (Google Authenticator, Microsoft Authenticator, etc)")
|
||||||
|
print("2. Abra o aplicativo")
|
||||||
|
print("3. Selecione a opção para adicionar uma nova conta")
|
||||||
|
print("4. Escaneie o QR Code gerado")
|
||||||
|
print("\nOU, se preferir configuração manual:")
|
||||||
|
print(f"1. Use o segredo: {admin.otp_secret}")
|
||||||
|
print("2. Nome da conta: admin")
|
||||||
|
print("3. Tipo: Baseado em tempo (TOTP)")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nErro durante a execução: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
create_admin_user()
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey
|
from sqlalchemy import create_engine, Column, Integer, String, Boolean, Numeric, Date, ForeignKey
|
||||||
from sqlalchemy.orm import relationship, sessionmaker
|
from sqlalchemy.orm import relationship, sessionmaker
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
import pyotp
|
||||||
|
import os
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
engine = create_engine('sqlite:///database.db', echo=True)
|
engine = create_engine('sqlite:///database.db', echo=True)
|
||||||
@@ -130,6 +133,7 @@ class Setor(Base):
|
|||||||
|
|
||||||
relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="setor")
|
relatorios_cotas = relationship("RelatorioCotasMensais", back_populates="setor")
|
||||||
relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="setor")
|
relatorios_vendas = relationship("RelatorioVendasMateriais", back_populates="setor")
|
||||||
|
usuarios = relationship("Usuario", back_populates="setor")
|
||||||
|
|
||||||
class ComiteCentral(Base):
|
class ComiteCentral(Base):
|
||||||
__tablename__ = 'comites_centrais'
|
__tablename__ = 'comites_centrais'
|
||||||
@@ -164,4 +168,106 @@ class RelatorioVendasMateriais(Base):
|
|||||||
setor = relationship("Setor", back_populates="relatorios_vendas")
|
setor = relationship("Setor", back_populates="relatorios_vendas")
|
||||||
comite = relationship("ComiteCentral", back_populates="relatorios_vendas")
|
comite = relationship("ComiteCentral", back_populates="relatorios_vendas")
|
||||||
|
|
||||||
Base.metadata.create_all(engine)
|
class Usuario(Base):
|
||||||
|
__tablename__ = 'usuarios'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
username = Column(String(50), unique=True, nullable=False)
|
||||||
|
password_hash = Column(String(255), nullable=False)
|
||||||
|
email = Column(String(100), unique=True, nullable=True)
|
||||||
|
otp_secret = Column(String(32), nullable=True)
|
||||||
|
role_id = Column(Integer, ForeignKey('roles.id'), nullable=True)
|
||||||
|
setor_id = Column(Integer, ForeignKey('setores.id'), nullable=True)
|
||||||
|
ativo = Column(Boolean, default=True)
|
||||||
|
is_admin = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
role = relationship("Role", back_populates="usuarios")
|
||||||
|
setor = relationship("Setor", back_populates="usuarios")
|
||||||
|
|
||||||
|
def __init__(self, username, password, is_admin=False):
|
||||||
|
self.username = username
|
||||||
|
self.set_password(password)
|
||||||
|
self.otp_secret = pyotp.random_base32()
|
||||||
|
self.is_admin = is_admin
|
||||||
|
self.ativo = True
|
||||||
|
|
||||||
|
def set_password(self, password):
|
||||||
|
self.password_hash = generate_password_hash(password)
|
||||||
|
|
||||||
|
def check_password(self, password):
|
||||||
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
|
def verify_otp(self, otp_code):
|
||||||
|
totp = pyotp.TOTP(self.otp_secret)
|
||||||
|
return totp.verify(otp_code)
|
||||||
|
|
||||||
|
def get_otp_uri(self):
|
||||||
|
totp = pyotp.TOTP(self.otp_secret)
|
||||||
|
return totp.provisioning_uri(self.username, issuer_name="Sistema de Gestão")
|
||||||
|
|
||||||
|
class Role(Base):
|
||||||
|
__tablename__ = 'roles'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
nome = Column(String(50), unique=True, nullable=False)
|
||||||
|
nivel = Column(Integer, nullable=False) # Nível hierárquico (1: admin, 2: coordenador, 3: militante)
|
||||||
|
|
||||||
|
usuarios = relationship("Usuario", back_populates="role")
|
||||||
|
permissoes = relationship("RolePermissao", back_populates="role")
|
||||||
|
|
||||||
|
class Permissao(Base):
|
||||||
|
__tablename__ = 'permissoes'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
nome = Column(String(50), unique=True, nullable=False)
|
||||||
|
descricao = Column(String(255))
|
||||||
|
|
||||||
|
roles = relationship("RolePermissao", back_populates="permissao")
|
||||||
|
|
||||||
|
class RolePermissao(Base):
|
||||||
|
__tablename__ = 'roles_permissoes'
|
||||||
|
|
||||||
|
role_id = Column(Integer, ForeignKey('roles.id'), primary_key=True)
|
||||||
|
permissao_id = Column(Integer, ForeignKey('permissoes.id'), primary_key=True)
|
||||||
|
|
||||||
|
role = relationship("Role", back_populates="permissoes")
|
||||||
|
permissao = relationship("Permissao", back_populates="roles")
|
||||||
|
|
||||||
|
# Remover o banco de dados existente (se existir)
|
||||||
|
if os.path.exists('database.db'):
|
||||||
|
os.remove('database.db')
|
||||||
|
|
||||||
|
# Criar todas as tabelas novamente
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
# Criar roles iniciais
|
||||||
|
def create_initial_data():
|
||||||
|
session = get_db_connection()
|
||||||
|
try:
|
||||||
|
# Criar role de admin
|
||||||
|
admin_role = Role(nome="Administrador", nivel=1)
|
||||||
|
session.add(admin_role)
|
||||||
|
session.flush() # Para obter o ID da role
|
||||||
|
|
||||||
|
# Criar usuário admin
|
||||||
|
admin = Usuario(
|
||||||
|
username="admin",
|
||||||
|
password="admin123",
|
||||||
|
is_admin=True
|
||||||
|
)
|
||||||
|
admin.role_id = admin_role.id
|
||||||
|
session.add(admin)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
print(f"Segredo OTP do admin: {admin.otp_secret}")
|
||||||
|
print("Usuário admin criado com sucesso!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro ao criar dados iniciais: {e}")
|
||||||
|
session.rollback()
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
# Executar a criação dos dados iniciais
|
||||||
|
if __name__ == "__main__":
|
||||||
|
create_initial_data()
|
||||||
@@ -1,20 +1,14 @@
|
|||||||
black==24.10.0
|
Flask==3.0.2
|
||||||
blinker==1.9.0
|
Flask-SQLAlchemy==3.1.1
|
||||||
click==8.1.7
|
SQLAlchemy==2.0.27
|
||||||
Flask==3.1.0
|
Werkzeug==3.0.1
|
||||||
greenlet==3.1.1
|
pyotp==2.9.0
|
||||||
importlib_metadata==8.5.0
|
qrcode==7.4.2
|
||||||
itsdangerous==2.2.0
|
pillow==11.0.0
|
||||||
Jinja2==3.1.4
|
python-dotenv==1.0.1
|
||||||
MarkupSafe==3.0.2
|
flask-login==0.6.3
|
||||||
mypy-extensions==1.0.0
|
flask-wtf==1.2.1
|
||||||
mysql-connector-python==9.1.0
|
email-validator==2.1.0.post1
|
||||||
packaging==24.2
|
|
||||||
pathspec==0.12.1
|
|
||||||
platformdirs==4.3.6
|
|
||||||
SQLAlchemy==2.0.36
|
|
||||||
tomli==2.2.1
|
|
||||||
typing_extensions==4.12.2
|
|
||||||
Werkzeug==3.1.3
|
|
||||||
zipp==3.21.0
|
|
||||||
Bootstrap-Flask==2.4.1
|
Bootstrap-Flask==2.4.1
|
||||||
|
flask-bootstrap5==0.1.dev1
|
||||||
|
|
||||||
|
|||||||
@@ -6,34 +6,39 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 offset-md-3">
|
<div class="col-md-6 offset-md-3">
|
||||||
<h2 class="mb-4">Login</h2>
|
<div class="card mt-5">
|
||||||
|
<div class="card-header">
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
<h3 class="text-center">Login</h3>
|
||||||
{% if messages %}
|
|
||||||
{% for category, message in messages %}
|
|
||||||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<form method="post">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="username" class="form-label">Usuário:</label>
|
|
||||||
<input type="text" class="form-control" id="username" name="username" required>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<div class="mb-3">
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
<label for="password" class="form-label">Senha:</label>
|
{% if messages %}
|
||||||
<input type="password" class="form-control" id="password" name="password" required>
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Usuário</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Senha</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="otp" class="form-label">Código OTP</label>
|
||||||
|
<input type="text" class="form-control" id="otp" name="otp" required>
|
||||||
|
<small class="form-text text-muted">Digite o código de 6 dígitos do seu aplicativo autenticador</small>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Entrar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="otp" class="form-label">Código OTP:</label>
|
|
||||||
<input type="text" class="form-control" id="otp" name="otp" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Entrar</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
41
templates/mostrar_qr_code.html
Normal file
41
templates/mostrar_qr_code.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Configurar Autenticação em Dois Fatores{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 offset-md-2 text-center">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>Configure a Autenticação em Dois Fatores</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="lead">Siga os passos abaixo para configurar a autenticação em dois fatores:</p>
|
||||||
|
|
||||||
|
<ol class="text-start mb-4">
|
||||||
|
<li>Instale um aplicativo autenticador no seu celular (Google Authenticator, Microsoft Authenticator, etc)</li>
|
||||||
|
<li>Abra o aplicativo e escaneie o QR Code abaixo</li>
|
||||||
|
<li>O aplicativo irá gerar um código de 6 dígitos a cada 30 segundos</li>
|
||||||
|
<li>Use este código ao fazer login no sistema</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<img src="https://chart.googleapis.com/chart?cht=qr&chs=300x300&chl={{ qr_uri|urlencode }}"
|
||||||
|
class="img-fluid" alt="QR Code para OTP">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Importante:</strong> Guarde este QR Code em um lugar seguro.
|
||||||
|
Você precisará dele caso troque de celular ou reinstale o aplicativo autenticador.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<a href="{{ url_for('login') }}" class="btn btn-primary">Ir para Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
58
templates/novo_usuario.html
Normal file
58
templates/novo_usuario.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Novo Usuário{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 offset-md-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>Cadastro de Novo Usuário</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Nome de usuário</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">E-mail</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Senha</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="confirm_password" class="form-label">Confirmar Senha</label>
|
||||||
|
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if session.get('is_admin') %}
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="is_admin" name="is_admin">
|
||||||
|
<label class="form-check-label" for="is_admin">Usuário Administrador</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Cadastrar</button>
|
||||||
|
<a href="{{ url_for('home') }}" class="btn btn-secondary">Voltar</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user