Initial Commit
This commit is contained in:
commit
b20d65ca96
BIN
__pycache__/api.cpython-38.pyc
Normal file
BIN
__pycache__/api.cpython-38.pyc
Normal file
Binary file not shown.
BIN
__pycache__/frontend.cpython-38.pyc
Normal file
BIN
__pycache__/frontend.cpython-38.pyc
Normal file
Binary file not shown.
469
api.py
Normal file
469
api.py
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
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)
|
||||||
151
frontend.py
Normal file
151
frontend.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
from flask import Flask, render_template, request, redirect, url_for, make_response, abort, jsonify
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = 'your_secret_key_here'
|
||||||
|
|
||||||
|
# API URL
|
||||||
|
API_URL = 'http://localhost:5023'
|
||||||
|
|
||||||
|
# Routes
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/modal')
|
||||||
|
def modal():
|
||||||
|
id = request.args.get('id')
|
||||||
|
if id == "scratch":
|
||||||
|
return render_template('scratch_modal.html')
|
||||||
|
|
||||||
|
@app.route('/login/user', methods=['GET', 'POST'])
|
||||||
|
def login_user():
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.form
|
||||||
|
response = requests.post(f'{API_URL}/api/user/login', json={'email': data['email'], 'password': data['password']})
|
||||||
|
if response.status_code == 200:
|
||||||
|
session_token = response.json()['session_token']
|
||||||
|
resp = make_response("Login Successful!")
|
||||||
|
resp.set_cookie('session_token', session_token, secure=True, httponly=True)
|
||||||
|
return resp
|
||||||
|
else:
|
||||||
|
abort(400)
|
||||||
|
return render_template('login_user.html')
|
||||||
|
|
||||||
|
@app.route('/login/developer', methods=['GET', 'POST'])
|
||||||
|
def login_developer():
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.form
|
||||||
|
response = requests.post(f'{API_URL}/api/developer/login', json={'email': data['email'], 'password': data['password']})
|
||||||
|
if response.status_code == 200:
|
||||||
|
session_token = response.json()['session_token']
|
||||||
|
resp = make_response("Login Successful!")
|
||||||
|
resp.set_cookie('devsession_token', session_token, secure=True, httponly=True)
|
||||||
|
return resp
|
||||||
|
else:
|
||||||
|
return render_template('login_developer.html', error='Invalid email or password')
|
||||||
|
return render_template('login_developer.html')
|
||||||
|
|
||||||
|
@app.route('/register/user', methods=['GET', 'POST'])
|
||||||
|
def register_user():
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.form
|
||||||
|
response = requests.post(f'{API_URL}/api/user/register', json={'username': data['username'], 'email': data['email'], 'password': data['password'], 'game_password': data['game_password']})
|
||||||
|
if response.status_code == 201:
|
||||||
|
return render_template('user_registered.html', message=response.json()['message'])
|
||||||
|
else:
|
||||||
|
return render_template('register_user.html', error='Failed to register user')
|
||||||
|
return render_template('register_user.html')
|
||||||
|
|
||||||
|
@app.route('/register/developer', methods=['GET', 'POST'])
|
||||||
|
def register_developer():
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.form
|
||||||
|
response = requests.post(f'{API_URL}/api/developer/register', json={'email': data['email'], 'password': data['password']})
|
||||||
|
if response.status_code == 201:
|
||||||
|
return render_template('developer_registered.html', message=response.json()['message'])
|
||||||
|
else:
|
||||||
|
return render_template('register_developer.html', error='Failed to register developer')
|
||||||
|
return render_template('register_developer.html')
|
||||||
|
|
||||||
|
@app.route('/user/dashboard')
|
||||||
|
def user_dashboard():
|
||||||
|
session_token = request.cookies.get('session_token')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_user'))
|
||||||
|
|
||||||
|
response = requests.post(f'{API_URL}/api/user/stats', json={'token': f'{session_token}'})
|
||||||
|
if response.status_code == 200:
|
||||||
|
user_stats = response.json()
|
||||||
|
games_played_count = len(user_stats['games_played'])
|
||||||
|
return render_template('user_dashboard.html', user_stats=user_stats, games_played_count=games_played_count)
|
||||||
|
else:
|
||||||
|
return redirect(url_for('login_user'))
|
||||||
|
|
||||||
|
@app.route('/developer/dashboard')
|
||||||
|
def developer_dashboard():
|
||||||
|
session_token = request.cookies.get('devsession_token')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_developer'))
|
||||||
|
|
||||||
|
count = requests.post(f'{API_URL}/api/developerstats/games', json={'token': session_token})
|
||||||
|
if count.status_code == 200:
|
||||||
|
return render_template('developer_dashboard.html', session_token=session_token, count=count.json()['len'])
|
||||||
|
else:
|
||||||
|
return redirect('login_developer')
|
||||||
|
|
||||||
|
@app.route('/developer/dashboard/games')
|
||||||
|
def developer_dashboard_games():
|
||||||
|
session_token = request.cookies.get('devsession_token')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_developer'))
|
||||||
|
count = requests.post(f'{API_URL}/api/developerstats/games', json={'token': session_token})
|
||||||
|
if count.status_code == 200:
|
||||||
|
return render_template('developer_dashboard_game.html', session_token=session_token, count=count.json()['len'], games=count.json()['games'])
|
||||||
|
else:
|
||||||
|
return redirect('login_developer')
|
||||||
|
|
||||||
|
@app.route('/developer/register/game', methods=['GET', 'POST'])
|
||||||
|
def register_game():
|
||||||
|
session_token = request.cookies.get('devsession_token')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_developer'))
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.form
|
||||||
|
response = requests.post(f'{API_URL}/api/game/register', json={'session_token': session_token, 'name': data['name']})
|
||||||
|
if response.status_code == 201:
|
||||||
|
return "OK"
|
||||||
|
else:
|
||||||
|
return response.json()['error'], 400
|
||||||
|
return render_template('register_game.html')
|
||||||
|
|
||||||
|
@app.route('/developer/delete/game', methods=['GET'])
|
||||||
|
def delete_game():
|
||||||
|
gameugi = request.args.get('ugi')
|
||||||
|
gamename = request.args.get('name')
|
||||||
|
session_token = request.cookies.get('devsession_token')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_developer'))
|
||||||
|
if not gameugi or not gamename:
|
||||||
|
return jsonify({"error": "The ugi or the name parameter was not found"})
|
||||||
|
return render_template('developer_dashboard_delete_game.html', name=gamename, ugi=gameugi)
|
||||||
|
|
||||||
|
@app.route('/developer/dashboard/games/delete/confirmed', methods=['POST'])
|
||||||
|
def delete_actually_do_it():
|
||||||
|
session_token = request.cookies.get('devsession_token')
|
||||||
|
ugi = request.args.get('ugi')
|
||||||
|
if not session_token:
|
||||||
|
return redirect(url_for('login_developer'))
|
||||||
|
if not ugi:
|
||||||
|
return jsonify({"error": "Ugi parameter must be provided."})
|
||||||
|
resp = requests.post(f'{API_URL}/api/game/delete', json={'session_token': session_token, 'ugi': ugi})
|
||||||
|
if resp.status_code == 200:
|
||||||
|
return "OK"
|
||||||
|
|
||||||
|
# Other routes...
|
||||||
|
|
||||||
|
#if __name__ == '__main__':
|
||||||
|
#app.run(debug=True, port=5001)
|
||||||
BIN
instance/game_save.db
Normal file
BIN
instance/game_save.db
Normal file
Binary file not shown.
16
main.py
Normal file
16
main.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import threading
|
||||||
|
from api import app as api_app
|
||||||
|
from frontend import app as frontend_app
|
||||||
|
|
||||||
|
def run_api():
|
||||||
|
api_app.run(debug=False, port=5023)
|
||||||
|
|
||||||
|
def run_frontend():
|
||||||
|
frontend_app.run(debug=False, port=5024)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
api_thread = threading.Thread(target=run_api)
|
||||||
|
frontend_thread = threading.Thread(target=run_frontend)
|
||||||
|
|
||||||
|
api_thread.start()
|
||||||
|
frontend_thread.start()
|
||||||
BIN
static/.DS_Store
vendored
Normal file
BIN
static/.DS_Store
vendored
Normal file
Binary file not shown.
24
static/js/color-modes.js
Normal file
24
static/js/color-modes.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
* Color mode toggler with OS change detection
|
||||||
|
*/
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Function to update theme based on localStorage or system preference
|
||||||
|
const updateTheme = () => {
|
||||||
|
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize theme on load
|
||||||
|
updateTheme();
|
||||||
|
|
||||||
|
// Listen for OS color scheme changes
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||||
|
updateTheme();
|
||||||
|
});
|
||||||
|
})();
|
||||||
33
static/js/modals.js
Normal file
33
static/js/modals.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Performs basic AJAX functionality to fetch and render a modal.
|
||||||
|
|
||||||
|
function showLoadingOverlay() {
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLoadingOverlay() {
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
$(`#dynamicModal`)[0].classList.add('modal-closing');
|
||||||
|
setTimeout(() => {
|
||||||
|
$("#modalContainer").empty();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openModal(id) {
|
||||||
|
showLoadingOverlay(); // Show loading overlay before fetching content
|
||||||
|
$("#modalContainer").load(`/modal?id=${id}`, function(responseTxt, statusTxt, xhr){
|
||||||
|
hideLoadingOverlay(); // Hide loading overlay after content is fetched
|
||||||
|
if (statusTxt == "error") {
|
||||||
|
console.log("Error loading modal: " + xhr.status + ": " + xhr.statusText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a modal when a button is clicked
|
||||||
|
document.querySelectorAll(".open-modal-button").forEach(button => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
openModal(button.dataset.itemId); // Use the item ID from the button instead of the target, as it tends to be undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
42
static/js/navbar.js
Normal file
42
static/js/navbar.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||||
|
const mobileMenu = document.getElementById('mobile-menu');
|
||||||
|
|
||||||
|
mobileMenuButton.addEventListener('click', function () {
|
||||||
|
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
||||||
|
|
||||||
|
this.setAttribute('aria-expanded', !isExpanded);
|
||||||
|
mobileMenu.classList.toggle('hidden');
|
||||||
|
|
||||||
|
// Toggle icons
|
||||||
|
const icons = this.getElementsByTagName('svg');
|
||||||
|
icons[0].classList.toggle('hidden');
|
||||||
|
icons[1].classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
const desktopDropdownButton = document.getElementById('desktop-dropdown-button');
|
||||||
|
const desktopDropdownMenu = document.getElementById('desktop-dropdown-menu');
|
||||||
|
|
||||||
|
desktopDropdownButton.addEventListener('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
desktopDropdownMenu.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
const mobileDropdownButton = document.getElementById('mobile-dropdown-button');
|
||||||
|
const mobileDropdownMenu = document.getElementById('mobile-dropdown-menu');
|
||||||
|
|
||||||
|
mobileDropdownButton.addEventListener('click', function () {
|
||||||
|
mobileDropdownMenu.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
if (!desktopDropdownButton.contains(e.target)) {
|
||||||
|
desktopDropdownMenu.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
desktopDropdownMenu.classList.add('hidden');
|
||||||
|
mobileDropdownMenu.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
237
templates/developer_dashboard.html
Normal file
237
templates/developer_dashboard.html
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="h-full bg-gray-100 dark:bg-gray-900">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body class="h-full">
|
||||||
|
<div class="flex mx-auto w-full h-full">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside
|
||||||
|
class="w-1/6 bg-white text-black dark:text-white dark:bg-gray-800 p-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">Developer Dashboard</h2>
|
||||||
|
<nav>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li
|
||||||
|
class="block bg-red-400 text-white font-bold px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-layout-dashboard"
|
||||||
|
>
|
||||||
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
||||||
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
||||||
|
</svg>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard/games">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-scroll"
|
||||||
|
>
|
||||||
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
||||||
|
<path
|
||||||
|
d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Games
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-users-round"
|
||||||
|
>
|
||||||
|
<path d="M18 21a8 8 0 0 0-16 0" />
|
||||||
|
<circle cx="10" cy="8" r="5" />
|
||||||
|
<path
|
||||||
|
d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Accounts
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-hard-drive"
|
||||||
|
>
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12" />
|
||||||
|
<path
|
||||||
|
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
||||||
|
/>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16" />
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-cog"
|
||||||
|
>
|
||||||
|
<path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
|
||||||
|
<path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 22v-2" />
|
||||||
|
<path d="m17 20.66-1-1.73" />
|
||||||
|
<path d="M11 10.27 7 3.34" />
|
||||||
|
<path d="m20.66 17-1.73-1" />
|
||||||
|
<path d="m3.34 7 1.73 1" />
|
||||||
|
<path d="M14 12h8" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="m20.66 7-1.73 1" />
|
||||||
|
<path d="m3.34 17 1.73-1" />
|
||||||
|
<path d="m17 3.34-1 1.73" />
|
||||||
|
<path d="m11 13.73-4 6.93" />
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-6 dark:text-white">
|
||||||
|
<!-- Dashboard Header -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-semibold text-gray-800 dark:text-white">
|
||||||
|
Overview
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Cards -->
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">
|
||||||
|
Total Games Created
|
||||||
|
</h3>
|
||||||
|
<p class="text-2xl font-bold text-red-400">
|
||||||
|
{{ count }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h2 class="text-xl font-semibold text-gray-700 dark:text-white mb-4">
|
||||||
|
Logs
|
||||||
|
</h2>
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b">
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Timestamp
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Class
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">nil</td>
|
||||||
|
<td class="px-4 py-2">Game</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
Game ID 123 has been created!
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-green-600">Transaction Successful</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">nil</td>
|
||||||
|
<td class="px-4 py-2">game</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
Game ID 123 have been deleted!
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-green-600">Transaction Successful</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
267
templates/developer_dashboard_delete_game.html
Normal file
267
templates/developer_dashboard_delete_game.html
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="h-full bg-gray-100 dark:bg-gray-900">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
<body class="h-full">
|
||||||
|
<div class="flex mx-auto w-full h-full">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside
|
||||||
|
class="w-1/6 bg-white text-black dark:text-white dark:bg-gray-800 p-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">Developer Dashboard</h2>
|
||||||
|
<nav>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-layout-dashboard"
|
||||||
|
>
|
||||||
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
||||||
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
||||||
|
</svg>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard/games">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-scroll"
|
||||||
|
>
|
||||||
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
||||||
|
<path
|
||||||
|
d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Games
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-users-round"
|
||||||
|
>
|
||||||
|
<path d="M18 21a8 8 0 0 0-16 0" />
|
||||||
|
<circle cx="10" cy="8" r="5" />
|
||||||
|
<path
|
||||||
|
d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Accounts
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-hard-drive"
|
||||||
|
>
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12" />
|
||||||
|
<path
|
||||||
|
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
||||||
|
/>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16" />
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-cog"
|
||||||
|
>
|
||||||
|
<path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
|
||||||
|
<path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 22v-2" />
|
||||||
|
<path d="m17 20.66-1-1.73" />
|
||||||
|
<path d="M11 10.27 7 3.34" />
|
||||||
|
<path d="m20.66 17-1.73-1" />
|
||||||
|
<path d="m3.34 7 1.73 1" />
|
||||||
|
<path d="M14 12h8" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="m20.66 7-1.73 1" />
|
||||||
|
<path d="m3.34 17 1.73-1" />
|
||||||
|
<path d="m17 3.34-1 1.73" />
|
||||||
|
<path d="m11 13.73-4 6.93" />
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-6 dark:text-white">
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">You are attempting to delete the game {{ name }} with the UGI {{ ugi }}</h3>
|
||||||
|
<h4>Please confirm your action below.</h4>
|
||||||
|
<p>Take a moment to observe the consequences and then confirm below if you really want to do this.</p>
|
||||||
|
<p>Please note that deleting this game is an <strong>inreversable</strong> action.</p>
|
||||||
|
<p>All game saves from all users will be deleted.</p>
|
||||||
|
<br>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Continue (ACTION INREVERSABLE)
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/developer/dashboard/games" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/developer/dashboard/games/delete/confirmed?ugi={{ ugi }}", {
|
||||||
|
method: "POST",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/developer/dashboard/games");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/developer/dashboard/games");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
238
templates/developer_dashboard_game.html
Normal file
238
templates/developer_dashboard_game.html
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="h-full bg-gray-100 dark:bg-gray-900">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body class="h-full">
|
||||||
|
<div class="flex mx-auto w-full h-full">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside
|
||||||
|
class="w-1/6 bg-white text-black dark:text-white dark:bg-gray-800 p-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">Developer Dashboard</h2>
|
||||||
|
<nav>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-layout-dashboard"
|
||||||
|
>
|
||||||
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
||||||
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
||||||
|
</svg>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block bg-red-400 text-white font-bold px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-scroll"
|
||||||
|
>
|
||||||
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
||||||
|
<path
|
||||||
|
d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Games
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-users-round"
|
||||||
|
>
|
||||||
|
<path d="M18 21a8 8 0 0 0-16 0" />
|
||||||
|
<circle cx="10" cy="8" r="5" />
|
||||||
|
<path
|
||||||
|
d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Accounts
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-hard-drive"
|
||||||
|
>
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12" />
|
||||||
|
<path
|
||||||
|
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
||||||
|
/>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16" />
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-cog"
|
||||||
|
>
|
||||||
|
<path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
|
||||||
|
<path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 22v-2" />
|
||||||
|
<path d="m17 20.66-1-1.73" />
|
||||||
|
<path d="M11 10.27 7 3.34" />
|
||||||
|
<path d="m20.66 17-1.73-1" />
|
||||||
|
<path d="m3.34 7 1.73 1" />
|
||||||
|
<path d="M14 12h8" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="m20.66 7-1.73 1" />
|
||||||
|
<path d="m3.34 17 1.73-1" />
|
||||||
|
<path d="m17 3.34-1 1.73" />
|
||||||
|
<path d="m11 13.73-4 6.93" />
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-6 dark:text-white">
|
||||||
|
<!-- Dashboard Header -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-semibold text-gray-800 dark:text-white">
|
||||||
|
Games
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Cards -->
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">
|
||||||
|
Total Games Created
|
||||||
|
</h3>
|
||||||
|
<p class="text-2xl font-bold text-red-400">
|
||||||
|
{{ count }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<a href="/developer/register/game" type="button" class="w-50 px-6 py-3 bg-gray-100 dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
Create a Game
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h2 class="text-xl font-semibold text-gray-700 dark:text-white mb-4">
|
||||||
|
Your Games
|
||||||
|
</h2>
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b">
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
UGI
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for game in games %}
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">{{ game.name }}</td>
|
||||||
|
<td class="px-4 py-2">{{ game.ugi }}</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
<a href="/developer/delete/game?name={{ game.name }}&ugi={{ game.ugi }}">
|
||||||
|
<button type="submit" class="flex space-x-2 items-center px-3 py-2 bg-rose-500 hover:bg-rose-800 rounded-md drop-shadow-md cursor-pointer duration-300">
|
||||||
|
<svg class="fill-white" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="20" height="20" viewBox="0 0 24 24">
|
||||||
|
<path d="M 10 2 L 9 3 L 3 3 L 3 5 L 21 5 L 21 3 L 15 3 L 14 2 L 10 2 z M 4.3652344 7 L 5.8925781 20.263672 C 6.0245781 21.253672 6.877 22 7.875 22 L 16.123047 22 C 17.121047 22 17.974422 21.254859 18.107422 20.255859 L 19.634766 7 L 4.3652344 7 z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="text-white">Delete</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
0
templates/developer_registered.html
Normal file
0
templates/developer_registered.html
Normal file
0
templates/game_registered.html
Normal file
0
templates/game_registered.html
Normal file
366
templates/index.html
Normal file
366
templates/index.html
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Home Page</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/static/img/favicons/favicon.ico">
|
||||||
|
<link rel="manifest" href="/assets/static/site.webmanifest"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100..900" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Lexend Deca', 'sans-serif'],
|
||||||
|
serif: ['Lexend Deca', 'serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<h1>You need to enable JavaScript to run this webapp.
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<nav
|
||||||
|
class="bg-white dark:bg-gray-800 border-b sticky top-0 z-40 backdrop-blur-lg bg-opacity-80 dark:bg-opacity-80">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex justify-between h-16">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="/" class="flex items-center text-xl font-bold text-gray-800 dark:text-white hover:text-red-400 dark:hover:text-red-400 transition-colors">
|
||||||
|
<span class="flex items-center justify-center pb-4">
|
||||||
|
<svg class="w-12 h-auto mt-4" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex items-center lg:hidden">
|
||||||
|
<button type="button"
|
||||||
|
class="inline-flex items-center justify-center p-2 rounded-lg text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline-none focus:ring-2 focus:ring-red-500"
|
||||||
|
aria-controls="navbarMenu1" aria-expanded="false" id="mobile-menu-button">
|
||||||
|
<span class="sr-only">Open main menu</span>
|
||||||
|
|
||||||
|
<svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="hidden lg:flex lg:items-center lg:space-x-6">
|
||||||
|
<a href="/"
|
||||||
|
class="text-red-600 dark:text-red-400 px-3 py-2 text-sm font-medium rounded-lg bg-red-50 dark:bg-red-900/30">
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<button type="button" id="desktop-dropdown-button"
|
||||||
|
class="flex items-center text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white px-3 py-2 text-sm font-medium rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||||
|
|
||||||
|
|
||||||
|
Get started
|
||||||
|
|
||||||
|
|
||||||
|
<svg class="ml-2 h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="desktop-dropdown-menu"
|
||||||
|
class="hidden absolute right-0 mt-2 w-48 rounded-xl bg-white dark:bg-gray-800 py-2 shadow-lg ring-1 ring-gray-200 dark:ring-gray-700 focus:outline-none">
|
||||||
|
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/register/user?redirect="
|
||||||
|
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Create an User Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/login/user?redirect="
|
||||||
|
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Login to User Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/register/developer?redirect="
|
||||||
|
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Create an Developer Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/login/developer?redirect=/"
|
||||||
|
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Login to Developer Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="lg:hidden hidden" id="mobile-menu">
|
||||||
|
<div class="px-2 pt-2 pb-3 space-y-1">
|
||||||
|
<a href="/"
|
||||||
|
class="block px-3 py-2 rounded-lg text-base font-medium text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/30">
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
<button type="button" id="mobile-dropdown-button"
|
||||||
|
class="flex items-center justify-between w-full px-3 py-2 rounded-lg text-base font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Get started
|
||||||
|
|
||||||
|
<svg class="ml-2 h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="hidden pl-4" id="mobile-dropdown-menu">
|
||||||
|
<a href="#"
|
||||||
|
class="block px-3 py-2 rounded-lg text-base font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Create an User Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#"
|
||||||
|
class="block px-3 py-2 rounded-lg text-base font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
|
||||||
|
Login to User Account
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/static/js/navbar.js" onload></script>
|
||||||
|
|
||||||
|
|
||||||
|
<main id="main" class="flex-grow bg-gray-100 dark:bg-gray-900">
|
||||||
|
<div class="container mx-auto px-4 py-4 text-center items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container border py-8 px-4 rounded-xl text-center justify-center mb-4 bg-white dark:bg-gray-900">
|
||||||
|
<span class="flex items-center justify-center pb-4 text-black dark:text-white">
|
||||||
|
<svg width="177" height="123" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h1 class="text-5xl font-bold mt-2 mb-2 dark:text-white">Welcome to GDSS (Game Data Successfully Saved)</h1>
|
||||||
|
<p class="text-lg dark:text-white mb-2">A game data management api. Alternative to Cloudlink Omega</p>
|
||||||
|
<p class="text-xl dark:text-white mb-2"><b>Thanks to Cloudlink Omega for the template</b></p>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<a href="/login/user" type="button" class="w-50 px-6 py-3 bg-gray-100 dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
Login as User
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="border py-8 rounded-xl mb-4 bg-white dark:bg-gray-900">
|
||||||
|
<div class="container text-center justify-center mb-8">
|
||||||
|
<h2 class="text-3xl font-bold dark:text-white mb-4">Methods</h2>
|
||||||
|
<p class="text-lg dark:text-white">Here are some of the methods to use GDSS!</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-wrap flex-column gap-3 justify-center px-4">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] xl:w-[calc(25%-18px)]">
|
||||||
|
<div class="relative group">
|
||||||
|
<div
|
||||||
|
class="relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl overflow-hidden transition-all duration-300 hover:shadow-2xl hover:shadow-black/20">
|
||||||
|
|
||||||
|
<div class="relative h-56 overflow-hidden">
|
||||||
|
|
||||||
|
|
||||||
|
<img src="https://scratch.mit.edu/favicon.ico"
|
||||||
|
class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" alt="..."
|
||||||
|
loading="lazy">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="pt-6 pl-6 pr-6 pb-3">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button type="button" class="open-modal-button w-full px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200" data-item-id="scratch">
|
||||||
|
<span class="flex items-center justify-center gap-2">
|
||||||
|
Launch
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="px-6 py-4 flex items-center">
|
||||||
|
<div class="grid grid-cols-5">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/80">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<small class="text-gray-600 dark:text-gray-400">
|
||||||
|
|
||||||
|
Launching April 1st, 2025
|
||||||
|
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium
|
||||||
|
bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300">
|
||||||
|
<span class="w-2 h-2 rounded-full bg-red-600 dark:bg-red-400"></span>
|
||||||
|
New
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/static/js/modals.js"></script>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="modalContainer" class="relative z-50"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-white dark:bg-gray-800 shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
GDSS
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
An open source WebRTC-powered multiplayer network.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Copyright © 2024 Mike J. Renaker. All rights reserved.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<a class="underline" href="/terms">Terms of Service & Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/static/js/color-modes.js"></script>
|
||||||
|
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'91f838fde897283e',t:'MTc0MTgzNDIxNC4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"rayId":"91f838fde897283e","version":"2025.1.0","r":1,"token":"b093103948234786baccc3f4378175c2","serverTiming":{"name":{"cfExtPri":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}}}' crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
257
templates/login_developer.html
Normal file
257
templates/login_developer.html
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/static/img/favicons/favicon.ico">
|
||||||
|
<link rel="manifest" href="/assets/static/site.webmanifest"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100..900" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Lexend Deca', 'sans-serif'],
|
||||||
|
serif: ['Lexend Deca', 'serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<noscript><h1>You need to enable JavaScript to run this webapp.</noscript>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<main id="main" class="flex-grow bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="container text-center justify-center mx-auto mb-4">
|
||||||
|
<span class="flex items-center justify-center pb-4 text-black dark:text-white">
|
||||||
|
<svg width="177" height="123" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<h1 class="text-5xl font-bold mt-2 mb-2 dark:text-white">Login</h1>
|
||||||
|
<span id="red_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"></span>
|
||||||
|
<span id="info_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<form id="login">
|
||||||
|
<div id="login_prompt" class="flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your email address:</h2>
|
||||||
|
<input type="email" id="email" name="email"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your email" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your password:</h2>
|
||||||
|
<input type="password" id="password" name="password"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your password" required />
|
||||||
|
<div class="flex flex-col justify-center items-center mb-4">
|
||||||
|
<h2 class="text-black dark:text-white">If you have enabled TOTP on your account and we don't ask for it, your browser might be auto-filling it.</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="totp_prompt" class="hidden flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Please enter your TOTP code.</h2>
|
||||||
|
<input type="number" id="totp" name="totp"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-8 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="TOTP code" required />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/accounts/?redirect=" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
$(`#info_message`)[0].classList.add("hidden");
|
||||||
|
$(`#red_message`)[0].classList.add("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#email`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter your email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test($(`#email`)[0].value)) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "That doesn't look like a valid email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#password`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a password.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#totp_prompt`)[0].classList.contains("hidden") == false) {
|
||||||
|
if ($(`#totp`)[0].value.length != 6) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a valid TOTP code.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/login/developer", {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData($(`#login`)[0]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/accounts/?redirect=");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/accounts/");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "totp required":
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
$(`#login_prompt`)[0].classList.add("hidden");
|
||||||
|
$(`#totp_prompt`)[0].classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-white dark:bg-gray-800 shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
CloudLink Omega
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
An open source WebRTC-powered multiplayer network.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Copyright © 2024 Mike J. Renaker. All rights reserved.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<b>You are currently using .</b>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<a class="underline" href="/terms">Terms of Service & Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 flex items-center justify-center space-x-2">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300">Powered by</span>
|
||||||
|
<a href="https://go.dev/"
|
||||||
|
class="text-sm hover:opacity-80 transition-opacity">
|
||||||
|
<img src="/accounts/assets/static/img/golang.png" alt='A blue gopher next to a blue "Go" word' class="h-6 inline-block" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/accounts/assets/js/color-modes.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
257
templates/login_user.html
Normal file
257
templates/login_user.html
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/static/img/favicons/favicon.ico">
|
||||||
|
<link rel="manifest" href="/assets/static/site.webmanifest"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100..900" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Lexend Deca', 'sans-serif'],
|
||||||
|
serif: ['Lexend Deca', 'serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<noscript><h1>You need to enable JavaScript to run this webapp.</noscript>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<main id="main" class="flex-grow bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="container text-center justify-center mx-auto mb-4">
|
||||||
|
<span class="flex items-center justify-center pb-4 text-black dark:text-white">
|
||||||
|
<svg width="177" height="123" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<h1 class="text-5xl font-bold mt-2 mb-2 dark:text-white">Login</h1>
|
||||||
|
<span id="red_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"></span>
|
||||||
|
<span id="info_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<form id="login">
|
||||||
|
<div id="login_prompt" class="flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your email address:</h2>
|
||||||
|
<input type="email" id="email" name="email"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your email" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your password:</h2>
|
||||||
|
<input type="password" id="password" name="password"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your password" required />
|
||||||
|
<div class="flex flex-col justify-center items-center mb-4">
|
||||||
|
<h2 class="text-black dark:text-white">If you have enabled TOTP on your account and we don't ask for it, your browser might be auto-filling it.</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="totp_prompt" class="hidden flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Please enter your TOTP code.</h2>
|
||||||
|
<input type="number" id="totp" name="totp"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-8 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="TOTP code" required />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/accounts/?redirect=" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
$(`#info_message`)[0].classList.add("hidden");
|
||||||
|
$(`#red_message`)[0].classList.add("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#email`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter your email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test($(`#email`)[0].value)) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "That doesn't look like a valid email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#password`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a password.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#totp_prompt`)[0].classList.contains("hidden") == false) {
|
||||||
|
if ($(`#totp`)[0].value.length != 6) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a valid TOTP code.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/login/user", {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData($(`#login`)[0]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/accounts/?redirect=");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/accounts/");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "totp required":
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
$(`#login_prompt`)[0].classList.add("hidden");
|
||||||
|
$(`#totp_prompt`)[0].classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-white dark:bg-gray-800 shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
CloudLink Omega
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
An open source WebRTC-powered multiplayer network.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Copyright © 2024 Mike J. Renaker. All rights reserved.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<b>You are currently using .</b>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<a class="underline" href="/terms">Terms of Service & Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 flex items-center justify-center space-x-2">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300">Powered by</span>
|
||||||
|
<a href="https://go.dev/"
|
||||||
|
class="text-sm hover:opacity-80 transition-opacity">
|
||||||
|
<img src="/accounts/assets/static/img/golang.png" alt='A blue gopher next to a blue "Go" word' class="h-6 inline-block" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/accounts/assets/js/color-modes.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
257
templates/register_developer.html
Normal file
257
templates/register_developer.html
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/static/img/favicons/favicon.ico">
|
||||||
|
<link rel="manifest" href="/assets/static/site.webmanifest"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100..900" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Lexend Deca', 'sans-serif'],
|
||||||
|
serif: ['Lexend Deca', 'serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<noscript><h1>You need to enable JavaScript to run this webapp.</noscript>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<main id="main" class="flex-grow bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="container text-center justify-center mx-auto mb-4">
|
||||||
|
<span class="flex items-center justify-center pb-4 text-black dark:text-white">
|
||||||
|
<svg width="177" height="123" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<h1 class="text-5xl font-bold mt-2 mb-2 dark:text-white">Signup</h1>
|
||||||
|
<span id="red_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"></span>
|
||||||
|
<span id="info_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<form id="login">
|
||||||
|
<div id="login_prompt" class="flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your email address:</h2>
|
||||||
|
<input type="email" id="email" name="email"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your email" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your password:</h2>
|
||||||
|
<input type="password" id="password" name="password"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your password" required />
|
||||||
|
<div class="flex flex-col justify-center items-center mb-4">
|
||||||
|
<h2 class="text-black dark:text-white">If you have enabled TOTP on your account and we don't ask for it, your browser might be auto-filling it.</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="totp_prompt" class="hidden flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Please enter your TOTP code.</h2>
|
||||||
|
<input type="number" id="totp" name="totp"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-8 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="TOTP code" required />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/accounts/?redirect=" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
$(`#info_message`)[0].classList.add("hidden");
|
||||||
|
$(`#red_message`)[0].classList.add("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#email`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter your email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test($(`#email`)[0].value)) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "That doesn't look like a valid email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#password`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a password.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#totp_prompt`)[0].classList.contains("hidden") == false) {
|
||||||
|
if ($(`#totp`)[0].value.length != 6) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a valid TOTP code.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/register/developer", {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData($(`#login`)[0]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/accounts/?redirect=");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/accounts/");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "totp required":
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
$(`#login_prompt`)[0].classList.add("hidden");
|
||||||
|
$(`#totp_prompt`)[0].classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-white dark:bg-gray-800 shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
CloudLink Omega
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
An open source WebRTC-powered multiplayer network.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Copyright © 2024 Mike J. Renaker. All rights reserved.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<b>You are currently using .</b>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<a class="underline" href="/terms">Terms of Service & Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 flex items-center justify-center space-x-2">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300">Powered by</span>
|
||||||
|
<a href="https://go.dev/"
|
||||||
|
class="text-sm hover:opacity-80 transition-opacity">
|
||||||
|
<img src="/accounts/assets/static/img/golang.png" alt='A blue gopher next to a blue "Go" word' class="h-6 inline-block" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/accounts/assets/js/color-modes.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
296
templates/register_game.html
Normal file
296
templates/register_game.html
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="h-full bg-gray-100 dark:bg-gray-900">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
<body class="h-full">
|
||||||
|
<div class="flex mx-auto w-full h-full">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside
|
||||||
|
class="w-1/6 bg-white text-black dark:text-white dark:bg-gray-800 p-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">Developer Dashboard</h2>
|
||||||
|
<nav>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-layout-dashboard"
|
||||||
|
>
|
||||||
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
||||||
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
||||||
|
</svg>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="/developer/dashboard/games">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-scroll"
|
||||||
|
>
|
||||||
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
||||||
|
<path
|
||||||
|
d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Games
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-users-round"
|
||||||
|
>
|
||||||
|
<path d="M18 21a8 8 0 0 0-16 0" />
|
||||||
|
<circle cx="10" cy="8" r="5" />
|
||||||
|
<path
|
||||||
|
d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Accounts
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-hard-drive"
|
||||||
|
>
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12" />
|
||||||
|
<path
|
||||||
|
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
||||||
|
/>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16" />
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-cog"
|
||||||
|
>
|
||||||
|
<path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
|
||||||
|
<path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 22v-2" />
|
||||||
|
<path d="m17 20.66-1-1.73" />
|
||||||
|
<path d="M11 10.27 7 3.34" />
|
||||||
|
<path d="m20.66 17-1.73-1" />
|
||||||
|
<path d="m3.34 7 1.73 1" />
|
||||||
|
<path d="M14 12h8" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="m20.66 7-1.73 1" />
|
||||||
|
<path d="m3.34 17 1.73-1" />
|
||||||
|
<path d="m17 3.34-1 1.73" />
|
||||||
|
<path d="m11 13.73-4 6.93" />
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-6 dark:text-white">
|
||||||
|
<!-- Dashboard Header -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-semibold text-gray-800 dark:text-white">
|
||||||
|
Create a Game
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Cards -->
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">
|
||||||
|
Total Games Created
|
||||||
|
</h3>
|
||||||
|
<p class="text-2xl font-bold text-red-400">
|
||||||
|
{{ count }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<span id="red_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"></span>
|
||||||
|
<form id="gamedetails">
|
||||||
|
<div id="login_prompt" class="flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter the Game's Name:</h2>
|
||||||
|
<input type="text" id="name" name="name"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter the game's name" required />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Setup!
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/developer/dashboard/games" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$(`#red_message`)[0].classList.add("hidden");
|
||||||
|
|
||||||
|
if ($(`#name`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a name for the game.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/developer/register/game", {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData($(`#gamedetails`)[0]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/developer/dashboard/games");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/developer/dashboard/games");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
266
templates/register_user.html
Normal file
266
templates/register_user.html
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/static/img/favicons/favicon.ico">
|
||||||
|
<link rel="manifest" href="/assets/static/site.webmanifest"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@100..900" rel="stylesheet">
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Lexend Deca', 'sans-serif'],
|
||||||
|
serif: ['Lexend Deca', 'serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<noscript><h1>You need to enable JavaScript to run this webapp.</noscript>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<main id="main" class="flex-grow bg-gray-100 dark:bg-gray-900">
|
||||||
|
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="container text-center justify-center mx-auto mb-4">
|
||||||
|
<span class="flex items-center justify-center pb-4 text-black dark:text-white">
|
||||||
|
<svg width="177" height="123" viewBox="0 0 177 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_149_138)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.4632 48.5129H42.0789C24.3611 48.5129 10 62.875 10 80.5918C10 98.3086 24.3611 112.671 42.0789 112.671H134.32C152.037 112.671 166.399 98.3086 166.399 80.5918C166.399 62.875 152.037 48.5129 134.32 48.5129H120.935L120.166 39.3498C118.786 22.9153 104.995 10 88.1993 10C71.4038 10 57.6129 22.9153 56.2327 39.3498L55.4632 48.5129ZM130.131 38.5129C128.319 16.9423 110.237 0 88.1993 0C66.1613 0 48.0793 16.9423 46.2678 38.5129H42.0789C18.838 38.5129 0 57.3523 0 80.5918C0 103.831 18.838 122.671 42.0789 122.671H134.32C157.561 122.671 176.399 103.831 176.399 80.5918C176.399 57.3523 157.561 38.5129 134.32 38.5129H130.131Z" fill="currentColor"/>
|
||||||
|
<path d="M117.278 98.9703C117.278 101.364 115.338 103.304 112.944 103.304H97.5482C95.1549 103.304 93.2148 101.364 93.2148 98.9703V89.6158C93.2148 87.9408 94.1803 86.4159 95.6942 85.6991C101.585 82.911 105.391 76.9013 105.391 70.3889C105.391 61.0546 97.7967 53.4604 88.4622 53.4604C79.1277 53.4604 71.5337 61.0546 71.5337 70.3889C71.5337 76.9015 75.3399 82.911 81.2305 85.6991C82.7445 86.4156 83.71 87.9406 83.71 89.6158V98.9703C83.71 101.364 81.77 103.304 79.3766 103.304H63.9802C61.5869 103.304 59.6468 101.364 59.6468 98.9703C59.6468 96.577 61.5869 94.6369 63.9802 94.6369H75.0433V92.1875C71.8002 90.1894 69.0342 87.4877 66.9496 84.2607C64.2787 80.1262 62.8669 75.3296 62.8669 70.3889C62.8669 56.2755 74.3489 44.7936 88.4622 44.7936C102.576 44.7936 114.058 56.2755 114.058 70.3887C114.058 75.3294 112.646 80.1262 109.975 84.2605C107.891 87.4875 105.125 90.1894 101.882 92.1875V94.6369H112.944C115.338 94.6369 117.278 96.577 117.278 98.9703Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_149_138">
|
||||||
|
<rect width="176.399" height="122.671" fill="currentColor"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<h1 class="text-5xl font-bold mt-2 mb-2 dark:text-white">Signup</h1>
|
||||||
|
<span id="red_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"></span>
|
||||||
|
<span id="info_message" class="hidden inline-flex items-center mt-4 gap-1 px-2 py-1 rounded-full text-2xl font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<form id="login">
|
||||||
|
<div id="login_prompt" class="flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your username:</h2>
|
||||||
|
<input type="email" id="username" name="username"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your username" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your email address:</h2>
|
||||||
|
<input type="email" id="email" name="email"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your email" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your password:</h2>
|
||||||
|
<input type="password" id="password" name="password"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your password" required />
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Enter your game password:</h2>
|
||||||
|
<p>(the password given to games <strong>DO NOT USE YOUR ACCOUNT PASSWORD</strong>)</p>
|
||||||
|
<input type="game_password" id="game_password" name="game_password"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-4 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent"
|
||||||
|
placeholder="Enter your game password" required />
|
||||||
|
<div class="flex flex-col justify-center items-center mb-4">
|
||||||
|
<h2 class="text-black dark:text-white">If you have enabled TOTP on your account and we don't ask for it, your browser might be auto-filling it.</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="totp_prompt" class="hidden flex flex-col justify-center items-center">
|
||||||
|
<h2 class="text-2xl text-black dark:text-white">Please enter your TOTP code.</h2>
|
||||||
|
<input type="number" id="totp" name="totp"
|
||||||
|
class="text-2xl block text-center px-4 py-2 mt-4 mb-8 bg-white dark:bg-gray-900 text-black dark:text-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
placeholder="TOTP code" required />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="buttons" class="w-full flex flex-wrap flex-row items-center justify-center gap-3">
|
||||||
|
<button id="submit" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M31 15L35.6 19.6C35.9739 19.9665 36.4765 20.1717 37 20.1717C37.5235 20.1717 38.0261 19.9665 38.4 19.6L42.6 15.4C42.9665 15.0261 43.1717 14.5235 43.1717 14C43.1717 13.4765 42.9665 12.9739 42.6 12.6L38 8"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M41.9998 4L22.7998 23.2" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M15 42C21.0751 42 26 37.0751 26 31C26 24.9249 21.0751 20 15 20C8.92487 20 4 24.9249 4 31C4 37.0751 8.92487 42 15 42Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="/accounts/?redirect=" type="button" id="cancel" class="w-50 px-6 py-3 bg-white dark:bg-gray-600 hover:font-bold hover:bg-red-400 dark:hover:bg-red-400 text-black dark:text-white hover:text-white rounded-xl
|
||||||
|
font-medium transition-all duration-300
|
||||||
|
hover:shadow-lg hover:shadow-red-500/30 focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
||||||
|
active:scale-95">
|
||||||
|
<span class="flex items-center justify-center gap-2 text-2xl">
|
||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
|
||||||
|
stroke="currentColor" stroke-width="3" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path d="M9.7998 9.80005L38.1998 38.2001" stroke="currentColor" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
$(`#submit`)[0].addEventListener("click", async function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
$(`#info_message`)[0].classList.add("hidden");
|
||||||
|
$(`#red_message`)[0].classList.add("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#email`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter your email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test($(`#email`)[0].value)) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "That doesn't look like a valid email address.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#password`)[0].value.length == 0) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a password.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($(`#totp_prompt`)[0].classList.contains("hidden") == false) {
|
||||||
|
if ($(`#totp`)[0].value.length != 6) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = "Please enter a valid TOTP code.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(`#loadingOverlay`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
response = await fetch(
|
||||||
|
"\/register/user", {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData($(`#login`)[0]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(async() => {
|
||||||
|
message = await response.text();
|
||||||
|
switch (message) {
|
||||||
|
|
||||||
|
case "OK":
|
||||||
|
if ("" != "") {
|
||||||
|
window.location.replace("\/accounts/?redirect=");
|
||||||
|
} else {
|
||||||
|
window.location.replace("\/accounts/");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "totp required":
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
$(`#login_prompt`)[0].classList.add("hidden");
|
||||||
|
$(`#totp_prompt`)[0].classList.remove("hidden");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$(`#loadingOverlay`)[0].classList.add("hidden");
|
||||||
|
if (!response.ok) {
|
||||||
|
$(`#red_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#red_message`)[0].textContent = message;
|
||||||
|
} else {
|
||||||
|
$(`#info_message`)[0].classList.remove("hidden");
|
||||||
|
$(`#info_message`)[0].textContent = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="bg-white dark:bg-gray-800 shadow-lg">
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
CloudLink Omega
|
||||||
|
</h3>
|
||||||
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
An open source WebRTC-powered multiplayer network.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Copyright © 2024 Mike J. Renaker. All rights reserved.
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<b>You are currently using .</b>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<a class="underline" href="/terms">Terms of Service & Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 flex items-center justify-center space-x-2">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300">Powered by</span>
|
||||||
|
<a href="https://go.dev/"
|
||||||
|
class="text-sm hover:opacity-80 transition-opacity">
|
||||||
|
<img src="/accounts/assets/static/img/golang.png" alt='A blue gopher next to a blue "Go" word' class="h-6 inline-block" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black/75 flex justify-center items-center z-50">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/accounts/assets/js/color-modes.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
116
templates/scratch_modal.html
Normal file
116
templates/scratch_modal.html
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="dynamicModal"
|
||||||
|
class="fixed inset-0 z-50 hidden overflow-y-auto"
|
||||||
|
aria-labelledby="dynamicModalLabel"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="fixed inset-0 bg-black/75 backdrop-blur-sm transition-opacity"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="relative transform overflow-hidden rounded-2xl bg-white dark:bg-gray-800 text-left shadow-2xl transition-all sm:my-8 sm:w-full sm:max-w-lg
|
||||||
|
animate-[modal-open_0.3s_ease-out]"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3
|
||||||
|
class="text-xl font-semibold text-gray-900 dark:text-white"
|
||||||
|
id="dynamicModalLabel"
|
||||||
|
>
|
||||||
|
Development In Progress.
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="px-6 py-4">
|
||||||
|
<div class="text-gray-600 dark:text-gray-300">
|
||||||
|
The Scratch Extension and the backend are both in development and should not be used already for production games.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex flex-row-reverse gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button-confirm inline-flex px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
I agree
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button-dismiss inline-flex px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
I disagree
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes modal-open {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.95) translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-open {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes modal-close {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.95) translateY(10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-closing {
|
||||||
|
animation: modal-close 0.2s ease-out forwards;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript" onload>
|
||||||
|
|
||||||
|
|
||||||
|
$(`#dynamicModal`)[0].classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
$(`.button-confirm`)[0].addEventListener('click', function() {
|
||||||
|
$(`#dynamicModal`)[0].classList.add('modal-closing');
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
|
||||||
|
$("#main").empty();
|
||||||
|
showLoadingOverlay();
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
$("#main").append(`<iframe class="mx-auto min-h-screen w-full" frameborder="0" scrolling="no" src="/hosted/Countdown.html" allowfullscreen="true" allow="autoplay; clipboard-write;" referrerpolicy="same-origin"></iframe>`);
|
||||||
|
hideLoadingOverlay();
|
||||||
|
$("#modalContainer").empty();
|
||||||
|
}, 1000);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
$(`.button-dismiss`)[0].addEventListener('click', closeModal);
|
||||||
|
|
||||||
|
</script>
|
||||||
251
templates/user_dashboard.html
Normal file
251
templates/user_dashboard.html
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="h-full bg-gray-100 dark:bg-gray-900">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body class="h-full">
|
||||||
|
<div class="flex mx-auto w-full h-full">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside
|
||||||
|
class="w-1/6 bg-white text-black dark:text-white dark:bg-gray-800 p-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">User Dashboard</h2>
|
||||||
|
<nav>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li
|
||||||
|
class="block bg-red-400 text-white font-bold px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-layout-dashboard"
|
||||||
|
>
|
||||||
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
||||||
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
||||||
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
||||||
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
||||||
|
</svg>
|
||||||
|
Overview
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-scroll"
|
||||||
|
>
|
||||||
|
<path d="M19 17V5a2 2 0 0 0-2-2H4" />
|
||||||
|
<path
|
||||||
|
d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Logs
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-users-round"
|
||||||
|
>
|
||||||
|
<path d="M18 21a8 8 0 0 0-16 0" />
|
||||||
|
<circle cx="10" cy="8" r="5" />
|
||||||
|
<path
|
||||||
|
d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Accounts
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-hard-drive"
|
||||||
|
>
|
||||||
|
<line x1="22" x2="2" y1="12" y2="12" />
|
||||||
|
<path
|
||||||
|
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
||||||
|
/>
|
||||||
|
<line x1="6" x2="6.01" y1="16" y2="16" />
|
||||||
|
<line x1="10" x2="10.01" y1="16" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="block px-3 py-2 rounded-xl ring-1 ring-inset ring-gray-300 dark:ring-gray-600 bg-gray-100 dark:bg-gray-700 hover:bg-red-400 dark:hover:bg-red-400 hover:text-white hover:font-bold dark:text-white transition-all duration-200"
|
||||||
|
>
|
||||||
|
<a href="#">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-cog"
|
||||||
|
>
|
||||||
|
<path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
|
||||||
|
<path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 22v-2" />
|
||||||
|
<path d="m17 20.66-1-1.73" />
|
||||||
|
<path d="M11 10.27 7 3.34" />
|
||||||
|
<path d="m20.66 17-1.73-1" />
|
||||||
|
<path d="m3.34 7 1.73 1" />
|
||||||
|
<path d="M14 12h8" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="m20.66 7-1.73 1" />
|
||||||
|
<path d="m3.34 17 1.73-1" />
|
||||||
|
<path d="m17 3.34-1 1.73" />
|
||||||
|
<path d="m11 13.73-4 6.93" />
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-6 dark:text-white">
|
||||||
|
<!-- Dashboard Header -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-semibold text-gray-800 dark:text-white">
|
||||||
|
Overview
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Cards -->
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">
|
||||||
|
Total Games Played
|
||||||
|
</h3>
|
||||||
|
<p class="text-2xl font-bold text-red-400">
|
||||||
|
{{ games_played_count }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-white">
|
||||||
|
Total Points
|
||||||
|
</h3>
|
||||||
|
<p class="text-2xl font-bold text-red-400">
|
||||||
|
{{ user_stats.points_earned }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-gray-800 p-4 rounded shadow">
|
||||||
|
<h2 class="text-xl font-semibold text-gray-700 dark:text-white mb-4">
|
||||||
|
Logs
|
||||||
|
</h2>
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b">
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Timestamp
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Class
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="px-4 py-2 text-gray-600 dark:text-white font-semibold"
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">nil</td>
|
||||||
|
<td class="px-4 py-2">Points</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
Game ID 123 added 5 points to your account!
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-green-600">Transaction Successful</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">nil</td>
|
||||||
|
<td class="px-4 py-2">Points</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
Game ID 123 removed 5 points from your account!
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-green-600">Transaction Successful</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="border-b hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<td class="px-4 py-2">nil</td>
|
||||||
|
<td class="px-4 py-2">Game Played</td>
|
||||||
|
<td class="px-4 py-2">You've played the game 123 for 4 hours!</td>
|
||||||
|
<td class="px-4 py-2 text-yellow-600">nil</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
0
templates/user_registered.html
Normal file
0
templates/user_registered.html
Normal file
Loading…
Reference in New Issue
Block a user