320 lines
12 KiB
Python
320 lines
12 KiB
Python
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash
|
||
from flask_login import login_user, logout_user, login_required, current_user
|
||
from werkzeug.security import generate_password_hash, check_password_hash
|
||
from db import get_db, generate_clan_key
|
||
from datetime import datetime, timezone
|
||
|
||
auth = Blueprint('auth', __name__)
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# Helper — resolve the User class from app (avoid circular import)
|
||
# ------------------------------------------------------------------
|
||
def _make_user(row):
|
||
from app import User
|
||
return User(row['id'], row['username'], row['clan_id'])
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET/POST /auth/login
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/login', methods=['GET', 'POST'])
|
||
def login():
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for('dashboard.index'))
|
||
|
||
error = None
|
||
if request.method == 'POST':
|
||
username = request.form.get('username', '').strip()
|
||
password = request.form.get('password', '')
|
||
|
||
conn = get_db()
|
||
row = conn.execute(
|
||
'SELECT id, username, password_hash, clan_id FROM users WHERE username = ?', (username,)
|
||
).fetchone()
|
||
conn.close()
|
||
|
||
if row and check_password_hash(row['password_hash'], password):
|
||
user = _make_user(row)
|
||
login_user(user, remember=True)
|
||
return redirect(url_for('dashboard.index'))
|
||
else:
|
||
error = 'Λάθος όνομα χρήστη ή κωδικός.'
|
||
|
||
return render_template('login.html', error=error)
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET/POST /auth/register
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/register', methods=['GET', 'POST'])
|
||
def register():
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for('dashboard.index'))
|
||
|
||
error = None
|
||
if request.method == 'POST':
|
||
username = request.form.get('username', '').strip()
|
||
password = request.form.get('password', '')
|
||
confirm = request.form.get('confirm', '')
|
||
|
||
if not username or not password:
|
||
error = 'Συμπλήρωσε όνομα χρήστη και κωδικό.'
|
||
elif password != confirm:
|
||
error = 'Οι κωδικοί δεν ταιριάζουν.'
|
||
elif len(password) < 6:
|
||
error = 'Ο κωδικός πρέπει να έχει τουλάχιστον 6 χαρακτήρες.'
|
||
else:
|
||
conn = get_db()
|
||
existing = conn.execute(
|
||
'SELECT id FROM users WHERE username = ?', (username,)
|
||
).fetchone()
|
||
if existing:
|
||
error = 'Το όνομα χρήστη χρησιμοποιείται ήδη.'
|
||
conn.close()
|
||
else:
|
||
pw_hash = generate_password_hash(password)
|
||
conn.execute(
|
||
'INSERT INTO users (username, password_hash) VALUES (?, ?)',
|
||
(username, pw_hash)
|
||
)
|
||
conn.commit()
|
||
row = conn.execute(
|
||
'SELECT id, username, password_hash, clan_id FROM users WHERE username = ?', (username,)
|
||
).fetchone()
|
||
conn.close()
|
||
user = _make_user(row)
|
||
login_user(user, remember=True)
|
||
return redirect(url_for('auth.options'))
|
||
|
||
return render_template('register.html', error=error)
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /auth/logout
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/logout')
|
||
@login_required
|
||
def logout():
|
||
logout_user()
|
||
return redirect(url_for('auth.login'))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET/POST /auth/options — Clan management page
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/options', methods=['GET', 'POST'])
|
||
@login_required
|
||
def options():
|
||
conn = get_db()
|
||
|
||
# Load clan based on current user's clan_id
|
||
clan = None
|
||
if current_user.clan_id:
|
||
clan = conn.execute(
|
||
'SELECT * FROM clans WHERE id = ?', (current_user.clan_id,)
|
||
).fetchone()
|
||
|
||
# Fetch website admins (users belonging to this clan other than current user)
|
||
admins = []
|
||
if clan and clan['owner_id'] == current_user.id:
|
||
admins = conn.execute(
|
||
'SELECT id, username, created_at FROM users WHERE clan_id = ? AND id != ? ORDER BY created_at ASC',
|
||
(clan['id'], current_user.id)
|
||
).fetchall()
|
||
|
||
members = []
|
||
if clan:
|
||
rows = conn.execute(
|
||
'''SELECT cm.id, cm.player_id, cm.player_name, cm.joined_at, cm.features,
|
||
ts.updated_at
|
||
FROM clan_members cm
|
||
LEFT JOIN town_state ts ON ts.player_id = cm.player_id
|
||
WHERE cm.clan_id = ?
|
||
GROUP BY cm.player_id
|
||
ORDER BY cm.joined_at DESC''',
|
||
(clan['id'],)
|
||
).fetchall()
|
||
|
||
now = datetime.utcnow()
|
||
for row in rows:
|
||
is_online = False
|
||
if row['updated_at']:
|
||
try:
|
||
last_seen = datetime.fromisoformat(row['updated_at'])
|
||
if (now - last_seen).total_seconds() <= 150:
|
||
is_online = True
|
||
except Exception:
|
||
pass
|
||
members.append({
|
||
'id': row['id'],
|
||
'player_id': row['player_id'],
|
||
'player_name': row['player_name'] or 'Άγνωστος',
|
||
'joined_at': row['joined_at'][:10] if row['joined_at'] else '',
|
||
'is_online': is_online,
|
||
'feat_farm': 'farm' in (row['features'] or 'farm,admin'),
|
||
'feat_admin': 'admin' in (row['features'] or 'farm,admin'),
|
||
})
|
||
|
||
conn.close()
|
||
return render_template('options.html', clan=clan, members=members, admins=admins)
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/create
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/create', methods=['POST'])
|
||
@login_required
|
||
def create_clan():
|
||
clan_name = request.form.get('clan_name', '').strip()
|
||
if not clan_name:
|
||
return redirect(url_for('auth.options'))
|
||
|
||
conn = get_db()
|
||
existing = conn.execute(
|
||
'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,)
|
||
).fetchone()
|
||
|
||
if not existing:
|
||
key = generate_clan_key()
|
||
cursor = conn.execute(
|
||
'INSERT INTO clans (owner_id, name, clan_key) VALUES (?, ?, ?)',
|
||
(current_user.id, clan_name, key)
|
||
)
|
||
clan_id = cursor.lastrowid
|
||
conn.execute('UPDATE users SET clan_id = ? WHERE id = ?', (clan_id, current_user.id))
|
||
conn.commit()
|
||
|
||
# Update the current_user object dynamically to reflect the new clan_id without re-login
|
||
current_user.clan_id = clan_id
|
||
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/regenerate-key
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/regenerate-key', methods=['POST'])
|
||
@login_required
|
||
def regenerate_key():
|
||
new_key = generate_clan_key()
|
||
conn = get_db()
|
||
conn.execute(
|
||
'UPDATE clans SET clan_key = ? WHERE owner_id = ?',
|
||
(new_key, current_user.id)
|
||
)
|
||
conn.commit()
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|
||
@auth.route('/auth/clan/remove-member/<player_id>', methods=['POST'])
|
||
@login_required
|
||
def remove_member(player_id):
|
||
conn = get_db()
|
||
clan = conn.execute(
|
||
'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,)
|
||
).fetchone()
|
||
if clan:
|
||
conn.execute(
|
||
'DELETE FROM clan_members WHERE clan_id = ? AND player_id = ?',
|
||
(clan['id'], player_id)
|
||
)
|
||
conn.commit()
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/update-features/<player_id>
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/update-features/<player_id>', methods=['POST'])
|
||
@login_required
|
||
def update_member_features(player_id):
|
||
farm = 'farm' if request.form.get('farm') else None
|
||
admin = 'admin' if request.form.get('admin') else None
|
||
features = ','.join(f for f in [farm, admin] if f) or ''
|
||
|
||
conn = get_db()
|
||
clan = conn.execute(
|
||
'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,)
|
||
).fetchone()
|
||
if clan:
|
||
conn.execute(
|
||
'UPDATE clan_members SET features = ? WHERE clan_id = ? AND player_id = ?',
|
||
(features, clan['id'], player_id)
|
||
)
|
||
conn.commit()
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/add-admin
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/add-admin', methods=['POST'])
|
||
@login_required
|
||
def add_admin():
|
||
username = request.form.get('admin_username', '').strip()
|
||
if not username:
|
||
return redirect(url_for('auth.options'))
|
||
|
||
conn = get_db()
|
||
clan = conn.execute(
|
||
'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,)
|
||
).fetchone()
|
||
|
||
if clan:
|
||
# Check if user exists
|
||
user = conn.execute('SELECT id, clan_id FROM users WHERE username = ?', (username,)).fetchone()
|
||
if user:
|
||
# If user already belongs to a clan, we could show an error, but let's just overwrite for now
|
||
# or maybe only if clan_id is NULL
|
||
conn.execute('UPDATE users SET clan_id = ? WHERE id = ?', (clan['id'], user['id']))
|
||
conn.commit()
|
||
flash(f"Ο χρήστης {username} προστέθηκε ως διαχειριστής.", "success")
|
||
else:
|
||
flash(f"Ο χρήστης {username} δεν βρέθηκε.", "error")
|
||
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/remove-admin/<admin_id>
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/remove-admin/<int:admin_id>', methods=['POST'])
|
||
@login_required
|
||
def remove_admin(admin_id):
|
||
conn = get_db()
|
||
clan = conn.execute(
|
||
'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,)
|
||
).fetchone()
|
||
|
||
if clan:
|
||
conn.execute('UPDATE users SET clan_id = NULL WHERE id = ? AND clan_id = ?', (admin_id, clan['id']))
|
||
conn.commit()
|
||
flash("Ο διαχειριστής αφαιρέθηκε.", "success")
|
||
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
# ------------------------------------------------------------------
|
||
# POST /auth/clan/leave
|
||
# ------------------------------------------------------------------
|
||
@auth.route('/auth/clan/leave', methods=['POST'])
|
||
@login_required
|
||
def leave_clan():
|
||
conn = get_db()
|
||
if current_user.clan_id:
|
||
clan = conn.execute('SELECT owner_id FROM clans WHERE id = ?', (current_user.clan_id,)).fetchone()
|
||
if clan and clan['owner_id'] != current_user.id:
|
||
conn.execute('UPDATE users SET clan_id = NULL WHERE id = ?', (current_user.id,))
|
||
conn.commit()
|
||
current_user.clan_id = None
|
||
flash("Έχετε αποχωρήσει από την ομάδα.", "success")
|
||
conn.close()
|
||
return redirect(url_for('auth.options'))
|
||
|
||
|