GSS/api.py
kokofixcomputers b20d65ca96 Initial Commit
2025-05-22 09:14:41 -07:00

470 lines
17 KiB
Python

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
import uuid
import jwt
from datetime import datetime, timedelta
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///game_save.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'your_secret_key_here'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
# Models
class Developer(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=True)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
game_password = db.Column(db.String(120), nullable=False)
class GameSave(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
game_id = db.Column(db.String(80), nullable=False)
slot = db.Column(db.Integer, nullable=False) # Save slot (1-10)
save_data = db.Column(db.Text, nullable=False)
class Game(db.Model):
id = db.Column(db.Integer, primary_key=True)
ugi = db.Column(db.String(80), unique=True, nullable=False)
name = db.Column(db.String(100), nullable=False)
developer_id = db.Column(db.Integer, db.ForeignKey('developer.id'), nullable=False)
class Log(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
game_id = db.Column(db.String(80), nullable=False)
action = db.Column(db.String(20), nullable=False) # 'add_points' or 'remove_points'
points = db.Column(db.Integer, nullable=False)
timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
class UserStats(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
games_played = db.Column(db.String(200), nullable=False, default='')
points_earned = db.Column(db.Integer, nullable=False, default=0)
class GamePlayed(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
game_id = db.Column(db.String(200), nullable=False, default='')
class AuthToken(db.Model):
id = db.Column(db.Integer, primary_key=True)
token = db.Column(db.String(200), nullable=False)
game_id = db.Column(db.String(80), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
class Session(db.Model):
id = db.Column(db.Integer, primary_key=True)
session_token = db.Column(db.String(200), nullable=False)
user_type = db.Column(db.String(20), nullable=False) # 'user' or 'developer'
user_id = db.Column(db.Integer, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
# Routes
@app.route('/api/user/login', methods=['POST'])
def login_user():
data = request.json
user = User.query.filter_by(email=data['email']).first()
if user and bcrypt.check_password_hash(user.password, data['password']):
session_token = str(uuid.uuid4())
new_session = Session(
session_token=session_token,
user_type='user',
user_id=user.id
)
db.session.add(new_session)
db.session.commit()
return jsonify({'session_token': session_token}), 200
else:
return jsonify({'error': 'Invalid email or password'}), 401
@app.route('/api/user/register', methods=['POST'])
def register_user():
data = request.json
if 'username' not in data or 'email' not in data or 'password' not in data or 'game_password' not in data:
return jsonify({'error': 'Username, email, password, and game password are required'}), 400
existing_user = User.query.filter_by(email=data['email']).first()
if existing_user:
return jsonify({'error': 'Email already in use'}), 400
hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')
new_user = User(
username=data['username'],
email=data['email'],
password=hashed_password,
game_password=data['game_password']
)
db.session.add(new_user)
db.session.commit()
new_user_stats = UserStats(user_id=new_user.id)
db.session.add(new_user_stats)
db.session.commit()
return jsonify({'message': 'User registered successfully'}), 201
@app.route('/api/developer/login', methods=['POST'])
def login_developer():
data = request.json
developer = Developer.query.filter_by(email=data['email']).first()
if developer and bcrypt.check_password_hash(developer.password, data['password']):
session_token = str(uuid.uuid4())
new_session = Session(
session_token=session_token,
user_type='developer',
user_id=developer.id
)
db.session.add(new_session)
db.session.commit()
return jsonify({'session_token': session_token, 'id': developer.id}), 200
else:
return jsonify({'error': 'Invalid email or password'}), 401
@app.route('/api/developer/register', methods=['POST'])
def register_developer():
data = request.json
if 'email' not in data or 'password' not in data:
return jsonify({'error': 'Email and password are required'}), 400
existing_dev = Developer.query.filter_by(email=data['email']).first()
if existing_dev:
return jsonify({'error': 'Email already in use'}), 400
hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')
new_dev = Developer(
username=data.get('username'),
email=data['email'],
password=hashed_password
)
db.session.add(new_dev)
db.session.commit()
return jsonify({'message': 'Developer registered successfully'}), 201
@app.route('/api/game/register', methods=['POST'])
def register_game():
data = request.json
if 'session_token' not in data:
return jsonify({'error': 'Session token is required'}), 401
session = Session.query.filter_by(session_token=data['session_token']).first()
if not session or session.user_type != 'developer':
return jsonify({'error': 'Invalid session or not a developer'}), 401
if 'name' not in data:
return jsonify({'error': 'Game name is required'}), 400
gameexist = Game.query.filter_by(name=data['name']).first()
if gameexist:
return jsonify({'error': 'Game name already taken.'}), 400
ugi = str(uuid.uuid4())
new_game = Game(
ugi=ugi,
name=data['name'],
developer_id=session.user_id
)
db.session.add(new_game)
db.session.commit()
return jsonify({'message': 'Game registered successfully', 'ugi': ugi}), 201
@app.route('/api/game/delete', methods=['POST'])
def delete_game():
data = request.json
if 'session_token' not in data:
return jsonify({'error': 'Session token is required'}), 401
session = Session.query.filter_by(session_token=data['session_token']).first()
if not session or session.user_type != 'developer':
return jsonify({'error': 'Invalid session or not a developer'}), 401
if 'ugi' not in data:
return jsonify({'error': 'Game UGI is required'}), 400
game_to_delete = Game.query.filter_by(ugi=data['ugi'], developer_id=session.user_id).first()
if not game_to_delete:
return jsonify({'error': 'Game not found or not owned by you'}), 404
game_saves_to_delete = GameSave.query.filter_by(game_id=data['ugi']).all()
for save in game_saves_to_delete:
db.session.delete(save)
db.session.delete(game_to_delete)
db.session.commit()
return jsonify({'message': 'Game deleted successfully'}), 200
@app.route('/api/game/authenticate', methods=['POST'])
def authenticate_game():
data = request.json
if 'username' not in data or 'game_password' not in data or 'game_id' not in data:
return jsonify({'error': 'Username, game password, and game ID are required'}), 400
user = User.query.filter_by(username=data['username']).first()
if not user:
return jsonify({'error': 'User not found'}), 404
if user.game_password != data['game_password']:
return jsonify({'error': 'Incorrect game password'}), 401
payload = {
'exp': datetime.utcnow() + timedelta(minutes=30),
'iat': datetime.utcnow(),
'sub': user.id
}
token = jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256')
# data['game_id'] is the game's UGI. When storing it in db, first, find the ugi and then find the id of the game then store that id in the db
game_id = Game.query.filter_by(ugi=data['game_id']).first().id
if not game_id:
return jsonify({'error': 'Game not found'}), 404
new_auth_token = AuthToken(
token=token,
game_id=game_id,
user_id=user.id
)
db.session.add(new_auth_token)
db.session.commit()
return jsonify({'message': 'Game authenticated successfully', 'token': token}), 200
@app.route('/api/game/save-data', methods=['POST'])
def save_game_data():
data = request.json
if 'save_data' not in data or 'token' not in data or 'slot' not in data:
return jsonify({'error': 'Save data, token, and slot are required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
if not auth_token:
return jsonify({'error': 'Invalid token'}), 401
if not 1 <= int(data['slot']) <= 10:
return jsonify({'error': 'Invalid save slot'}), 400
existing_save = GameSave.query.filter_by(user_id=auth_token.user_id, game_id=auth_token.game_id, slot=int(data['slot'])).first()
if existing_save:
existing_save.save_data = data['save_data']
db.session.commit()
return jsonify({'message': 'Game save data updated successfully'}), 200
else:
new_save = GameSave(
user_id=auth_token.user_id,
game_id=auth_token.game_id,
slot=data['slot'],
save_data=data['save_data']
)
db.session.add(new_save)
db.session.commit()
return jsonify({'message': 'Game save data saved successfully'}), 201
@app.route('/api/game/read-data', methods=['POST'])
def read_game_data():
data = request.json
if 'token' not in data or 'slot' not in data:
return jsonify({'error': 'Token and slot are required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
if not auth_token:
return jsonify({'error': 'Invalid token'}), 401
if not 1 <= int(data['slot']) <= 10:
return jsonify({'error': 'Invalid save slot'}), 400
save_data = GameSave.query.filter_by(user_id=auth_token.user_id, game_id=auth_token.game_id, slot=int(data['slot'])).first()
if save_data:
return jsonify({'save_data': save_data.save_data}), 200
else:
return jsonify({'error': 'No save data found for this user and game'}), 404
@app.route('/api/user/stats', methods=['POST'])
def get_user_stats():
data = request.json
if 'token' not in data:
return jsonify({'error': 'Token is required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
auth_token2 = Session.query.filter_by(session_token=data['token']).first()
if not auth_token and not auth_token2:
return jsonify({'error': 'Invalid token'}), 401
if auth_token:
user_id = auth_token.user_id
elif auth_token2:
user_id = auth_token2.user_id
user_stats = UserStats.query.filter_by(user_id=user_id).first()
gamed = GamePlayed.query.filter_by(user_id=user_id).all()
gamesPld = []
for game in gamed:
gamesPld.append(game.game_id)
if user_stats:
return jsonify({
'games_played': gamesPld,
'points_earned': user_stats.points_earned
}), 200
else:
return jsonify({'error': 'User stats not found'}), 404
@app.route('/api/user/add-game-played', methods=['POST'])
def add_game_played():
data = request.json
if 'token' not in data:
return jsonify({'error': 'Token is required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
if not auth_token:
return jsonify({'error': 'Invalid token'}), 401
user_id = auth_token.user_id
user_stats = UserStats.query.filter_by(user_id=user_id).first()
if user_stats:
game_already_played = GamePlayed.query.filter_by(game_id=auth_token.game_id).first()
if not game_already_played:
new_game_played = GamePlayed(
user_id=user_id,
game_id=auth_token.game_id
)
db.session.add(new_game_played)
db.session.commit()
return jsonify({'message': 'Game added to played games successfully'}), 200
else:
return jsonify({'error': 'Game is already marked as played'}), 200
else:
return jsonify({'error': 'User stats not found'}), 404
@app.route('/api/user/points/add', methods=['POST'])
def add_points():
data = request.json
if 'points' not in data or 'token' not in data:
return jsonify({'error': 'Points and token are required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
auth_token2 = Session.query.filter_by(session_token=data['token']).first()
if not auth_token and not auth_token2:
return jsonify({'error': 'Invalid token'}), 401
if auth_token is not None:
user_id = auth_token.user_id
elif auth_token2:
user_id = auth_token2.user_id
user_stats = UserStats.query.filter_by(user_id=user_id).first()
if user_stats:
user_stats.points_earned += data['points']
db.session.commit()
new_log = Log(
user_id=user_id,
game_id="game_id",
action='add_points',
points=data['points']
)
db.session.add(new_log)
db.session.commit()
return jsonify({'message': 'Points added successfully'}), 200
else:
return jsonify({'error': 'User stats not found'}), 404
@app.route('/api/user/points/remove', methods=['POST'])
def remove_points():
data = request.json
if 'points' not in data or 'token' not in data:
return jsonify({'error': 'Points and token are required'}), 400
auth_token = AuthToken.query.filter_by(token=data['token']).first()
auth_token2 = Session.query.filter_by(session_token=data['token']).first()
if not auth_token and not auth_token2:
return jsonify({'error': 'Invalid token'}), 401
if auth_token:
user_id = auth_token.user_id
elif auth_token2:
user_id = auth_token2.user_id
user_stats = UserStats.query.filter_by(user_id=user_id).first()
if user_stats:
if user_stats.points_earned >= data['points']:
user_stats.points_earned -= data['points']
db.session.commit()
new_log = Log(
user_id=user_id,
game_id="game_id",
action='remove_points',
points=data['points']
)
db.session.add(new_log)
db.session.commit()
return jsonify({'message': 'Points removed successfully'}), 200
else:
return jsonify({'error': 'Not enough points to remove'}), 400
else:
return jsonify({'error': 'User stats not found'}), 404
@app.route('/api/logs', methods=['GET'])
def get_logs():
session_token = request.cookies.get('session_token')
if not session_token:
return jsonify({'error': 'Session token is required'}), 401
session = Session.query.filter_by(session_token=session_token).first()
if not session:
return jsonify({'error': 'Invalid session'}), 401
logs = Log.query.filter_by(user_id=session.user_id).all()
log_data = []
for log in logs:
log_data.append({
'id': log.id,
'user_id': log.user_id,
'game_id': log.game_id,
'action': log.action,
'points': log.points,
'timestamp': log.timestamp
})
return jsonify(log_data), 200
@app.route('/api/developerstats/games', methods=['POST'])
def games_count():
data = request.json
if 'token' not in data:
return jsonify({'error': 'token is required'}), 401
session = Session.query.filter_by(session_token=data['token']).first()
if not session:
return jsonify({'error': 'Invalid session'}), 401
if session.user_type != "developer":
return jsonify({'error': 'Invalid User type. Requested developer, got user.'})
total_games = Game.query.filter_by(developer_id=session.user_id).all()
games_data = [
{'name': game.name, 'ugi': game.ugi}
for game in total_games
]
return jsonify({"len": len(total_games), "games": games_data}), 200
# Purge old tokens
#@app.before_first_request
#def purge_old_tokens():
# old_tokens = AuthToken.query.filter(AuthToken.created_at < datetime.utcnow() - timedelta(days=10)).all()
# for token in old_tokens:
# db.session.delete(token)
# db.session.commit()
with app.app_context():
db.create_all()
#if __name__ == '__main__':
# with app.app_context():
# db.create_all()
#app.run(debug=True, host='0.0.0.0', port=5034)