├── __init__.py ├── src ├── config │ ├── __init__.py │ └── database.py ├── models │ ├── __init__.py │ ├── post.py │ ├── user.py │ ├── image_post.py │ └── message.py ├── routes │ └── __init__.py └── services │ ├── __init__.py │ └── userservice.py ├── env.txt ├── requirements.txt ├── .github └── dependabot.yml ├── LICENSE ├── README.md ├── .gitignore ├── app.py └── yarn.lock /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/routes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /env.txt: -------------------------------------------------------------------------------- 1 | DB_USER=gruwin 2 | DB_PASSWORD=marinmarin123 3 | DB_HOST=gruwin.mysql.pythonanywhere-services.com 4 | DB_NAME=gruwin$default -------------------------------------------------------------------------------- /src/config/database.py: -------------------------------------------------------------------------------- 1 | import mysql.connector 2 | 3 | def config(): 4 | dsource = mysql.connector.connect( 5 | user='gruwin', 6 | password='marinmarin123', 7 | host='gruwin.mysql.pythonanywhere-services.com', 8 | database='gruwin$default' 9 | ) 10 | return dsource 11 | 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==9.0.1 2 | autopep8==2.3.1 3 | click==8.1.3 4 | colorama==0.4.6 5 | Flask==3.0.3 6 | Flask-JWT-Extended==4.4.4 7 | Flask-RESTful==0.3.10 8 | itsdangerous==2.2.0 9 | Jinja2==3.1.4 10 | MarkupSafe==2.1.5 11 | mysql-connector-python==9.0.0 12 | protobuf==5.27.2 13 | pycodestyle==2.12.0 14 | PyJWT==2.8.0 15 | python-dotenv==1.0.1 16 | pytz==2024.1 17 | six==1.16.0 18 | Werkzeug==3.0.3 -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alexander M. Marin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/models/post.py: -------------------------------------------------------------------------------- 1 | from flask import request, jsonify 2 | from flask_restful import Resource 3 | from src.config.database import config 4 | 5 | class Posts(Resource): 6 | def get(self): 7 | db = config() 8 | cur = db.cursor() 9 | cur.execute("SELECT * FROM post") 10 | rows = cur.fetchall() 11 | posts = [] 12 | for row in rows: 13 | post = { 14 | "id": row[0], 15 | "userid": row[1], 16 | "public": row[2], 17 | "context": row[3], 18 | "category": row[4], 19 | "reward": row[5], 20 | "title": row[7], 21 | "datepost": row[6].strftime('%Y-%m-%d %H:%M:%S'), 22 | } 23 | posts.append(post) 24 | cur.close() 25 | return jsonify(posts) 26 | 27 | def post(self): 28 | data = request.get_json() 29 | userid = data['userid'] 30 | public = data['public'] 31 | context = data['context'] 32 | category = data['category'] 33 | reward = data['reward'] 34 | title = data['title'] 35 | 36 | db = config() 37 | cur = db.cursor() 38 | cur.execute( 39 | "INSERT INTO post (userid, public, context, category, reward, title, datepost) VALUES (%s, %s, %s, %s, %s, %s,now())", 40 | (userid, public, context, category, reward, title) 41 | ) 42 | db.commit() 43 | cur.close() 44 | return jsonify({'message': 'Datos agregados satisfactoriamente'}) 45 | -------------------------------------------------------------------------------- /src/services/userservice.py: -------------------------------------------------------------------------------- 1 | from flask import request, jsonify 2 | from flask_jwt_extended import create_access_token 3 | from src.config.database import config 4 | 5 | 6 | class UserService: 7 | 8 | 9 | def signup(self): 10 | username = request.json.get('username') 11 | email = request.json.get('email') 12 | password = request.json.get('password') 13 | 14 | 15 | if not username or not password: 16 | return jsonify({'message': 'Datos incompletos.'}), 400 17 | 18 | db = config() 19 | cur = db.cursor() 20 | cur.execute( 21 | "INSERT INTO account (username, email, password) VALUES (%s, %s, %s)", 22 | (username, email, password)) 23 | db.commit() 24 | cur.close() 25 | 26 | return jsonify({'message': 'User logged successful'}), 201 27 | 28 | def login(self): 29 | username = request.json.get('username') 30 | password = request.json.get('password') 31 | 32 | if not username or not password: 33 | return jsonify({'message': 'Please complete all the camps'}), 400 34 | 35 | db = config() 36 | cur = db.cursor() 37 | cur.execute('SELECT * FROM account WHERE username = %s', (username,)) 38 | user = cur.fetchone() 39 | cur.close() 40 | 41 | if not user: 42 | return jsonify({'message': 'User not found.'}), 401 43 | 44 | if user[1] == password: 45 | return jsonify({'message': 'Credentials error.'}), 401 46 | 47 | access_token = create_access_token(identity=user[0],expires_delta=False) 48 | 49 | return jsonify({'access_token': access_token}), 200 -------------------------------------------------------------------------------- /src/models/user.py: -------------------------------------------------------------------------------- 1 | from flask import request, jsonify 2 | from flask_restful import Resource 3 | from src.config.database import config 4 | 5 | class Users(Resource): 6 | def get(self): 7 | db = config() 8 | cur = db.cursor() 9 | cur.execute("SELECT * FROM users") 10 | rows = cur.fetchall() 11 | columnas = [desc[0] for desc in cur.description] 12 | resultado = [dict(zip(columnas, row)) for row in rows] 13 | cur.close() 14 | return jsonify(resultado) 15 | 16 | def post(self): 17 | data = request.get_json() 18 | connected = config() 19 | cursor = connected.cursor() 20 | cursor.execute( 21 | "SELECT * FROM users WHERE id = %s OR email = %s", 22 | (data['id'], data['email']) 23 | ) 24 | user = cursor.fetchone() 25 | if user: 26 | cursor.close() 27 | return jsonify({'message': 'El usuario ya existe'}) 28 | else: 29 | cursor.execute( 30 | "INSERT INTO users (id, name, email, gcoins, aboutme, regdate, country, photourl) VALUES (%s, %s, %s, %s, %s, now(), %s, %s)", 31 | tuple(data.values()) 32 | ) 33 | connected.commit() 34 | cursor.close() 35 | return jsonify({'message': 'Datos agregados satisfactoriamente'}) 36 | 37 | class User(Resource): 38 | def get(self, id=None): 39 | id = str(id) 40 | db = config() 41 | cur = db.cursor() 42 | cur.execute("SELECT * FROM users WHERE id = %s", (id,)) 43 | row = cur.fetchone() 44 | if not row: 45 | return {'message': 'User not found'}, 404 46 | columnas = [desc[0] for desc in cur.description] 47 | resultado = dict(zip(columnas, row)) 48 | cur.close() 49 | return resultado -------------------------------------------------------------------------------- /src/models/image_post.py: -------------------------------------------------------------------------------- 1 | from flask import request, jsonify, Response 2 | from flask_restful import Resource 3 | from src.config.database import config 4 | from werkzeug.utils import secure_filename 5 | import os 6 | 7 | class ImagePost(Resource): 8 | def get(self, postid): 9 | db = config() 10 | cur = db.cursor() 11 | cur.execute("SELECT image FROM image_post WHERE postid = %s", (postid,)) 12 | result = cur.fetchone() 13 | 14 | if result is None: 15 | return {'message': 'La imagen solicitada no existe.'}, 404 16 | 17 | image_data = result[0] 18 | cur.close() 19 | 20 | # Obtener la extensión del archivo a partir del nombre de archivo 21 | filename = f'image_{postid}' 22 | extension = os.path.splitext(filename)[-1] 23 | 24 | # Establecer el tipo MIME en la respuesta 25 | mimetype = f'image/{extension[1:]}' 26 | 27 | return Response(image_data, mimetype=mimetype) 28 | 29 | 30 | def post(self): 31 | data = request.form 32 | postid = data['postid'] 33 | image = request.files['image'] 34 | 35 | # Verificar que se haya enviado una imagen 36 | if image.filename == '': 37 | return jsonify({'message': 'No se ha seleccionado ninguna imagen.'}), 400 38 | 39 | # Verificar que la extensión de la imagen sea permitida 40 | allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'} 41 | if not allowed_file(image.filename, allowed_extensions): 42 | return jsonify({'message': 'El archivo de imagen no es válido.'}), 400 43 | 44 | # Guardar la imagen en el servidor 45 | filename = secure_filename(image.filename) 46 | image.save(filename) 47 | 48 | # Leer la imagen y guardarla en la base de datos 49 | with open(filename, 'rb') as f: 50 | image_data = f.read() 51 | 52 | db = config() 53 | cur = db.cursor() 54 | cur.execute("INSERT INTO image_post (postid, image) VALUES (%s, %s)", (postid, image_data)) 55 | db.commit() 56 | cur.close() 57 | 58 | return jsonify({'message': 'La imagen ha sido agregada correctamente.'}) 59 | 60 | 61 | def allowed_file(filename, allowed_extensions): 62 | return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions 63 | -------------------------------------------------------------------------------- /src/models/message.py: -------------------------------------------------------------------------------- 1 | from src.config.database import config 2 | from datetime import datetime 3 | 4 | class MessageService: 5 | 6 | def get_messages(self, sender_id, recipient_id): 7 | db = config() 8 | cur = db.cursor() 9 | cur.execute("SELECT * FROM messages WHERE (sender_id=%s AND recipient_id=%s) OR (sender_id=%s AND recipient_id=%s) ORDER BY created_at ASC", (sender_id, recipient_id, recipient_id, sender_id)) 10 | rows = cur.fetchall() 11 | messages = [] 12 | for row in rows: 13 | message_date = row[4].strftime('%Y-%m-%d %H:%M:%S') 14 | message = { 15 | "id": row[0], 16 | "sender_id": row[1], 17 | "recipient_id": row[2], 18 | "content": row[3], 19 | "created_at": message_date 20 | } 21 | messages.append(message) 22 | cur.close() 23 | return messages 24 | 25 | def create_message(self, sender_id, recipient_id, content): 26 | date_sent = datetime.now() 27 | 28 | db = config() 29 | cur = db.cursor() 30 | cur.execute("INSERT INTO messages (sender_id, recipient_id, content, created_at) VALUES (%s, %s, %s, %s)", (sender_id, recipient_id, content, date_sent)) 31 | db.commit() 32 | 33 | message_id = cur.lastrowid 34 | 35 | cur.execute("SELECT * FROM messages WHERE id=%s", (message_id,)) 36 | row = cur.fetchone() 37 | message_date = row[4].strftime('%Y-%m-%d %H:%M:%S') 38 | message = { 39 | "id": row[0], 40 | "sender_id": row[1], 41 | "recipient_id": row[2], 42 | "content": row[3], 43 | "created_at": message_date 44 | } 45 | cur.close() 46 | return message 47 | 48 | def update_message(self, id, data): 49 | text = data['text'] 50 | 51 | db = config() 52 | cur = db.cursor() 53 | cur.execute("UPDATE messages SET text=%s WHERE id=%s", (text, id)) 54 | db.commit() 55 | 56 | cur.execute("SELECT * FROM messages WHERE id=%s", (id,)) 57 | row = cur.fetchone() 58 | message_date = row[2].strftime('%Y-%m-%d %H:%M:%S') 59 | message = { 60 | "id": row[0], 61 | "user_id": row[1], 62 | "text": row[3], 63 | "date_sent": message_date 64 | } 65 | cur.close() 66 | return message 67 | 68 | def delete_message(self, id): 69 | db = config() 70 | cur = db.cursor() 71 | cur.execute("DELETE FROM messages WHERE id=%s", (id,)) 72 | db.commit() 73 | cur.close() 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask RESTful API 2 | 3 | This is a web application developed with Flask that provides a RESTful API for managing users, posts, messages, and images. The application also includes JWT authentication and CORS support. 4 | 5 | ## Installation 6 | 7 | 1. Clone this repository: 8 | 9 | ```bash 10 | git clone https://github.com/your_username/your_repository.git 11 | cd your_repository 12 | ``` 13 | 14 | 2. Create and activate a virtual environment: 15 | 16 | ```bash 17 | python3 -m venv venv 18 | source venv/bin/activate # On Windows use `venv\Scripts\activate` 19 | ``` 20 | 21 | 3. Install the dependencies: 22 | 23 | ```bash 24 | pip install -r requirements.txt 25 | ``` 26 | 27 | ## Configuration 28 | 29 | Make sure to configure the following variables in your configuration file: 30 | 31 | ```python 32 | app.config['JWT_SECRET_KEY'] = 'GRUWIN-ADMIN-ACCOUNT' 33 | app.config['JWT_ACCESS_TOKEN_EXPIRES'] = False 34 | ``` 35 | 36 | ## Usage 37 | 38 | Run the application: 39 | 40 | ```bash 41 | flask run 42 | ``` 43 | 44 | The application will be available at `http://127.0.0.1:5000/`. 45 | 46 | ## Endpoints 47 | 48 | ### Authentication 49 | 50 | - `POST /signup`: Register new users. 51 | - `POST /login`: Log in existing users. 52 | 53 | ### Users 54 | 55 | - `GET /users`: Get all users (Requires JWT authentication). 56 | - `POST /users`: Add a new user. 57 | - `GET /user/`: Get a user by ID (Requires JWT authentication). 58 | 59 | ### Posts 60 | 61 | - `GET /post`: Get all posts (Requires JWT authentication). 62 | - `POST /post`: Create a new post. 63 | 64 | ### Image Posts 65 | 66 | - `GET /imagepost/`: Get an image post by post ID (Requires JWT authentication). 67 | - `POST /imagepost`: Create a new image post. 68 | 69 | ### Messages 70 | 71 | - `GET /messages//`: Get messages between two users (Requires JWT authentication). 72 | - `POST /messages`: Create a new message (Requires JWT authentication). 73 | - `PUT /messages/`: Update a message by ID (Requires JWT authentication). 74 | - `DELETE /messages/`: Delete a message by ID (Requires JWT authentication). 75 | 76 | ### Emails 77 | 78 | - `POST /email`: Send an email (Requires JWT authentication). 79 | 80 | ### Protected Routes 81 | 82 | - `GET /protected`: Protected route that returns a welcome message with the current user's ID (Requires JWT authentication). 83 | 84 | ## Error Handling 85 | 86 | - `404 Not Found`: Returned when the requested URL is not found on the server. 87 | 88 | ## Dependencies 89 | 90 | - Flask 91 | - Flask-RESTful 92 | - Flask-JWT-Extended 93 | - Flask-CORS 94 | - smtplib 95 | - email.mime 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from src.services.userservice import UserService 2 | from flask import Flask, jsonify, request 3 | from flask_restful import Api 4 | from src.models.user import Users, User 5 | from src.models.post import Posts 6 | from src.models.message import MessageService 7 | from src.models.image_post import ImagePost 8 | from flask_jwt_extended import ( 9 | JWTManager, 10 | jwt_required, 11 | get_jwt_identity 12 | ) 13 | import smtplib 14 | from email.mime.text import MIMEText 15 | from email.mime.multipart import MIMEMultipart 16 | from flask_cors import CORS 17 | 18 | app = Flask(__name__) 19 | CORS(app) 20 | 21 | app.config['JWT_SECRET_KEY'] = 'GRUWIN-ADMIN-ACCOUNT' 22 | 23 | app.config['JWT_ACCESS_TOKEN_EXPIRES'] = False 24 | 25 | jwt = JWTManager(app) 26 | 27 | api = Api(app) 28 | 29 | uservice = UserService() 30 | 31 | msg_service = MessageService() 32 | 33 | @app.route('/') 34 | def welcome(): 35 | return jsonify({'Flask restful': 'conected'}) 36 | 37 | 38 | @app.route('/signup', methods=['POST']) 39 | def signup(): 40 | return uservice.signup() 41 | 42 | 43 | @app.route('/login', methods=['POST']) 44 | def login(): 45 | return uservice.login() 46 | 47 | 48 | @app.route('/users', methods=['GET']) 49 | @jwt_required() 50 | def getUsers(): 51 | users = Users().get() 52 | return users 53 | 54 | 55 | @app.route('/users', methods=['POST']) 56 | def addUsers(): 57 | users = Users() 58 | return users.post() 59 | 60 | 61 | @app.route('/user/') 62 | @jwt_required() 63 | def getUserById(id): 64 | user = User().get(id) 65 | return jsonify(user) 66 | 67 | 68 | @app.route('/post', methods=['GET']) 69 | @jwt_required() 70 | def getPost(): 71 | posts = Posts().get() 72 | return posts 73 | 74 | 75 | @app.route('/post', methods=['POST']) 76 | def createPost(): 77 | post = Posts() 78 | return post.post() 79 | 80 | 81 | @app.route('/imagepost/', methods=['GET']) 82 | @jwt_required() 83 | def getImagePost(postid): 84 | Image = ImagePost() 85 | return Image.get(postid) 86 | 87 | 88 | @app.route('/imagepost', methods=['POST']) 89 | def createImagePost(): 90 | image_post = ImagePost() 91 | return image_post.post() 92 | 93 | 94 | @app.route('/messages//', methods=['GET']) 95 | @jwt_required() 96 | def getMessages(sender_id, recipient_id): 97 | messages = msg_service.get_messages(sender_id, recipient_id) 98 | return jsonify(messages) 99 | 100 | 101 | @app.route('/messages', methods=['POST']) 102 | @jwt_required() 103 | def createMessage(): 104 | data = request.get_json() 105 | message = msg_service.create_message(data['sender_id'], data['recipient_id'], data['content']) 106 | return jsonify(message) 107 | 108 | 109 | @app.route('/messages/', methods=['PUT']) 110 | @jwt_required() 111 | def updateMessage(id): 112 | data = request.get_json() 113 | message = msg_service.update_message(id, data) 114 | return jsonify(message) 115 | 116 | 117 | @app.route('/messages/', methods=['DELETE']) 118 | @jwt_required() 119 | def deleteMessage(id): 120 | msg_service.delete_message(id) 121 | return '', 204 122 | 123 | 124 | @app.route('/protected') 125 | @jwt_required() 126 | def protected(): 127 | current_user_id = get_jwt_identity() 128 | return jsonify({'message': f'¡Bienvenido, usuario con id {current_user_id}!'}), 200 129 | 130 | @app.route('/email', methods=['POST']) 131 | @jwt_required() 132 | def sendEmail(): 133 | try: 134 | # Obtener los datos del cuerpo del mensaje 135 | data = request.get_json() 136 | 137 | if data: 138 | name_client = data.get('name_client') 139 | email_client = data.get('email_client') 140 | phone_client = data.get('phone_client') 141 | email_address = data.get('email_address') 142 | message_subject = data.get('message_subject') 143 | message_send = data.get('message_send') 144 | 145 | sender_email = "marinalexander691@gmail.com" 146 | password = "ibdzsrjmsfkjhdbj" 147 | receiver_email = email_client 148 | 149 | message = MIMEMultipart("alternative") 150 | message["Subject"] = message_subject 151 | message["From"] = "Intermediario <{}>".format(sender_email) 152 | message["To"] = receiver_email 153 | 154 | # Crear el cuerpo del correo electrónico con los datos 155 | text = """ 156 | 157 | Ey! Te estamos enviando este correo para informarte que tienes un nuevo mensaje: 158 |
159 |
160 | Cliente:
{}

