Moved authentification code into js/auth.js

This commit is contained in:
Josh-Dev-Quest 2026-03-28 13:36:17 +01:00
parent fec4f513c8
commit e3724a4842
No known key found for this signature in database
8 changed files with 330 additions and 364 deletions

View File

@ -6,109 +6,120 @@ import os
from datetime import datetime from datetime import datetime
app = Flask(__name__) app = Flask(__name__)
app.secret_key = 'your-secret-key-here-change-in-production' # needed for sessions app.secret_key = "your-secret-key-here-change-in-production" # needed for sessions
CORS(app, supports_credentials=True) # allow cookies CORS(app, supports_credentials=True) # allow cookies
# Directories # Directories
DATA_DIR = 'data' DATA_DIR = "data"
USERS_FILE = os.path.join(DATA_DIR, 'users.json') USERS_FILE = os.path.join(DATA_DIR, "users.json")
os.makedirs(DATA_DIR, exist_ok=True) os.makedirs(DATA_DIR, exist_ok=True)
# ==================== User helpers ==================== # ==================== User helpers ====================
def load_users(): def load_users():
try: try:
if os.path.exists(USERS_FILE): if os.path.exists(USERS_FILE):
with open(USERS_FILE, 'r') as f: with open(USERS_FILE, "r") as f:
return json.load(f) return json.load(f)
except Exception as e: except Exception as e:
print(f"Error loading users: {e}") print(f"Error loading users: {e}")
return [] return []
def save_users(users): def save_users(users):
try: try:
with open(USERS_FILE, 'w') as f: with open(USERS_FILE, "w") as f:
json.dump(users, f, indent=2) json.dump(users, f, indent=2)
except Exception as e: except Exception as e:
print(f"Error saving users: {e}") print(f"Error saving users: {e}")
def get_next_user_id(users): def get_next_user_id(users):
if not users: if not users:
return 1 return 1
return max(u['id'] for u in users) + 1 return max(u["id"] for u in users) + 1
def get_user_by_username(username): def get_user_by_username(username):
users = load_users() users = load_users()
return next((u for u in users if u['username'] == username), None) return next((u for u in users if u["username"] == username), None)
def get_user_by_id(user_id): def get_user_by_id(user_id):
users = load_users() users = load_users()
return next((u for u in users if u['id'] == user_id), None) return next((u for u in users if u["id"] == user_id), None)
# ==================== Peruser data helpers ==================== # ==================== Peruser data helpers ====================
def get_user_data_dir(user_id): def get_user_data_dir(user_id):
user_dir = os.path.join(DATA_DIR, 'users', str(user_id)) user_dir = os.path.join(DATA_DIR, "users", str(user_id))
os.makedirs(user_dir, exist_ok=True) os.makedirs(user_dir, exist_ok=True)
return user_dir return user_dir
def load_user_journeys(user_id): def load_user_journeys(user_id):
file_path = os.path.join(get_user_data_dir(user_id), 'journeys.json') file_path = os.path.join(get_user_data_dir(user_id), "journeys.json")
try: try:
if os.path.exists(file_path): if os.path.exists(file_path):
with open(file_path, 'r') as f: with open(file_path, "r") as f:
return json.load(f) return json.load(f)
except Exception as e: except Exception as e:
print(f"Error loading journeys for user {user_id}: {e}") print(f"Error loading journeys for user {user_id}: {e}")
return [] return []
def save_user_journeys(user_id, journeys): def save_user_journeys(user_id, journeys):
file_path = os.path.join(get_user_data_dir(user_id), 'journeys.json') file_path = os.path.join(get_user_data_dir(user_id), "journeys.json")
try: try:
with open(file_path, 'w') as f: with open(file_path, "w") as f:
json.dump(journeys, f, indent=2) json.dump(journeys, f, indent=2)
except Exception as e: except Exception as e:
print(f"Error saving journeys for user {user_id}: {e}") print(f"Error saving journeys for user {user_id}: {e}")
def load_user_posts(user_id): def load_user_posts(user_id):
file_path = os.path.join(get_user_data_dir(user_id), 'posts.json') file_path = os.path.join(get_user_data_dir(user_id), "posts.json")
try: try:
if os.path.exists(file_path): if os.path.exists(file_path):
with open(file_path, 'r') as f: with open(file_path, "r") as f:
return json.load(f) return json.load(f)
except Exception as e: except Exception as e:
print(f"Error loading posts for user {user_id}: {e}") print(f"Error loading posts for user {user_id}: {e}")
return [] return []
def save_user_posts(user_id, posts): def save_user_posts(user_id, posts):
file_path = os.path.join(get_user_data_dir(user_id), 'posts.json') file_path = os.path.join(get_user_data_dir(user_id), "posts.json")
try: try:
with open(file_path, 'w') as f: with open(file_path, "w") as f:
json.dump(posts, f, indent=2) json.dump(posts, f, indent=2)
except Exception as e: except Exception as e:
print(f"Error saving posts for user {user_id}: {e}") print(f"Error saving posts for user {user_id}: {e}")
# ==================== Authentication endpoints ==================== # ==================== Authentication endpoints ====================
@app.route('/api/register', methods=['POST']) @app.route("/api/register", methods=["POST"])
def register(): def register():
data = request.get_json() data = request.get_json()
username = data.get('username') username = data.get("username")
password = data.get('password') password = data.get("password")
if not username or not password: if not username or not password:
return jsonify({'error': 'Username and password required'}), 400 return jsonify({"error": "Username and password required"}), 400
# Check if username already exists # Check if username already exists
if get_user_by_username(username): if get_user_by_username(username):
return jsonify({'error': 'Username already taken'}), 409 return jsonify({"error": "Username already taken"}), 409
users = load_users() users = load_users()
new_id = get_next_user_id(users) new_id = get_next_user_id(users)
hashed = generate_password_hash(password) hashed = generate_password_hash(password)
new_user = { new_user = {
'id': new_id, "id": new_id,
'username': username, "username": username,
'password_hash': hashed, "password_hash": hashed,
'created_at': datetime.now().isoformat() "created_at": datetime.now().isoformat(),
} }
users.append(new_user) users.append(new_user)
save_users(users) save_users(users)
@ -118,59 +129,58 @@ def register():
save_user_posts(new_id, []) save_user_posts(new_id, [])
# Log the user in automatically # Log the user in automatically
session['user_id'] = new_id session["user_id"] = new_id
return jsonify({ return jsonify(
'id': new_id, {"id": new_id, "username": username, "message": "Registration successful"}
'username': username, ), 201
'message': 'Registration successful'
}), 201
@app.route('/api/login', methods=['POST'])
@app.route("/api/login", methods=["POST"])
def login(): def login():
data = request.get_json() data = request.get_json()
username = data.get('username') username = data.get("username")
password = data.get('password') password = data.get("password")
user = get_user_by_username(username) user = get_user_by_username(username)
if not user or not check_password_hash(user['password_hash'], password): if not user or not check_password_hash(user["password_hash"], password):
return jsonify({'error': 'Invalid username or password'}), 401 return jsonify({"error": "Invalid username or password"}), 401
session['user_id'] = user['id'] session["user_id"] = user["id"]
return jsonify({ return jsonify(
'id': user['id'], {"id": user["id"], "username": user["username"], "message": "Login successful"}
'username': user['username'], )
'message': 'Login successful'
})
@app.route('/api/logout', methods=['POST'])
@app.route("/api/logout", methods=["POST"])
def logout(): def logout():
session.pop('user_id', None) session.pop("user_id", None)
return jsonify({'message': 'Logged out'}) return jsonify({"message": "Logged out"})
@app.route('/api/me', methods=['GET'])
@app.route("/api/me", methods=["GET"])
def me(): def me():
user_id = session.get('user_id') user_id = session.get("user_id")
if not user_id: if not user_id:
return jsonify({'error': 'Not logged in'}), 401 return jsonify({"error": "Not logged in"}), 401
user = get_user_by_id(user_id) user = get_user_by_id(user_id)
if not user: if not user:
# Should not happen, but clean session # Should not happen, but clean session
session.pop('user_id', None) session.pop("user_id", None)
return jsonify({'error': 'User not found'}), 401 return jsonify({"error": "User not found"}), 401
return jsonify({ return jsonify({"id": user["id"], "username": user["username"]})
'id': user['id'],
'username': user['username']
})
# ==================== Journey endpoints (protected, userspecific) ==================== # ==================== Journey endpoints (protected, userspecific) ====================
def require_login(): def require_login():
if 'user_id' not in session: if "user_id" not in session:
return False return False
return True return True
def get_current_user_id(): def get_current_user_id():
return session.get('user_id') return session.get("user_id")
def get_journeys_for_current_user(): def get_journeys_for_current_user():
user_id = get_current_user_id() user_id = get_current_user_id()
@ -178,91 +188,98 @@ def get_journeys_for_current_user():
return None return None
return load_user_journeys(user_id) return load_user_journeys(user_id)
@app.route('/api/journeys', methods=['GET'])
@app.route("/api/journeys", methods=["GET"])
def get_journeys(): def get_journeys():
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
journeys = get_journeys_for_current_user() journeys = get_journeys_for_current_user()
return jsonify(journeys) return jsonify(journeys)
def get_next_journey_id(journeys): def get_next_journey_id(journeys):
if not journeys: if not journeys:
return 1 return 1
return max(j['id'] for j in journeys) + 1 return max(j["id"] for j in journeys) + 1
@app.route('/api/journeys', methods=['POST'])
@app.route("/api/journeys", methods=["POST"])
def create_journey(): def create_journey():
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
data = request.get_json() data = request.get_json()
if not data: if not data:
return jsonify({'error': 'No data provided'}), 400 return jsonify({"error": "No data provided"}), 400
title = data.get('title') title = data.get("title")
if not title: if not title:
return jsonify({'error': 'Journey title is required'}), 400 return jsonify({"error": "Journey title is required"}), 400
user_id = get_current_user_id() user_id = get_current_user_id()
journeys = get_journeys_for_current_user() journeys = get_journeys_for_current_user()
new_id = get_next_journey_id(journeys) new_id = get_next_journey_id(journeys)
new_journey = { new_journey = {
'id': new_id, "id": new_id,
'title': title, "title": title,
'description': data.get('description', ''), "description": data.get("description", ""),
'markers': data.get('markers', []), "markers": data.get("markers", []),
'created_at': datetime.now().isoformat() "created_at": datetime.now().isoformat(),
} }
journeys.append(new_journey) journeys.append(new_journey)
save_user_journeys(user_id, journeys) save_user_journeys(user_id, journeys)
return jsonify(new_journey), 201 return jsonify(new_journey), 201
@app.route('/api/journeys/<int:journey_id>', methods=['GET'])
@app.route("/api/journeys/<int:journey_id>", methods=["GET"])
def get_journey(journey_id): def get_journey(journey_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
journeys = get_journeys_for_current_user() journeys = get_journeys_for_current_user()
journey = next((j for j in journeys if j['id'] == journey_id), None) journey = next((j for j in journeys if j["id"] == journey_id), None)
if journey is None: if journey is None:
return jsonify({'error': 'Journey not found'}), 404 return jsonify({"error": "Journey not found"}), 404
return jsonify(journey) return jsonify(journey)
@app.route('/api/journeys/<int:journey_id>', methods=['PUT'])
@app.route("/api/journeys/<int:journey_id>", methods=["PUT"])
def update_journey(journey_id): def update_journey(journey_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
journeys = get_journeys_for_current_user() journeys = get_journeys_for_current_user()
journey = next((j for j in journeys if j['id'] == journey_id), None) journey = next((j for j in journeys if j["id"] == journey_id), None)
if journey is None: if journey is None:
return jsonify({'error': 'Journey not found'}), 404 return jsonify({"error": "Journey not found"}), 404
data = request.get_json() data = request.get_json()
if not data: if not data:
return jsonify({'error': 'No data provided'}), 400 return jsonify({"error": "No data provided"}), 400
if 'title' in data: if "title" in data:
journey['title'] = data['title'] journey["title"] = data["title"]
if 'description' in data: if "description" in data:
journey['description'] = data['description'] journey["description"] = data["description"]
if 'markers' in data: if "markers" in data:
journey['markers'] = data['markers'] journey["markers"] = data["markers"]
save_user_journeys(get_current_user_id(), journeys) save_user_journeys(get_current_user_id(), journeys)
return jsonify(journey) return jsonify(journey)
@app.route('/api/journeys/<int:journey_id>', methods=['DELETE'])
@app.route("/api/journeys/<int:journey_id>", methods=["DELETE"])
def delete_journey(journey_id): def delete_journey(journey_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
journeys = get_journeys_for_current_user() journeys = get_journeys_for_current_user()
journey = next((j for j in journeys if j['id'] == journey_id), None) journey = next((j for j in journeys if j["id"] == journey_id), None)
if journey is None: if journey is None:
return jsonify({'error': 'Journey not found'}), 404 return jsonify({"error": "Journey not found"}), 404
journeys = [j for j in journeys if j['id'] != journey_id] journeys = [j for j in journeys if j["id"] != journey_id]
save_user_journeys(get_current_user_id(), journeys) save_user_journeys(get_current_user_id(), journeys)
return jsonify({'message': 'Journey deleted successfully', 'journey': journey}) return jsonify({"message": "Journey deleted successfully", "journey": journey})
# ==================== Blog Post endpoints (protected, userspecific) ==================== # ==================== Blog Post endpoints (protected, userspecific) ====================
def get_posts_for_current_user(): def get_posts_for_current_user():
@ -271,97 +288,105 @@ def get_posts_for_current_user():
return None return None
return load_user_posts(user_id) return load_user_posts(user_id)
def get_next_post_id(posts): def get_next_post_id(posts):
if not posts: if not posts:
return 1 return 1
return max(p['id'] for p in posts) + 1 return max(p["id"] for p in posts) + 1
@app.route('/api/blog-posts', methods=['GET'])
@app.route("/api/blog-posts", methods=["GET"])
def get_blog_posts(): def get_blog_posts():
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
posts = get_posts_for_current_user() posts = get_posts_for_current_user()
return jsonify(posts) return jsonify(posts)
@app.route('/api/blog-posts/<int:post_id>', methods=['GET'])
@app.route("/api/blog-posts/<int:post_id>", methods=["GET"])
def get_blog_post(post_id): def get_blog_post(post_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
posts = get_posts_for_current_user() posts = get_posts_for_current_user()
post = next((p for p in posts if p['id'] == post_id), None) post = next((p for p in posts if p["id"] == post_id), None)
if not post: if not post:
return jsonify({'error': 'Post not found'}), 404 return jsonify({"error": "Post not found"}), 404
return jsonify(post) return jsonify(post)
@app.route('/api/blog-posts', methods=['POST'])
@app.route("/api/blog-posts", methods=["POST"])
def create_blog_post(): def create_blog_post():
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
data = request.get_json() data = request.get_json()
title = data.get('title') title = data.get("title")
if not title: if not title:
return jsonify({'error': 'Title required'}), 400 return jsonify({"error": "Title required"}), 400
user_id = get_current_user_id() user_id = get_current_user_id()
posts = get_posts_for_current_user() posts = get_posts_for_current_user()
new_id = get_next_post_id(posts) new_id = get_next_post_id(posts)
new_post = { new_post = {
'id': new_id, "id": new_id,
'title': title, "title": title,
'content': data.get('content', ''), "content": data.get("content", ""),
'journeyId': data.get('journeyId'), "journeyId": data.get("journeyId"),
'image': data.get('image'), "image": data.get("image"),
'created_at': datetime.now().isoformat() "created_at": datetime.now().isoformat(),
} }
posts.append(new_post) posts.append(new_post)
save_user_posts(user_id, posts) save_user_posts(user_id, posts)
return jsonify(new_post), 201 return jsonify(new_post), 201
@app.route('/api/blog-posts/<int:post_id>', methods=['PUT'])
@app.route("/api/blog-posts/<int:post_id>", methods=["PUT"])
def update_blog_post(post_id): def update_blog_post(post_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
posts = get_posts_for_current_user() posts = get_posts_for_current_user()
post = next((p for p in posts if p['id'] == post_id), None) post = next((p for p in posts if p["id"] == post_id), None)
if not post: if not post:
return jsonify({'error': 'Post not found'}), 404 return jsonify({"error": "Post not found"}), 404
data = request.get_json() data = request.get_json()
if 'title' in data: if "title" in data:
post['title'] = data['title'] post["title"] = data["title"]
if 'content' in data: if "content" in data:
post['content'] = data['content'] post["content"] = data["content"]
if 'journeyId' in data: if "journeyId" in data:
post['journeyId'] = data['journeyId'] post["journeyId"] = data["journeyId"]
if 'image' in data: if "image" in data:
post['image'] = data['image'] post["image"] = data["image"]
save_user_posts(get_current_user_id(), posts) save_user_posts(get_current_user_id(), posts)
return jsonify(post) return jsonify(post)
@app.route('/api/blog-posts/<int:post_id>', methods=['DELETE'])
@app.route("/api/blog-posts/<int:post_id>", methods=["DELETE"])
def delete_blog_post(post_id): def delete_blog_post(post_id):
if not require_login(): if not require_login():
return jsonify({'error': 'Authentication required'}), 401 return jsonify({"error": "Authentication required"}), 401
posts = get_posts_for_current_user() posts = get_posts_for_current_user()
post = next((p for p in posts if p['id'] == post_id), None) post = next((p for p in posts if p["id"] == post_id), None)
if not post: if not post:
return jsonify({'error': 'Post not found'}), 404 return jsonify({"error": "Post not found"}), 404
posts = [p for p in posts if p['id'] != post_id] posts = [p for p in posts if p["id"] != post_id]
save_user_posts(get_current_user_id(), posts) save_user_posts(get_current_user_id(), posts)
return jsonify({'message': 'Post deleted'}) return jsonify({"message": "Post deleted"})
# ==================== Health and root ==================== # ==================== Health and root ====================
@app.route('/api/journeys/health', methods=['GET']) @app.route("/api/journeys/health", methods=["GET"])
def health_check(): def health_check():
return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()}) return jsonify({"status": "healthy", "timestamp": datetime.now().isoformat()})
@app.route('/')
@app.route("/")
def index(): def index():
return ''' return """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head><title>Journey Mapper API</title></head> <head><title>Journey Mapper API</title></head>
@ -371,7 +396,8 @@ def index():
<p>Authentication required: register, login, then use session cookies.</p> <p>Authentication required: register, login, then use session cookies.</p>
</body> </body>
</html> </html>
''' """
if __name__ == '__main__':
if __name__ == "__main__":
app.run(debug=True, port=5000) app.run(debug=True, port=5000)

View File

@ -1 +1,42 @@
[] [
{
"id": 1,
"title": "test",
"description": "11",
"markers": [
{
"lat": 48.356249029540734,
"lng": 4.866943359375,
"title": "New Marker",
"date": "",
"description": "",
"videoUrl": ""
},
{
"lat": 46.961510504873104,
"lng": 9.371337890625002,
"title": "New Marker",
"date": "",
"description": "",
"videoUrl": ""
},
{
"lat": 45.51404592560427,
"lng": 11.656494140625002,
"title": "New Marker",
"date": "",
"description": "",
"videoUrl": ""
},
{
"lat": 43.52465500687188,
"lng": 11.162109375,
"title": "New Marker",
"date": "",
"description": "",
"videoUrl": ""
}
],
"created_at": "2026-03-27T21:49:26.885353"
}
]

View File

@ -1 +1,10 @@
[] [
{
"id": 1,
"title": "test",
"content": "ksafladjsfk",
"journeyId": "1",
"image": null,
"created_at": "2026-03-27T21:23:39.755057"
}
]

View File

@ -221,61 +221,8 @@
<div id="toast" class="toast"></div> <div id="toast" class="toast"></div>
<script src="js/auth.js"></script>
<script> <script>
// ==================== AUTH ====================
const API_BASE = 'http://127.0.0.1:5000/api';
let currentUser = null;
async function checkAuthAndRedirect() {
try {
const res = await fetch(`${API_BASE}/me`, { credentials: 'include' });
if (res.ok) {
currentUser = await res.json();
return true;
} else {
window.location.href = 'login.html';
return false;
}
} catch (err) {
window.location.href = 'login.html';
return false;
}
}
function updateUserMenu() {
const container = document.getElementById('user-menu');
if (currentUser) {
container.innerHTML = `
<span class="username"><i class="fas fa-user"></i> ${escapeHtml(currentUser.username)}</span>
<button id="logout-btn" class="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</button>
`;
document.getElementById('logout-btn')?.addEventListener('click', logout);
}
}
async function logout() {
await fetch(`${API_BASE}/logout`, { method: 'POST', credentials: 'include' });
window.location.href = 'login.html';
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}
function showToast(msg, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = msg;
toast.style.backgroundColor = isError ? 'var(--red-7)' : 'var(--green-7)';
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 3000);
}
// ==================== BLOG POSTS ==================== // ==================== BLOG POSTS ====================
async function loadPosts() { async function loadPosts() {
try { try {

View File

@ -239,62 +239,10 @@
<div id="toast" class="toast"></div> <div id="toast" class="toast"></div>
<script src="js/auth.js"></script>
<script> <script>
// ==================== AUTH ====================
const API_BASE = 'http://127.0.0.1:5000/api';
let currentUser = null;
let currentPostId = null; let currentPostId = null;
async function checkAuthAndRedirect() {
try {
const res = await fetch(`${API_BASE}/me`, { credentials: 'include' });
if (res.ok) {
currentUser = await res.json();
return true;
} else {
window.location.href = 'login.html';
return false;
}
} catch (err) {
window.location.href = 'login.html';
return false;
}
}
function updateUserMenu() {
const container = document.getElementById('user-menu');
if (currentUser) {
container.innerHTML = `
<span class="username"><i class="fas fa-user"></i> ${escapeHtml(currentUser.username)}</span>
<button id="logout-btn" class="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</button>
`;
document.getElementById('logout-btn')?.addEventListener('click', logout);
}
}
async function logout() {
await fetch(`${API_BASE}/logout`, { method: 'POST', credentials: 'include' });
window.location.href = 'login.html';
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}
function showToast(message, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.style.backgroundColor = isError ? 'var(--red-7)' : 'var(--green-7)';
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 3000);
}
// ==================== POST CRUD ==================== // ==================== POST CRUD ====================
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const postId = urlParams.get('id'); const postId = urlParams.get('id');

67
js/auth.js Normal file
View File

@ -0,0 +1,67 @@
const API_BASE = "http://127.0.0.1:5000/api";
let currentUser = null;
async function checkAuth() {
try {
const res = await fetch(`${API_BASE}/me`, { credentials: "include" });
if (res.ok) {
currentUser = await res.json();
return true;
}
return false;
} catch (err) {
return false;
}
}
async function checkAuthAndRedirect() {
const ok = await checkAuth();
if (!ok) {
window.location.href = "login.html";
return false;
}
return true;
}
function updateUserMenu() {
const container = document.getElementById("user-menu");
if (!container) return;
if (currentUser) {
container.innerHTML = `
<span class="username"><i class="fas fa-user"></i> ${escapeHtml(currentUser.username)}</span>
<button id="logout-btn" class="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</button>
`;
document.getElementById("logout-btn")?.addEventListener("click", logout);
} else {
container.innerHTML = `<button id="login-open-btn" class="login-btn"><i class="fas fa-sign-in-alt"></i> Login</button>`;
document.getElementById("login-open-btn")?.addEventListener("click", () => {
window.location.href = "login.html";
});
}
}
async function logout() {
await fetch(`${API_BASE}/logout`, { method: "POST", credentials: "include" });
window.location.href = "login.html";
}
function escapeHtml(str) {
if (!str) return "";
return str.replace(/[&<>]/g, function (m) {
if (m === "&") return "&amp;";
if (m === "<") return "&lt;";
if (m === ">") return "&gt;";
return m;
});
}
function showToast(msg, isError = false) {
const toast = document.getElementById("toast");
if (!toast) return;
toast.textContent = msg;
toast.style.backgroundColor = isError ? "var(--red-7)" : "var(--green-7)";
toast.style.display = "block";
setTimeout(() => {
toast.style.display = "none";
}, 3000);
}

View File

@ -154,18 +154,27 @@
</div> </div>
<div id="toast" class="toast"></div> <div id="toast" class="toast"></div>
<script src="js/auth.js"></script>
<script> <script>
const API_BASE = 'http://127.0.0.1:5000/api'; document.addEventListener('DOMContentLoaded', () => {
// Tab switching
function showToast(msg, isError = false) { document.querySelectorAll('.auth-tab').forEach(tab => {
const toast = document.getElementById('toast'); tab.addEventListener('click', () => {
toast.textContent = msg; const target = tab.dataset.tab;
toast.style.backgroundColor = isError ? 'var(--red-7)' : 'var(--green-7)'; document.querySelectorAll('.auth-tab').forEach(t => t.classList.remove('active'));
toast.style.display = 'block'; tab.classList.add('active');
setTimeout(() => { toast.style.display = 'none'; }, 3000); document.querySelectorAll('.auth-form').forEach(f => f.classList.remove('active'));
document.getElementById(`${target}-form`).classList.add('active');
});
});
// Login button
document.getElementById('login-submit').addEventListener('click', async () => {
const username = document.getElementById('login-username').value.trim();
const password = document.getElementById('login-password').value;
if (!username || !password) {
showToast('Please enter username and password', true);
return;
} }
async function login(username, password) {
try { try {
const res = await fetch(`${API_BASE}/login`, { const res = await fetch(`${API_BASE}/login`, {
method: 'POST', method: 'POST',
@ -175,14 +184,24 @@
}); });
const data = await res.json(); const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Login failed'); if (!res.ok) throw new Error(data.error || 'Login failed');
showToast(`Welcome, ${data.username}!`); showToast(`Welcome, ${data.username}`);
window.location.href = 'map-page.html'; window.location.href = 'map-page.html';
} catch (err) { } catch (err) {
showToast(err.message, true); showToast(err.message, true);
} }
});
// Register button
document.getElementById('register-submit').addEventListener('click', async () => {
const username = document.getElementById('register-username').value.trim();
const password = document.getElementById('register-password').value;
if (!username || !password) {
showToast('Please enter username and password', true);
return;
}
if (password.length < 4) {
showToast('Password must be at least 4 characters', true);
return;
} }
async function register(username, password) {
try { try {
const res = await fetch(`${API_BASE}/register`, { const res = await fetch(`${API_BASE}/register`, {
method: 'POST', method: 'POST',
@ -197,42 +216,6 @@
} catch (err) { } catch (err) {
showToast(err.message, true); showToast(err.message, true);
} }
}
document.addEventListener('DOMContentLoaded', () => {
// Tab switching
document.querySelectorAll('.auth-tab').forEach(tab => {
tab.addEventListener('click', () => {
const target = tab.dataset.tab;
document.querySelectorAll('.auth-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
document.querySelectorAll('.auth-form').forEach(f => f.classList.remove('active'));
document.getElementById(`${target}-form`).classList.add('active');
});
});
// Login button
document.getElementById('login-submit').addEventListener('click', () => {
const username = document.getElementById('login-username').value.trim();
const password = document.getElementById('login-password').value;
if (!username || !password) {
showToast('Please enter username and password', true);
return;
}
login(username, password);
});
// Register button
document.getElementById('register-submit').addEventListener('click', () => {
const username = document.getElementById('register-username').value.trim();
const password = document.getElementById('register-password').value;
if (!username || !password) {
showToast('Please enter username and password', true);
return;
}
if (password.length < 4) {
showToast('Password must be at least 4 characters', true);
return;
}
register(username, password);
}); });
}); });
</script> </script>

View File

@ -876,65 +876,10 @@
</main> </main>
</div> </div>
<div id="toast" class="toast"><p id="toast-message"></p></div> <div id="toast" class="toast"><p id="toast-message"></p></div>
<script src="js/auth.js"></script>
<script> <script>
// ==================== AUTH & REDIRECT ==================== // ==================== MAP CODE ====================
const API_BASE = "http://127.0.0.1:5000/api";
let currentUser = null;
async function checkAuth() {
try {
const res = await fetch(`${API_BASE}/me`, {
credentials: "include",
});
if (res.ok) {
currentUser = await res.json();
return true;
} else {
window.location.href = "login.html";
return false;
}
} catch (err) {
window.location.href = "login.html";
return false;
}
}
function updateUserMenu() {
const container = document.getElementById("user-menu");
if (currentUser) {
container.innerHTML = `
<span class="username"><i class="fas fa-user"></i> ${escapeHtml(currentUser.username)}</span>
<button id="logout-btn" class="logout-btn"><i class="fas fa-sign-out-alt"></i> Logout</button>
`;
document
.getElementById("logout-btn")
?.addEventListener("click", logout);
}
}
async function logout() {
await fetch(`${API_BASE}/logout`, {
method: "POST",
credentials: "include",
});
window.location.href = "login.html";
}
function escapeHtml(str) {
if (!str) return "";
return str.replace(/[&<>]/g, function (m) {
if (m === "&") return "&amp;";
if (m === "<") return "&lt;";
if (m === ">") return "&gt;";
return m;
});
}
// ==================== MAP CODE (from your file, but with credentials added) ====================
(function () { (function () {
// ==================== CONFIG ====================
// API_BASE is already defined outside
// ==================== STATE ===================== // ==================== STATE =====================
let map; let map;
let currentJourney = { let currentJourney = {
@ -1462,7 +1407,7 @@
// ==================== AUTHENTICATION CHECK ==================== // ==================== AUTHENTICATION CHECK ====================
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
const authenticated = await checkAuth(); const authenticated = await checkAuthAndRedirect();
if (authenticated) { if (authenticated) {
updateUserMenu(); updateUserMenu();
window.startMap(); // call the map initializer from the IIFE window.startMap(); // call the map initializer from the IIFE