Add initial project structure with Docker setup and frontend/backend files
This commit is contained in:
9
backend/Dockerfile
Executable file
9
backend/Dockerfile
Executable file
@@ -0,0 +1,9 @@
|
||||
FROM python:3.9-slim
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . .
|
||||
ENV DATABASE_URL=postgresql://username:password@db/workout_buddy
|
||||
ENV JWT_SECRET_KEY=super-secret
|
||||
EXPOSE 5000
|
||||
CMD ["python", "app.py"]
|
||||
145
backend/app.py
Executable file
145
backend/app.py
Executable file
@@ -0,0 +1,145 @@
|
||||
import os
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_restful import Api, Resource
|
||||
from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_jwt_identity
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from marshmallow import Schema, fields, validate
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from flask_cors import CORS
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# PostgreSQL database configuration
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET_KEY', 'super-secret') # Change this in production!
|
||||
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = 3600 # 1 hour
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'postgresql://workout_user:workout_password@db/workout_buddy')
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
api = Api(app)
|
||||
jwt = JWTManager(app)
|
||||
limiter = Limiter(app, key_func=get_remote_address)
|
||||
|
||||
# Set up logging
|
||||
if not app.debug:
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
file_handler = RotatingFileHandler('logs/workout_buddy.log', maxBytes=10240, backupCount=10)
|
||||
file_handler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('Workout Buddy startup')
|
||||
|
||||
class User(db.Model):
|
||||
__tablename__ = 'users'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
age = db.Column(db.Integer, nullable=False)
|
||||
location = db.Column(db.String(100), nullable=False)
|
||||
gender = db.Column(db.String(10), nullable=False)
|
||||
password_hash = db.Column(db.String(128))
|
||||
created_at = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||
updated_at = db.Column(db.DateTime(timezone=True), server_default=db.func.now(), onupdate=db.func.now())
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
class Message(db.Model):
|
||||
__tablename__ = 'messages'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
sender_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
receiver_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
content = db.Column(db.String(500), nullable=False)
|
||||
created_at = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||
|
||||
sender = db.relationship('User', foreign_keys=[sender_id], backref=db.backref('sent_messages', lazy=True))
|
||||
receiver = db.relationship('User', foreign_keys=[receiver_id], backref=db.backref('received_messages', lazy=True))
|
||||
|
||||
class UserSchema(Schema):
|
||||
name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
|
||||
email = fields.Email(required=True)
|
||||
age = fields.Int(required=True, validate=validate.Range(min=18, max=120))
|
||||
location = fields.Str(required=True, validate=validate.Length(min=1, max=100))
|
||||
password = fields.Str(required=True, validate=validate.Length(min=8))
|
||||
|
||||
class MessageSchema(Schema):
|
||||
content = fields.Str(required=True, validate=validate.Length(min=1, max=500))
|
||||
|
||||
user_schema = UserSchema()
|
||||
message_schema = MessageSchema()
|
||||
|
||||
# Define the resource classes
|
||||
class UserResource(Resource):
|
||||
def post(self):
|
||||
try:
|
||||
data = request.get_json()
|
||||
errors = user_schema.validate(data)
|
||||
if errors:
|
||||
return {'message': 'Validation error', 'errors': errors}, 400
|
||||
|
||||
existing_user = User.query.filter_by(email=data['email']).first()
|
||||
if existing_user:
|
||||
return {'message': 'User with this email already exists'}, 409
|
||||
|
||||
user = User(
|
||||
name=data['name'],
|
||||
email=data['email'],
|
||||
age=data['age'],
|
||||
location=data['location'],
|
||||
gender=data['gender']
|
||||
)
|
||||
user.set_password(data['password'])
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return user_schema.dump(user), 201
|
||||
except Exception as e:
|
||||
app.logger.error(f'Error creating user: {str(e)}')
|
||||
app.logger.info(f'Attempting to create user: {data["email"]}')
|
||||
return {'message': 'An error occurred while creating the user'}, 500
|
||||
class MessageResource(Resource):
|
||||
@jwt_required()
|
||||
def get(self):
|
||||
messages = Message.query.all()
|
||||
return message_schema.dump(messages, many=True), 200
|
||||
|
||||
@jwt_required()
|
||||
def post(self):
|
||||
data = request.get_json()
|
||||
message = Message(
|
||||
sender_id=get_jwt_identity(), # Assuming JWT identity is user ID
|
||||
receiver_id=data['receiver_id'],
|
||||
content=data['content']
|
||||
)
|
||||
db.session.add(message)
|
||||
db.session.commit()
|
||||
return message_schema.dump(message), 201
|
||||
|
||||
class AuthResource(Resource):
|
||||
def post(self):
|
||||
data = request.get_json()
|
||||
user = User.query.filter_by(email=data['email']).first()
|
||||
if user and user.check_password(data['password']):
|
||||
access_token = create_access_token(identity=user.id)
|
||||
return {'access_token': access_token}, 200
|
||||
return {'message': 'Invalid credentials'}, 401
|
||||
|
||||
# Add resource endpoints
|
||||
api.add_resource(UserResource, '/users')
|
||||
api.add_resource(MessageResource, '/messages')
|
||||
api.add_resource(AuthResource, '/auth')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
9
backend/requirements.txt
Executable file
9
backend/requirements.txt
Executable file
@@ -0,0 +1,9 @@
|
||||
Flask==2.2.5
|
||||
Flask-SQLAlchemy==3.0.4
|
||||
Flask-RESTful==0.3.9
|
||||
Flask-JWT-Extended==4.4.4
|
||||
Werkzeug==2.3.0
|
||||
marshmallow==3.19.0
|
||||
Flask-Limiter==2.7.0
|
||||
psycopg2-binary==2.9.7
|
||||
flask-cors==3.0.10
|
||||
Reference in New Issue
Block a user