161 | Telefono:
{}

162 | Correo del cliente:
{}

163 | Mensaje del cliente:
{}

164 | 165 | """.format(name_client, phone_client, email_address, message_send) 166 | 167 | message.attach(MIMEText(text, "html")) 168 | 169 | # Iniciar sesión en el servidor SMTP y enviar el correo electrónico 170 | with smtplib.SMTP("smtp.gmail.com", 587) as server: 171 | server.starttls() 172 | server.login(sender_email, password) 173 | server.sendmail(sender_email, receiver_email, message.as_string()) 174 | 175 | return jsonify({"message": "Email sent successfully"}) 176 | else: 177 | return jsonify({"message": "No data received"}), 400 178 | 179 | except Exception as e: 180 | return jsonify({"message": "An error occurred: {}".format(str(e))}), 500 181 | 182 | 183 | @app.errorhandler(404) 184 | def not_found(error): 185 | return jsonify({'message': 'La URL solicitada no se encontró en el servidor.'}), 404 186 | 187 | 188 | if __name__ == '__main__': 189 | app.run() 190 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ajv@^6.12.3: 6 | version "6.12.6" 7 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 8 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 9 | dependencies: 10 | fast-deep-equal "^3.1.1" 11 | fast-json-stable-stringify "^2.0.0" 12 | json-schema-traverse "^0.4.1" 13 | uri-js "^4.2.2" 14 | 15 | asn1@~0.2.3: 16 | version "0.2.6" 17 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" 18 | integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== 19 | dependencies: 20 | safer-buffer "~2.1.0" 21 | 22 | assert-plus@1.0.0, assert-plus@^1.0.0: 23 | version "1.0.0" 24 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" 25 | integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== 26 | 27 | asynckit@^0.4.0: 28 | version "0.4.0" 29 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 30 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 31 | 32 | aws-sign2@~0.7.0: 33 | version "0.7.0" 34 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" 35 | integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== 36 | 37 | aws4@^1.8.0: 38 | version "1.12.0" 39 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" 40 | integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== 41 | 42 | balanced-match@^1.0.0: 43 | version "1.0.2" 44 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 45 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 46 | 47 | bcrypt-pbkdf@^1.0.0: 48 | version "1.0.2" 49 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" 50 | integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== 51 | dependencies: 52 | tweetnacl "^0.14.3" 53 | 54 | block-stream@*: 55 | version "0.0.9" 56 | resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" 57 | integrity sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ== 58 | dependencies: 59 | inherits "~2.0.0" 60 | 61 | brace-expansion@^1.1.7: 62 | version "1.1.11" 63 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 64 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 65 | dependencies: 66 | balanced-match "^1.0.0" 67 | concat-map "0.0.1" 68 | 69 | caseless@~0.12.0: 70 | version "0.12.0" 71 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 72 | integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== 73 | 74 | combined-stream@^1.0.6, combined-stream@~1.0.6: 75 | version "1.0.8" 76 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 77 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 78 | dependencies: 79 | delayed-stream "~1.0.0" 80 | 81 | concat-map@0.0.1: 82 | version "0.0.1" 83 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 84 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 85 | 86 | core-util-is@1.0.2: 87 | version "1.0.2" 88 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 89 | integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== 90 | 91 | dashdash@^1.12.0: 92 | version "1.14.1" 93 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" 94 | integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== 95 | dependencies: 96 | assert-plus "^1.0.0" 97 | 98 | delayed-stream@~1.0.0: 99 | version "1.0.0" 100 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 101 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 102 | 103 | ecc-jsbn@~0.1.1: 104 | version "0.1.2" 105 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" 106 | integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== 107 | dependencies: 108 | jsbn "~0.1.0" 109 | safer-buffer "^2.1.0" 110 | 111 | extend@~3.0.2: 112 | version "3.0.2" 113 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" 114 | integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== 115 | 116 | extsprintf@1.3.0: 117 | version "1.3.0" 118 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" 119 | integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== 120 | 121 | extsprintf@^1.2.0: 122 | version "1.4.1" 123 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" 124 | integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== 125 | 126 | fast-deep-equal@^3.1.1: 127 | version "3.1.3" 128 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 129 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 130 | 131 | fast-json-stable-stringify@^2.0.0: 132 | version "2.1.0" 133 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 134 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 135 | 136 | forever-agent@~0.6.1: 137 | version "0.6.1" 138 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" 139 | integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== 140 | 141 | form-data@~2.3.2: 142 | version "2.3.3" 143 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" 144 | integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== 145 | dependencies: 146 | asynckit "^0.4.0" 147 | combined-stream "^1.0.6" 148 | mime-types "^2.1.12" 149 | 150 | fs.realpath@^1.0.0: 151 | version "1.0.0" 152 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 153 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 154 | 155 | fstream@^1.0.12: 156 | version "1.0.12" 157 | resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" 158 | integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== 159 | dependencies: 160 | graceful-fs "^4.1.2" 161 | inherits "~2.0.0" 162 | mkdirp ">=0.5 0" 163 | rimraf "2" 164 | 165 | getpass@^0.1.1: 166 | version "0.1.7" 167 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" 168 | integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== 169 | dependencies: 170 | assert-plus "^1.0.0" 171 | 172 | glob@3.x: 173 | version "3.2.11" 174 | resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" 175 | integrity sha512-hVb0zwEZwC1FXSKRPFTeOtN7AArJcJlI6ULGLtrstaswKNlrTJqAA+1lYlSUop4vjA423xlBzqfVS3iWGlqJ+g== 176 | dependencies: 177 | inherits "2" 178 | minimatch "0.3" 179 | 180 | glob@^7.1.3: 181 | version "7.2.3" 182 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 183 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 184 | dependencies: 185 | fs.realpath "^1.0.0" 186 | inflight "^1.0.4" 187 | inherits "2" 188 | minimatch "^3.1.1" 189 | once "^1.3.0" 190 | path-is-absolute "^1.0.0" 191 | 192 | graceful-fs@^4.1.2: 193 | version "4.2.11" 194 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" 195 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 196 | 197 | har-schema@^2.0.0: 198 | version "2.0.0" 199 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" 200 | integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== 201 | 202 | har-validator@~5.1.3: 203 | version "5.1.5" 204 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" 205 | integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== 206 | dependencies: 207 | ajv "^6.12.3" 208 | har-schema "^2.0.0" 209 | 210 | http-signature@~1.2.0: 211 | version "1.2.0" 212 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" 213 | integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== 214 | dependencies: 215 | assert-plus "^1.0.0" 216 | jsprim "^1.2.2" 217 | sshpk "^1.7.0" 218 | 219 | inflight@^1.0.4: 220 | version "1.0.6" 221 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 222 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 223 | dependencies: 224 | once "^1.3.0" 225 | wrappy "1" 226 | 227 | inherits@2, inherits@~2.0.0: 228 | version "2.0.4" 229 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 230 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 231 | 232 | is-typedarray@~1.0.0: 233 | version "1.0.0" 234 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 235 | integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== 236 | 237 | isstream@~0.1.2: 238 | version "0.1.2" 239 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 240 | integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== 241 | 242 | jsbn@~0.1.0: 243 | version "0.1.1" 244 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" 245 | integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== 246 | 247 | json-schema-traverse@^0.4.1: 248 | version "0.4.1" 249 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 250 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 251 | 252 | json-schema@0.4.0: 253 | version "0.4.0" 254 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" 255 | integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== 256 | 257 | json-stringify-safe@~5.0.1: 258 | version "5.0.1" 259 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 260 | integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== 261 | 262 | jsprim@^1.2.2: 263 | version "1.4.2" 264 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" 265 | integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== 266 | dependencies: 267 | assert-plus "1.0.0" 268 | extsprintf "1.3.0" 269 | json-schema "0.4.0" 270 | verror "1.10.0" 271 | 272 | lru-cache@2: 273 | version "2.7.3" 274 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" 275 | integrity sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ== 276 | 277 | mime-db@1.52.0: 278 | version "1.52.0" 279 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 280 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 281 | 282 | mime-types@^2.1.12, mime-types@~2.1.19: 283 | version "2.1.35" 284 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 285 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 286 | dependencies: 287 | mime-db "1.52.0" 288 | 289 | minimatch@0.3: 290 | version "0.3.0" 291 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" 292 | integrity sha512-WFX1jI1AaxNTZVOHLBVazwTWKaQjoykSzCBNXB72vDTCzopQGtyP91tKdFK5cv1+qMwPyiTu1HqUriqplI8pcA== 293 | dependencies: 294 | lru-cache "2" 295 | sigmund "~1.0.0" 296 | 297 | minimatch@^3.1.1: 298 | version "3.1.2" 299 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 300 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 301 | dependencies: 302 | brace-expansion "^1.1.7" 303 | 304 | minimist@^1.2.6: 305 | version "1.2.8" 306 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" 307 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 308 | 309 | "mkdirp@>=0.5 0": 310 | version "0.5.6" 311 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" 312 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== 313 | dependencies: 314 | minimist "^1.2.6" 315 | 316 | node-xml@1.0.0: 317 | version "1.0.0" 318 | resolved "https://registry.yarnpkg.com/node-xml/-/node-xml-1.0.0.tgz#04ca2e5f5690124727a439fd8f422070d3046967" 319 | integrity sha512-t9gTdewvkfpsv9+tb8rdsvt0/yt1JneW8xCLS+4ICKkihfSXzN8UIcxZ2hnlmO0gG0fKpyfSmPAmcqSdClAQ1Q== 320 | 321 | oauth-sign@~0.9.0: 322 | version "0.9.0" 323 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" 324 | integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== 325 | 326 | once@^1.3.0: 327 | version "1.4.0" 328 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 329 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 330 | dependencies: 331 | wrappy "1" 332 | 333 | path-is-absolute@^1.0.0: 334 | version "1.0.1" 335 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 336 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 337 | 338 | performance-now@^2.1.0: 339 | version "2.1.0" 340 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" 341 | integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== 342 | 343 | psl@^1.1.28: 344 | version "1.9.0" 345 | resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" 346 | integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== 347 | 348 | punycode@^2.1.0, punycode@^2.1.1: 349 | version "2.3.0" 350 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" 351 | integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== 352 | 353 | qs@~6.5.2: 354 | version "6.5.3" 355 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" 356 | integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== 357 | 358 | request@2.x: 359 | version "2.88.2" 360 | resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" 361 | integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== 362 | dependencies: 363 | aws-sign2 "~0.7.0" 364 | aws4 "^1.8.0" 365 | caseless "~0.12.0" 366 | combined-stream "~1.0.6" 367 | extend "~3.0.2" 368 | forever-agent "~0.6.1" 369 | form-data "~2.3.2" 370 | har-validator "~5.1.3" 371 | http-signature "~1.2.0" 372 | is-typedarray "~1.0.0" 373 | isstream "~0.1.2" 374 | json-stringify-safe "~5.0.1" 375 | mime-types "~2.1.19" 376 | oauth-sign "~0.9.0" 377 | performance-now "^2.1.0" 378 | qs "~6.5.2" 379 | safe-buffer "^5.1.2" 380 | tough-cookie "~2.5.0" 381 | tunnel-agent "^0.6.0" 382 | uuid "^3.3.2" 383 | 384 | rimraf@2, rimraf@2.x: 385 | version "2.7.1" 386 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 387 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 388 | dependencies: 389 | glob "^7.1.3" 390 | 391 | safe-buffer@^5.0.1, safe-buffer@^5.1.2: 392 | version "5.2.1" 393 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 394 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 395 | 396 | safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: 397 | version "2.1.2" 398 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 399 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 400 | 401 | semver@5.x: 402 | version "5.7.1" 403 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 404 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 405 | 406 | sigmund@~1.0.0: 407 | version "1.0.1" 408 | resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" 409 | integrity sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g== 410 | 411 | sshpk@^1.7.0: 412 | version "1.17.0" 413 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" 414 | integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== 415 | dependencies: 416 | asn1 "~0.2.3" 417 | assert-plus "^1.0.0" 418 | bcrypt-pbkdf "^1.0.0" 419 | dashdash "^1.12.0" 420 | ecc-jsbn "~0.1.1" 421 | getpass "^0.1.1" 422 | jsbn "~0.1.0" 423 | safer-buffer "^2.0.2" 424 | tweetnacl "~0.14.0" 425 | 426 | tar@2.x: 427 | version "2.2.2" 428 | resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" 429 | integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== 430 | dependencies: 431 | block-stream "*" 432 | fstream "^1.0.12" 433 | inherits "2" 434 | 435 | tough-cookie@~2.5.0: 436 | version "2.5.0" 437 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" 438 | integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== 439 | dependencies: 440 | psl "^1.1.28" 441 | punycode "^2.1.1" 442 | 443 | tunnel-agent@^0.6.0: 444 | version "0.6.0" 445 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" 446 | integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== 447 | dependencies: 448 | safe-buffer "^5.0.1" 449 | 450 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: 451 | version "0.14.5" 452 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 453 | integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== 454 | 455 | uri-js@^4.2.2: 456 | version "4.4.1" 457 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 458 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 459 | dependencies: 460 | punycode "^2.1.0" 461 | 462 | uuid@^3.3.2: 463 | version "3.4.0" 464 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" 465 | integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== 466 | 467 | verror@1.10.0: 468 | version "1.10.0" 469 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" 470 | integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== 471 | dependencies: 472 | assert-plus "^1.0.0" 473 | core-util-is "1.0.2" 474 | extsprintf "^1.2.0" 475 | 476 | virtualenv@^0.3.1: 477 | version "0.3.1" 478 | resolved "https://registry.yarnpkg.com/virtualenv/-/virtualenv-0.3.1.tgz#240de31527b2c2c3ea6d701c71e75d13feb192c9" 479 | integrity sha512-VrHB3isrQz2dK342MLLfveaX7OMpJpJINwvD0xOkRvbJeI3ubyrzMUYmNRWjs87OUqPd51V5phVL8J25LitvWA== 480 | dependencies: 481 | glob "3.x" 482 | request "2.x" 483 | rimraf "2.x" 484 | semver "5.x" 485 | tar "2.x" 486 | xmlrpc "0.8.x" 487 | 488 | wrappy@1: 489 | version "1.0.2" 490 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 491 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 492 | 493 | xmlbuilder@0.1.2: 494 | version "0.1.2" 495 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.1.2.tgz#7b7ed43dca548284d4d138169b502adc56acbec4" 496 | integrity sha512-9eGK5W3MQeFeO6Ku9WH0xPaxp7HRb1ctNPe0NIiM4Hf21nRJ2reSo4Bqe4pNk9nQyhxHGcwQuSMYbwxdFgcqZA== 497 | 498 | xmlrpc@0.8.x: 499 | version "0.8.1" 500 | resolved "https://registry.yarnpkg.com/xmlrpc/-/xmlrpc-0.8.1.tgz#05c63dba749857d5cae54ea232d5b09f3a946fd8" 501 | integrity sha512-6dYRfeluMVS2EDbAcMpz7dlxgqUeaL1KDjhjyADvGnpsIYUn83sei2HCW9PHaq2llJI2hLwQKhasmjFzwweNKg== 502 | dependencies: 503 | node-xml "1.0.0" 504 | xmlbuilder "0.1.2" 505 | --------------------------------------------------------------------------------