diff --git a/app.py b/app.py index d2c559e..fd2f1a5 100644 --- a/app.py +++ b/app.py @@ -1,78 +1,224 @@ from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory -from flask_pymongo import PyMongo +from flask_pymongo import PyMongo, ObjectId +from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user from werkzeug.utils import secure_filename +from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime +from flask_pymongo import ObjectId import os +from uuid import uuid4 +from flask_wtf import FlaskForm +from wtforms import StringField, FileField, SubmitField, DateTimeField, SelectField, PasswordField +from wtforms.validators import DataRequired, Length -app = Flask(__name__) -app.config["MONGO_URI"] = "mongodb://localhost:27017/mapPinsDB" -app.config['UPLOAD_FOLDER'] = 'uploads' -app.config['SECRET_KEY'] = 'supersecretkey' -app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} -mongo = PyMongo(app) +def create_app(): + app = Flask(__name__) + app.config["MONGO_URI"] = "mongodb://localhost:27017/mapDB" + app.config['UPLOAD_FOLDER'] = 'uploads' + app.config['SECRET_KEY'] = 'supersecretkey' + app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} + + mongo = PyMongo(app) + login_manager = LoginManager(app) + login_manager.session_protection = "strong" #form = PinForm() -from flask_wtf import FlaskForm -from wtforms import StringField, FileField, SubmitField, DateTimeField, SelectField -from wtforms.validators import DataRequired + class User(UserMixin): + def __init__(self, user_data): + self.id = str(user_data['_id']) + self.username = user_data['username'] + self.referral_code = user_data['referral_code'] + self.invited_by = user_data.get('invited_by') + self.is_admin = user_data.get('is_admin', False) + self.pwd = user_data.get('pwd') -class PinForm(FlaskForm): - description = StringField('Description', validators=[DataRequired()]) - photo = FileField('Evidencia fotogénica', validators=[DataRequired()]) - timedate = DateTimeField(default=datetime.now()) - typeofpin = SelectField('Tipo de cosa', choices=['bache', 'coladera', 'obra sin terminar', 'escombro', 'robo-asalto']) - submit = SubmitField('Agregar') + @staticmethod + def get(user_id): + user_data = mongo.db.users.find_one({"_id": ObjectId(user_id)}) + if user_data: + return User(user_data) + else: + return None + class PinForm(FlaskForm): + description = StringField('Description', validators=[DataRequired()]) + photo = FileField('Evidencia fotogénica', validators=[DataRequired()]) + timedate = DateTimeField(default=datetime.now()) + typeofpin = SelectField('Tipo de cosa', choices=['bache', 'coladera', 'obra sin terminar', 'escombro', 'robo-asalto']) + submit = SubmitField('Agregar') -def allowed_file(filename): - return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS'] + class LoginForm(FlaskForm): + username = StringField('Usuario', validators=[DataRequired()]) + pwd = PasswordField('Tu clave', validators=[DataRequired()]) + submit = SubmitField('Entrar') -@app.route('/', methods=['GET', 'POST']) -def index(): - if request.method == 'GET': - form = PinForm() - pins = mongo.db.pins.find() - return render_template('index.html', pins=pins, form=form) - else: -#@app.route('/add_pin') -#def add_pin(): - form = request.form -# if form.validate_on_submit(): - #description = form.description.data - try: - photo = request.files["photo"] - except Exception as e: - print(e) - if photo and allowed_file(photo.filename): - #try: - filename = secure_filename(photo.filename) - filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) - photo.save(filepath) - - pin = { - #'description': description, - 'time': datetime.now(), - 'photo': filepath, - 'lat': request.form['lat'], - 'lng': request.form['lng'], - 'typeofpin': request.form['typeofpin'], + def Unique(model, field, message=None): + def _unique(form, field_data): + if mongo.db[model.__name__.lower()].find_one({field.name: field_data.data}): + raise ValidationError(message or f"{field.name} must be unique.") + return _unique + + class RegistrationForm(FlaskForm): + username = StringField('Nombre de usuarix', validators=[DataRequired(), Unique('users', StringField('username', message="Username already exists"))]) + pwd = PasswordField('Clave', validators=[DataRequired(), Length(min=10), Unique('users', StringField('pwd', message="Username already exists"))]) + referral = StringField('ID de quien te invito', [DataRequired()]) + submit = SubmitField('Registrar') + + def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS'] + + @app.route('/', methods=['GET', 'POST']) + def index(): + if request.method == 'GET': + form = PinForm() + pins = mongo.db.pins.find() + return render_template('index.html', pins=pins, form=form) + else: + #@app.route('/add_pin') + #def add_pin(): + form = request.form + # if form.validate_on_submit(): + #description = form.description.data + try: + photo = request.files["photo"] + except Exception as e: + print(e) + if photo and allowed_file(photo.filename): + #try: + filename = secure_filename(photo.filename) + filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + photo.save(filepath) + + pin = { + #'description': description, + 'time': datetime.now(), + 'photo': filepath, + 'lat': request.form['lat'], + 'lng': request.form['lng'], + 'typeofpin': request.form['typeofpin'], + 'added_by': current_user.id, + } + mongo.db.pins.insert_one(pin) + flash('¡Gracias por tu aportación!') + return redirect(url_for('index')) + else: + return allowed_file(photo.filename), 404 + #render_template('index.html', pins=pins, form=form) + #except Exception as e: + # flash(f'An error occurred: {e}') + #return redirect(url_for('add_pin')) + #else: + # flash('Invalid file type. Only images are allowed.') + #return render_template('index.html', form=form) + @app.route('/uploads/') + def download_file(name): + return send_from_directory(app.config["UPLOAD_FOLDER"], name) + + @login_manager.user_loader + def load_user(user_id): + return User.get(user_id) + + @app.route('/registrame/', methods=['GET', 'POST']) + def registrame(referral_code): + inviter = mongo.db.users.find_one({"referral_code": referral_code}) + if not inviter: + return "Ooops, parece que nadie te invitó aquí", 404 + + if request.method == 'POST': + username = request.form['username'] + pwd = request.form['pwd'] + new_user_data = { + "username": username, + "pwd": generate_password_hash(pwd), + "referral_code": str(uuid4()), + "invited_by": str(inviter['_id']), + "is_admin": False } - mongo.db.pins.insert_one(pin) - flash('¡Gracias por tu aportación!') + new_user_id = mongo.db.users.insert_one(new_user_data).inserted_id + invite_link = url_for('registrame', referral_code=new_user_data['referral_code'], _external=True) + return render_template('dashboard.html', invite_link=invite_link) + + if request.method == 'GET': + return render_template('register.html', form=RegistrationForm()) + + @app.route('/thelogin', methods=['GET', 'POST']) + def thelogin(): + form2 = LoginForm() + if request.method == 'POST': + username = request.form['username'] + pwd = request.form['pwd'] + user_data = mongo.db.users.find_one({"username": username}) + if user_data and check_password_hash(user_data['pwd'], pwd): + user = User(user_data) + login_user(user) + return redirect(url_for('dashboard')) + else: + return render_template('login.html', messages = 'Ooops, no hay tal usuario') + return render_template('login.html',form=form2) + + @app.route('/logout') + @login_required + def logout(): + logout_user() + flash('You have successfully logged yourself out.') + return redirect('/') + + @app.route('/desheredame/') + @login_required + def desheredame(user_id): + if not current_user.is_admin: return redirect(url_for('index')) else: - return allowed_file(photo.filename), 404 - #render_template('index.html', pins=pins, form=form) - #except Exception as e: - # flash(f'An error occurred: {e}') - #return redirect(url_for('add_pin')) - #else: - # flash('Invalid file type. Only images are allowed.') - #return render_template('index.html', form=form) -@app.route('/uploads/') -def download_file(name): - return send_from_directory(app.config["UPLOAD_FOLDER"], name) + user_to_remove = mongo.db.users.find_one({"invited_by": user_id}) + if not user_to_remove: + return "A este ya me lo desheredaron", 404 + else: + mongo.db.users.delete_many({"invited_by": user_id}) + mongo.db.users.delete_one({"_id": ObjectId(user_id)}) + mongo.db.pins.delete_many({"user_id": user_id}) + return redirect(url_for('dashboard')) + @app.route("/remove_pin/") + @login_required + def remove_pin(pin_id): + actual_pin = mongo.db.pins.find_one({"_id": pin_id}) + added_by = actual_pin.added_by + if not current_user.is_admin or current_user.id != added_by: + return redirect(url_for('index')) + else: + mongo.db.pins.delete_one({"_id": ObjectId(pin_id)}) + return redirect(url_for('dashboard')) + + @app.route('/dashboard') + @login_required + def dashboard(): + if not current_user.is_authenticated: + return redirect(url_for('thelogin')) + if not current_user.is_admin: + pins = list(mongo.db.pins.find({"added_by": current_user.id})) + return render_template('dashboard.html', pins=pins) + if current_user.is_admin: + users = list(mongo.db.users.find()) + pins = list(mongo.db.pins.find()) + return render_template('dashboard.html', users=users, pins=pins) + + @app.cli.command('add_user') + def add_user(): + username = input("Enter Username:\n") + pwd = input("Add pass:\n") + + admin_user = { + "username": username, + "pwd": generate_password_hash(pwd), + "referral_code": str(uuid4()), + "invited_by": None, + "is_admin": True + } + mongo.db.users.insert_one(admin_user) + print(f"Admin {username} added! Referral code is {admin_user['referral_code']}") + return app + +app=create_app() if __name__ == '__main__': app.run(debug=True) diff --git a/templates/base.html b/templates/base.html index 363c86b..8a73abb 100644 --- a/templates/base.html +++ b/templates/base.html @@ -11,6 +11,7 @@ + + + + + + + {% with messages = get_flashed_messages() %} + {% if messages %} +
    + {% for message in messages %} +
  • {{message}}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} +
+ +{% block content %} + +{% endblock %} + +
+ + + +