página de usuário
This commit is contained in:
1 parent
0a8538e5f2
commit
00f001aa6d
3 files changed
+211
-113
No files matched your search
@@ -40,6 +40,10 @@ bcrypt = Bcrypt(app)
|
||||
login_manager = LoginManager(app)
|
||||
login_manager.login_view = 'login'
|
||||
|
||||
# ==============================================================================
|
||||
# CLASSES UTILIZADAS
|
||||
# ==============================================================================
|
||||
|
||||
# --- MODELO DE USUÁRIO ---
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
@@ -54,6 +58,8 @@ class User(UserMixin, db.Model):
|
||||
cover_url = db.Column(db.String(255), default="/assets/img/default_cover.jpg")
|
||||
|
||||
projects = db.relationship('Project', backref='owner', lazy=True)
|
||||
samples = db.relationship('Sample', backref='owner', lazy=True)
|
||||
recordings = db.relationship('Recording', backref='owner', lazy=True)
|
||||
|
||||
class SecureModelView(ModelView):
|
||||
def is_accessible(self):
|
||||
@@ -75,6 +81,10 @@ class SecureIndexView(AdminIndexView):
|
||||
# return "<h1>Acesso Negado</h1><p>Você precisa ser admin.</p>", 403
|
||||
|
||||
return super(SecureIndexView, self).index()
|
||||
|
||||
# ==============================================================================
|
||||
# CLASSES DE ARTEFATOS
|
||||
# ==============================================================================
|
||||
|
||||
# Tabela de Projetos
|
||||
class Project(db.Model):
|
||||
@@ -86,6 +96,21 @@ class Project(db.Model):
|
||||
# Chave estrangeira ligando ao usuário
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
class Sample(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
filename = db.Column(db.String(255), nullable=False)
|
||||
display_name = db.Column(db.String(255), nullable=False)
|
||||
category = db.Column(db.String(50), default="Uncategorized") # Ex: drums, bass
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
class Recording(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
filename = db.Column(db.String(255), nullable=False)
|
||||
display_name = db.Column(db.String(255), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
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()
|
||||
@@ -322,30 +347,25 @@ 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
|
||||
if User.query.filter_by(username=new_username).first():
|
||||
return jsonify({"error": "Nome 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']
|
||||
if 'bio' in data: current_user.bio = data['bio'][:240]
|
||||
if 'tags' in data: current_user.tags = data['tags']
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Perfil atualizado!"}), 200
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
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():
|
||||
# Projetos
|
||||
user_projects = []
|
||||
for p in current_user.projects:
|
||||
user_projects.append({
|
||||
@@ -356,6 +376,22 @@ def user_profile():
|
||||
"download_link": f"/api/download/{p.filename}"
|
||||
})
|
||||
|
||||
# Samples
|
||||
user_samples = []
|
||||
for s in current_user.samples:
|
||||
# Define a URL pública baseada na estrutura do seu Jekyll/Apache
|
||||
# Ex: se SAMPLE_SRC é mapeado para /samples/ no navegador
|
||||
# Ajuste o prefixo '/samples/' conforme sua configuração de pastas públicas
|
||||
public_url = f"/samples/{s.category}/{s.filename}"
|
||||
|
||||
user_samples.append({
|
||||
"id": s.id,
|
||||
"display_name": s.display_name,
|
||||
"category": s.category,
|
||||
"created_at": s.created_at.strftime("%d/%m/%Y"),
|
||||
"download_link": public_url
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"username": current_user.username,
|
||||
"bio": current_user.bio,
|
||||
@@ -363,12 +399,9 @@ def user_profile():
|
||||
"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
|
||||
"samples": user_samples
|
||||
})
|
||||
|
||||
|
||||
|
||||
@app.route('/api/project/<int:project_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_project(project_id):
|
||||
@@ -393,6 +426,7 @@ def delete_project(project_id):
|
||||
return jsonify({"message": "Projeto removido com sucesso"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# ==============================================================================
|
||||
# ROTAS PRINCIPAIS
|
||||
# ==============================================================================
|
||||
@@ -540,22 +574,70 @@ def resolve_samples():
|
||||
|
||||
|
||||
@app.route('/api/upload/sample', methods=['POST'])
|
||||
@login_required # <--- PROTEGIDO
|
||||
@login_required
|
||||
def upload_sample_standalone():
|
||||
if 'sample_file' not in request.files: return jsonify({'error': 'Nenhum arquivo'}), 400
|
||||
if 'sample_file' not in request.files:
|
||||
return jsonify({'error': 'Nenhum arquivo'}), 400
|
||||
|
||||
file = request.files['sample_file']
|
||||
subfolder = request.form.get('subfolder', '').strip()
|
||||
# Pega a pasta escolhida pelo usuário, padrão 'uncategorized'
|
||||
raw_subfolder = request.form.get('subfolder', 'uncategorized').strip()
|
||||
|
||||
# Limpa o nome da pasta para evitar ../../etc/passwd
|
||||
safe_subfolder = secure_filename(raw_subfolder)
|
||||
if not safe_subfolder:
|
||||
safe_subfolder = 'uncategorized'
|
||||
|
||||
if file and allowed_sample(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
safe_subfolder = subfolder.replace('..', '').strip('/')
|
||||
|
||||
# Cria o caminho físico final: SAMPLE_SRC + Categoria + Arquivo
|
||||
target_dir = os.path.join(SAMPLE_SRC, safe_subfolder)
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
file.save(os.path.join(target_dir, filename))
|
||||
generate_manifests(SRC_MMPSEARCH)
|
||||
run_jekyll_build()
|
||||
return jsonify({'message': 'Sample enviado!'}), 200
|
||||
return jsonify({'error': 'Erro no arquivo'}), 400
|
||||
|
||||
file_path = os.path.join(target_dir, filename)
|
||||
file.save(file_path)
|
||||
|
||||
# Salva no Banco
|
||||
new_sample = Sample(
|
||||
filename=filename,
|
||||
display_name=filename,
|
||||
category=safe_subfolder,
|
||||
owner=current_user
|
||||
)
|
||||
db.session.add(new_sample)
|
||||
db.session.commit()
|
||||
|
||||
# Opcional: Atualizar índices se necessário
|
||||
# generate_manifests(SRC_MMPSEARCH)
|
||||
# run_jekyll_build()
|
||||
|
||||
return jsonify({'message': 'Sample enviado com sucesso!'}), 200
|
||||
|
||||
return jsonify({'error': 'Tipo de arquivo inválido'}), 400
|
||||
|
||||
@app.route('/api/sample/<int:sample_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_sample(sample_id):
|
||||
sample = Sample.query.get_or_404(sample_id)
|
||||
|
||||
if sample.owner != current_user and not current_user.is_admin:
|
||||
return jsonify({"error": "Proibido"}), 403
|
||||
|
||||
try:
|
||||
# 1. Tenta remover o arquivo físico
|
||||
file_path = os.path.join(SAMPLE_SRC, sample.category, sample.filename)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
# 2. Remove do banco
|
||||
db.session.delete(sample)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "Sample apagado"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# Inicializa o Flask-Admin
|
||||
admin = Admin(
|
||||
app,
|
||||
@@ -568,8 +650,6 @@ admin = Admin(
|
||||
admin.add_view(SecureModelView(User, db.session, name="Gerenciar Usuários"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
# DEBUG: Isso vai mostrar no log todas as rotas que o Flask reconheceu
|
||||
print(app.url_map)
|
||||
|
||||
print("Iniciando servidor na porta 33002 (HTTP Mode)...")
|
||||
admin = Admin(app, name='MMP Admin', url='/api/admin', index_view=SecureIndexView())
|
||||
admin.add_view(SecureModelView(User, db.session, name="Gerenciar Usuários"))
|
||||
app.run(host="0.0.0.0", port=33002, debug=True)
|
||||
Reference in new issue
Block a user