- Tabela FUNCIONARIO_PAINEL (nova, ja criada no banco de dev) + app/db/funcionarios.py: nome/login/senha (hash scrypt, stdlib, sem dependencia nova) por funcionario, nunca excluido de verdade (so ativo/inativo). - app/senha.py: gerar_hash/verificar_senha com hashlib.scrypt + salt aleatorio por senha. - app/auth.py: login passa a checar duas fontes, nessa ordem - ADMIN_USER/ADMIN_PASSWORD (bootstrap, continua exigindo as duas preenchidas) e depois FUNCIONARIO_PAINEL (so contas ATIVO=1). O nome retornado (usado no log) passa a ser o nome de verdade do funcionario, nao mais sempre 'admin'. - Tela /funcionarios: listar, criar (com confirmacao de senha e checagem de login duplicado), editar nome/login, redefinir senha (form separado) e ativar/desativar. Tudo logado em LOG_PAINEL. - Testado ponta a ponta contra o app real: funcionario criado via tela consegue logar de verdade, e a acao aparece no /log com o nome de quem fez. 93/93 testes passando (23 novos).
120 lines
3.8 KiB
Python
120 lines
3.8 KiB
Python
"""
|
|
CRUD de FUNCIONARIO_PAINEL — contas individuais de funcionário para login
|
|
no painel (substitui/complementa o ADMIN_USER/ADMIN_PASSWORD único).
|
|
|
|
A tabela já existe no banco de dev. Em qualquer outro ambiente, crie-a
|
|
primeiro com:
|
|
|
|
CREATE TABLE FUNCIONARIO_PAINEL (
|
|
ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
NOME VARCHAR(80) NOT NULL,
|
|
LOGIN VARCHAR(40) NOT NULL,
|
|
SENHA_HASH VARCHAR(255) NOT NULL,
|
|
ATIVO TINYINT(1) NOT NULL DEFAULT 1,
|
|
CRIADO_EM DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (ID),
|
|
UNIQUE KEY UQ_FUNCIONARIO_PAINEL_LOGIN (LOGIN)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
|
|
|
Nunca excluído de verdade — "excluir" aqui é alternar ATIVO para 0
|
|
(mesma lógica de nunca apagar histórico usada no resto do projeto).
|
|
"""
|
|
from .connection import get_connection
|
|
from ..senha import gerar_hash
|
|
|
|
|
|
def listar_funcionarios() -> list[dict]:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute("SELECT ID, NOME, LOGIN, ATIVO, CRIADO_EM FROM FUNCIONARIO_PAINEL ORDER BY NOME")
|
|
return cur.fetchall()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def obter_funcionario(funcionario_id: int) -> dict | None:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute(
|
|
"SELECT ID, NOME, LOGIN, ATIVO, CRIADO_EM FROM FUNCIONARIO_PAINEL WHERE ID = %s", (funcionario_id,)
|
|
)
|
|
return cur.fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def obter_por_login(login: str) -> dict | None:
|
|
"""Usado pelo login (app/auth.py) — inclui SENHA_HASH, ao contrário das outras consultas."""
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute(
|
|
"SELECT ID, NOME, LOGIN, SENHA_HASH, ATIVO FROM FUNCIONARIO_PAINEL WHERE LOGIN = %s", (login,)
|
|
)
|
|
return cur.fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def login_em_uso(login: str, ignorar_id: int | None = None) -> bool:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
sql = "SELECT ID FROM FUNCIONARIO_PAINEL WHERE LOGIN = %s"
|
|
params = [login]
|
|
if ignorar_id is not None:
|
|
sql += " AND ID != %s"
|
|
params.append(ignorar_id)
|
|
cur.execute(sql, params)
|
|
return cur.fetchone() is not None
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def criar_funcionario(nome: str, login: str, senha: str) -> int:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute(
|
|
"INSERT INTO FUNCIONARIO_PAINEL (NOME, LOGIN, SENHA_HASH, ATIVO) VALUES (%s, %s, %s, 1)",
|
|
(nome, login, gerar_hash(senha)),
|
|
)
|
|
return cur.lastrowid
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def atualizar_funcionario(funcionario_id: int, nome: str, login: str) -> None:
|
|
"""Só nome/login — troca de senha é redefinir_senha()."""
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute(
|
|
"UPDATE FUNCIONARIO_PAINEL SET NOME = %s, LOGIN = %s WHERE ID = %s", (nome, login, funcionario_id)
|
|
)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def redefinir_senha(funcionario_id: int, nova_senha: str) -> None:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute(
|
|
"UPDATE FUNCIONARIO_PAINEL SET SENHA_HASH = %s WHERE ID = %s",
|
|
(gerar_hash(nova_senha), funcionario_id),
|
|
)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def alternar_ativo(funcionario_id: int) -> None:
|
|
conn = get_connection()
|
|
try:
|
|
with conn.cursor() as cur:
|
|
cur.execute("UPDATE FUNCIONARIO_PAINEL SET ATIVO = 1 - ATIVO WHERE ID = %s", (funcionario_id,))
|
|
finally:
|
|
conn.close()
|