página de usuário
This commit is contained in:
1 parent
110a762ff9
commit
29645f3fde
9 files changed
+6975
-16
No files matched your search
@@ -8,10 +8,12 @@ import json
|
||||
import shutil
|
||||
import time
|
||||
import io
|
||||
import threading
|
||||
|
||||
from flask import Flask, request, jsonify, send_file, redirect
|
||||
from flask_cors import CORS
|
||||
from werkzeug.utils import secure_filename
|
||||
from datetime import datetime
|
||||
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_bcrypt import Bcrypt
|
||||
@@ -43,8 +45,15 @@ class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(150), unique=True, nullable=False)
|
||||
password = db.Column(db.String(150), nullable=False)
|
||||
# Novo campo para dizer se é chefe ou não
|
||||
is_admin = db.Column(db.Boolean, default=False)
|
||||
|
||||
# Detalhes do usuário
|
||||
bio = db.Column(db.String(240), default="")
|
||||
tags = db.Column(db.String(500), default="") # Vamos salvar separado por vírgula
|
||||
avatar_url = db.Column(db.String(255), default="/assets/img/default_avatar.png")
|
||||
cover_url = db.Column(db.String(255), default="/assets/img/default_cover.jpg")
|
||||
|
||||
projects = db.relationship('Project', backref='owner', lazy=True)
|
||||
|
||||
class SecureModelView(ModelView):
|
||||
def is_accessible(self):
|
||||
@@ -67,6 +76,16 @@ class SecureIndexView(AdminIndexView):
|
||||
|
||||
return super(SecureIndexView, self).index()
|
||||
|
||||
# Tabela de Projetos
|
||||
class Project(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
filename = db.Column(db.String(255), nullable=False) # Nome do arquivo físico .mmp
|
||||
display_name = db.Column(db.String(255), nullable=False) # Nome "bonito" para exibir
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# Chave estrangeira ligando ao usuário
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
# Cria o banco na inicialização se não existir
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
@@ -93,11 +112,17 @@ def run_jekyll_build():
|
||||
print("Iniciando build do Jekyll...")
|
||||
command = [BUNDLE_PATH, "exec", "jekyll", "build", "--destination", "/var/www/html/trens/mmpSearch/"]
|
||||
try:
|
||||
# Passamos env=env_vars para que ele ache o ruby3.2
|
||||
subprocess.run(command, check=True, cwd=BASE_DATA, capture_output=True, text=True, env=env_vars)
|
||||
print("Jekyll Build Sucesso!")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"ERRO no Jekyll Build: {e.stderr}")
|
||||
# Redirecionamos a saída para DEVNULL para não encher o buffer e travar
|
||||
subprocess.Popen(
|
||||
command,
|
||||
cwd=BASE_DATA,
|
||||
env=env_vars,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
print("Jekyll Build iniciado em segundo plano (background).")
|
||||
except Exception as e:
|
||||
print(f"Erro ao iniciar Jekyll: {e}")
|
||||
|
||||
def load_manifest_keys():
|
||||
"""Carrega as pastas de fábrica (keys do manifesto)."""
|
||||
@@ -212,15 +237,38 @@ def update_xml_paths_exact(mmp_filename, replacements):
|
||||
print(f"Erro crítico ao editar XML: {e}")
|
||||
return False
|
||||
|
||||
def process_and_build(filename):
|
||||
"""Encapsula a chamada do main.py"""
|
||||
result = process_single_file(filename)
|
||||
if result["success"]:
|
||||
rebuild_indexes()
|
||||
run_jekyll_build()
|
||||
def run_heavy_tasks_in_background():
|
||||
"""Esta função roda isolada sem travar o usuário"""
|
||||
print("--- [BACKGROUND] Iniciando reconstrução de índices ---")
|
||||
try:
|
||||
# 1. Isso é Python puro e PESADO (aqui é onde estava travando)
|
||||
rebuild_indexes()
|
||||
|
||||
# 2. Isso gera os manifestos (Python puro)
|
||||
generate_manifests(SRC_MMPSEARCH)
|
||||
|
||||
# 3. Isso chama o subprocesso do Jekyll (Externo)
|
||||
# Mantém sua função original que usa subprocess
|
||||
run_jekyll_build()
|
||||
|
||||
print("--- [BACKGROUND] Tarefas concluídas com sucesso ---")
|
||||
except Exception as e:
|
||||
print(f"--- [BACKGROUND] Erro: {e} ---")
|
||||
|
||||
def process_and_build(filename):
|
||||
"""Função chamada pela rota de upload"""
|
||||
# Processamento inicial do arquivo (rápido)
|
||||
result = process_single_file(filename)
|
||||
|
||||
if result["success"]:
|
||||
# Em vez de chamar rebuild_indexes() direto, criamos a Thread
|
||||
# O Flask vai responder o return abaixo imediatamente,
|
||||
# enquanto a thread continua rodando no servidor.
|
||||
task_thread = threading.Thread(target=run_heavy_tasks_in_background)
|
||||
task_thread.start()
|
||||
|
||||
return jsonify({
|
||||
"message": "Sucesso! Projeto processado.",
|
||||
"message": "Sucesso! O projeto foi recebido. O site será atualizado em instantes.",
|
||||
"data": result["data"]
|
||||
}), 200
|
||||
else:
|
||||
@@ -267,6 +315,84 @@ def check_auth():
|
||||
return jsonify({"logged_in": True, "user": current_user.username})
|
||||
return jsonify({"logged_in": False})
|
||||
|
||||
# No upload_server.py, adicione esta rota
|
||||
@app.route('/api/user/update', methods=['POST'])
|
||||
@login_required
|
||||
def update_profile():
|
||||
data = request.json
|
||||
new_username = data.get('username')
|
||||
|
||||
# Verificar disponibilidade do username se ele mudou
|
||||
if new_username and new_username != current_user.username:
|
||||
existing = User.query.filter_by(username=new_username).first()
|
||||
if existing:
|
||||
return jsonify({"error": "Nome de usuário já está em uso."}), 400
|
||||
current_user.username = new_username
|
||||
|
||||
if 'bio' in data:
|
||||
current_user.bio = data['bio'][:240] # Corta em 240 chars
|
||||
|
||||
if 'tags' in data:
|
||||
# Limpa e formata as tags
|
||||
current_user.tags = data['tags']
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Perfil atualizado!"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": "Erro ao salvar."}), 500
|
||||
|
||||
# Atualize a rota user_profile antiga para retornar os novos dados
|
||||
@app.route('/api/user/profile', methods=['GET'])
|
||||
@login_required
|
||||
def user_profile():
|
||||
user_projects = []
|
||||
for p in current_user.projects:
|
||||
user_projects.append({
|
||||
"id": p.id,
|
||||
"display_name": p.display_name,
|
||||
"filename": p.filename,
|
||||
"created_at": p.created_at.strftime("%d/%m/%Y"),
|
||||
"download_link": f"/api/download/{p.filename}"
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"username": current_user.username,
|
||||
"bio": current_user.bio,
|
||||
"tags": current_user.tags,
|
||||
"avatar": current_user.avatar_url,
|
||||
"cover": current_user.cover_url,
|
||||
"projects": user_projects,
|
||||
"samples": [], # Placeholder enquanto não cria a tabela Samples
|
||||
"recordings": [] # Placeholder enquanto não cria a tabela Recordings
|
||||
})
|
||||
|
||||
|
||||
|
||||
@app.route('/api/project/<int:project_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_project(project_id):
|
||||
project = Project.query.get_or_404(project_id)
|
||||
|
||||
# Segurança: Só o dono ou admin pode apagar
|
||||
if project.owner != current_user and not current_user.is_admin:
|
||||
return jsonify({"error": "Permissão negada"}), 403
|
||||
|
||||
try:
|
||||
# 1. Remove do Banco
|
||||
db.session.delete(project)
|
||||
db.session.commit()
|
||||
|
||||
# 2. Remove o arquivo físico (Opcional, mas recomendado para não lotar o disco)
|
||||
file_path = os.path.join(MMP_FOLDER, project.filename)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
# Nota: Você pode querer rodar o rebuild_indexes() aqui também se apagar o arquivo
|
||||
|
||||
return jsonify({"message": "Projeto removido com sucesso"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
# ==============================================================================
|
||||
# ROTAS PRINCIPAIS
|
||||
# ==============================================================================
|
||||
@@ -355,7 +481,18 @@ def upload_file():
|
||||
return jsonify({"error": "Falha crítica na descompactação."}), 400
|
||||
elif ext == ".mmp" and upload_path != final_mmp_path:
|
||||
shutil.move(upload_path, final_mmp_path)
|
||||
|
||||
|
||||
# Registra no banco de dados vinculado ao usuário logado
|
||||
new_project = Project(
|
||||
filename=final_mmp_filename,
|
||||
display_name=clean_name, # Ou name_without_ext se preferir o original
|
||||
owner=current_user # Flask-Login pega o user atual
|
||||
)
|
||||
db.session.add(new_project)
|
||||
db.session.commit()
|
||||
print(f"Projeto {clean_name} associado ao usuário {current_user.username}")
|
||||
# -----------------------------
|
||||
#
|
||||
is_clean, missing_files = analyze_mmp_dependencies(final_mmp_filename)
|
||||
|
||||
if not is_clean:
|
||||
|
||||
Reference in new issue
Block a user