Moved authentification code into js/auth.js
This commit is contained in:
parent
fec4f513c8
commit
e3724a4842
292
backend/app.py
292
backend/app.py
@ -6,109 +6,120 @@ import os
|
||||
from datetime import datetime
|
||||
|
||||
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
|
||||
|
||||
# Directories
|
||||
DATA_DIR = 'data'
|
||||
USERS_FILE = os.path.join(DATA_DIR, 'users.json')
|
||||
DATA_DIR = "data"
|
||||
USERS_FILE = os.path.join(DATA_DIR, "users.json")
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
|
||||
# ==================== User helpers ====================
|
||||
def load_users():
|
||||
try:
|
||||
if os.path.exists(USERS_FILE):
|
||||
with open(USERS_FILE, 'r') as f:
|
||||
with open(USERS_FILE, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading users: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def save_users(users):
|
||||
try:
|
||||
with open(USERS_FILE, 'w') as f:
|
||||
with open(USERS_FILE, "w") as f:
|
||||
json.dump(users, f, indent=2)
|
||||
except Exception as e:
|
||||
print(f"Error saving users: {e}")
|
||||
|
||||
|
||||
def get_next_user_id(users):
|
||||
if not users:
|
||||
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):
|
||||
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):
|
||||
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)
|
||||
|
||||
|
||||
# ==================== Per‑user data helpers ====================
|
||||
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)
|
||||
return user_dir
|
||||
|
||||
|
||||
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:
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
with open(file_path, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading journeys for user {user_id}: {e}")
|
||||
return []
|
||||
|
||||
|
||||
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:
|
||||
with open(file_path, 'w') as f:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(journeys, f, indent=2)
|
||||
except Exception as e:
|
||||
print(f"Error saving journeys for user {user_id}: {e}")
|
||||
|
||||
|
||||
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:
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
with open(file_path, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading posts for user {user_id}: {e}")
|
||||
return []
|
||||
|
||||
|
||||
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:
|
||||
with open(file_path, 'w') as f:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(posts, f, indent=2)
|
||||
except Exception as e:
|
||||
print(f"Error saving posts for user {user_id}: {e}")
|
||||
|
||||
|
||||
# ==================== Authentication endpoints ====================
|
||||
@app.route('/api/register', methods=['POST'])
|
||||
@app.route("/api/register", methods=["POST"])
|
||||
def register():
|
||||
data = request.get_json()
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
username = data.get("username")
|
||||
password = data.get("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
|
||||
if get_user_by_username(username):
|
||||
return jsonify({'error': 'Username already taken'}), 409
|
||||
return jsonify({"error": "Username already taken"}), 409
|
||||
|
||||
users = load_users()
|
||||
new_id = get_next_user_id(users)
|
||||
|
||||
hashed = generate_password_hash(password)
|
||||
new_user = {
|
||||
'id': new_id,
|
||||
'username': username,
|
||||
'password_hash': hashed,
|
||||
'created_at': datetime.now().isoformat()
|
||||
"id": new_id,
|
||||
"username": username,
|
||||
"password_hash": hashed,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}
|
||||
users.append(new_user)
|
||||
save_users(users)
|
||||
@ -118,59 +129,58 @@ def register():
|
||||
save_user_posts(new_id, [])
|
||||
|
||||
# Log the user in automatically
|
||||
session['user_id'] = new_id
|
||||
session["user_id"] = new_id
|
||||
|
||||
return jsonify({
|
||||
'id': new_id,
|
||||
'username': username,
|
||||
'message': 'Registration successful'
|
||||
}), 201
|
||||
return jsonify(
|
||||
{"id": new_id, "username": username, "message": "Registration successful"}
|
||||
), 201
|
||||
|
||||
@app.route('/api/login', methods=['POST'])
|
||||
|
||||
@app.route("/api/login", methods=["POST"])
|
||||
def login():
|
||||
data = request.get_json()
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
username = data.get("username")
|
||||
password = data.get("password")
|
||||
|
||||
user = get_user_by_username(username)
|
||||
if not user or not check_password_hash(user['password_hash'], password):
|
||||
return jsonify({'error': 'Invalid username or password'}), 401
|
||||
if not user or not check_password_hash(user["password_hash"], password):
|
||||
return jsonify({"error": "Invalid username or password"}), 401
|
||||
|
||||
session['user_id'] = user['id']
|
||||
return jsonify({
|
||||
'id': user['id'],
|
||||
'username': user['username'],
|
||||
'message': 'Login successful'
|
||||
})
|
||||
session["user_id"] = user["id"]
|
||||
return jsonify(
|
||||
{"id": user["id"], "username": user["username"], "message": "Login successful"}
|
||||
)
|
||||
|
||||
@app.route('/api/logout', methods=['POST'])
|
||||
|
||||
@app.route("/api/logout", methods=["POST"])
|
||||
def logout():
|
||||
session.pop('user_id', None)
|
||||
return jsonify({'message': 'Logged out'})
|
||||
session.pop("user_id", None)
|
||||
return jsonify({"message": "Logged out"})
|
||||
|
||||
@app.route('/api/me', methods=['GET'])
|
||||
|
||||
@app.route("/api/me", methods=["GET"])
|
||||
def me():
|
||||
user_id = session.get('user_id')
|
||||
user_id = session.get("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)
|
||||
if not user:
|
||||
# Should not happen, but clean session
|
||||
session.pop('user_id', None)
|
||||
return jsonify({'error': 'User not found'}), 401
|
||||
return jsonify({
|
||||
'id': user['id'],
|
||||
'username': user['username']
|
||||
})
|
||||
session.pop("user_id", None)
|
||||
return jsonify({"error": "User not found"}), 401
|
||||
return jsonify({"id": user["id"], "username": user["username"]})
|
||||
|
||||
|
||||
# ==================== Journey endpoints (protected, user‑specific) ====================
|
||||
def require_login():
|
||||
if 'user_id' not in session:
|
||||
if "user_id" not in session:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_current_user_id():
|
||||
return session.get('user_id')
|
||||
return session.get("user_id")
|
||||
|
||||
|
||||
def get_journeys_for_current_user():
|
||||
user_id = get_current_user_id()
|
||||
@ -178,91 +188,98 @@ def get_journeys_for_current_user():
|
||||
return None
|
||||
return load_user_journeys(user_id)
|
||||
|
||||
@app.route('/api/journeys', methods=['GET'])
|
||||
|
||||
@app.route("/api/journeys", methods=["GET"])
|
||||
def get_journeys():
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
journeys = get_journeys_for_current_user()
|
||||
return jsonify(journeys)
|
||||
|
||||
|
||||
def get_next_journey_id(journeys):
|
||||
if not journeys:
|
||||
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():
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
data = request.get_json()
|
||||
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:
|
||||
return jsonify({'error': 'Journey title is required'}), 400
|
||||
return jsonify({"error": "Journey title is required"}), 400
|
||||
|
||||
user_id = get_current_user_id()
|
||||
journeys = get_journeys_for_current_user()
|
||||
new_id = get_next_journey_id(journeys)
|
||||
|
||||
new_journey = {
|
||||
'id': new_id,
|
||||
'title': title,
|
||||
'description': data.get('description', ''),
|
||||
'markers': data.get('markers', []),
|
||||
'created_at': datetime.now().isoformat()
|
||||
"id": new_id,
|
||||
"title": title,
|
||||
"description": data.get("description", ""),
|
||||
"markers": data.get("markers", []),
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}
|
||||
|
||||
journeys.append(new_journey)
|
||||
save_user_journeys(user_id, journeys)
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
return jsonify({'error': 'Journey not found'}), 404
|
||||
return jsonify({"error": "Journey not found"}), 404
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
return jsonify({'error': 'Journey not found'}), 404
|
||||
return jsonify({"error": "Journey not found"}), 404
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'No data provided'}), 400
|
||||
return jsonify({"error": "No data provided"}), 400
|
||||
|
||||
if 'title' in data:
|
||||
journey['title'] = data['title']
|
||||
if 'description' in data:
|
||||
journey['description'] = data['description']
|
||||
if 'markers' in data:
|
||||
journey['markers'] = data['markers']
|
||||
if "title" in data:
|
||||
journey["title"] = data["title"]
|
||||
if "description" in data:
|
||||
journey["description"] = data["description"]
|
||||
if "markers" in data:
|
||||
journey["markers"] = data["markers"]
|
||||
|
||||
save_user_journeys(get_current_user_id(), journeys)
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
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)
|
||||
return jsonify({'message': 'Journey deleted successfully', 'journey': journey})
|
||||
return jsonify({"message": "Journey deleted successfully", "journey": journey})
|
||||
|
||||
|
||||
# ==================== Blog Post endpoints (protected, user‑specific) ====================
|
||||
def get_posts_for_current_user():
|
||||
@ -271,97 +288,105 @@ def get_posts_for_current_user():
|
||||
return None
|
||||
return load_user_posts(user_id)
|
||||
|
||||
|
||||
def get_next_post_id(posts):
|
||||
if not posts:
|
||||
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():
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
posts = get_posts_for_current_user()
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
return jsonify({'error': 'Post not found'}), 404
|
||||
return jsonify({"error": "Post not found"}), 404
|
||||
return jsonify(post)
|
||||
|
||||
@app.route('/api/blog-posts', methods=['POST'])
|
||||
|
||||
@app.route("/api/blog-posts", methods=["POST"])
|
||||
def create_blog_post():
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
data = request.get_json()
|
||||
title = data.get('title')
|
||||
title = data.get("title")
|
||||
if not title:
|
||||
return jsonify({'error': 'Title required'}), 400
|
||||
return jsonify({"error": "Title required"}), 400
|
||||
|
||||
user_id = get_current_user_id()
|
||||
posts = get_posts_for_current_user()
|
||||
new_id = get_next_post_id(posts)
|
||||
|
||||
new_post = {
|
||||
'id': new_id,
|
||||
'title': title,
|
||||
'content': data.get('content', ''),
|
||||
'journeyId': data.get('journeyId'),
|
||||
'image': data.get('image'),
|
||||
'created_at': datetime.now().isoformat()
|
||||
"id": new_id,
|
||||
"title": title,
|
||||
"content": data.get("content", ""),
|
||||
"journeyId": data.get("journeyId"),
|
||||
"image": data.get("image"),
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}
|
||||
|
||||
posts.append(new_post)
|
||||
save_user_posts(user_id, posts)
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
return jsonify({'error': 'Post not found'}), 404
|
||||
return jsonify({"error": "Post not found"}), 404
|
||||
|
||||
data = request.get_json()
|
||||
if 'title' in data:
|
||||
post['title'] = data['title']
|
||||
if 'content' in data:
|
||||
post['content'] = data['content']
|
||||
if 'journeyId' in data:
|
||||
post['journeyId'] = data['journeyId']
|
||||
if 'image' in data:
|
||||
post['image'] = data['image']
|
||||
if "title" in data:
|
||||
post["title"] = data["title"]
|
||||
if "content" in data:
|
||||
post["content"] = data["content"]
|
||||
if "journeyId" in data:
|
||||
post["journeyId"] = data["journeyId"]
|
||||
if "image" in data:
|
||||
post["image"] = data["image"]
|
||||
|
||||
save_user_posts(get_current_user_id(), posts)
|
||||
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):
|
||||
if not require_login():
|
||||
return jsonify({'error': 'Authentication required'}), 401
|
||||
return jsonify({"error": "Authentication required"}), 401
|
||||
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:
|
||||
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)
|
||||
return jsonify({'message': 'Post deleted'})
|
||||
return jsonify({"message": "Post deleted"})
|
||||
|
||||
|
||||
# ==================== Health and root ====================
|
||||
@app.route('/api/journeys/health', methods=['GET'])
|
||||
@app.route("/api/journeys/health", methods=["GET"])
|
||||
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():
|
||||
return '''
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Journey Mapper API</title></head>
|
||||
@ -371,7 +396,8 @@ def index():
|
||||
<p>Authentication required: register, login, then use session cookies.</p>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, port=5000)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=5000)
|
||||
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
@ -1 +1,10 @@
|
||||
[]
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title": "test",
|
||||
"content": "ksafladjsfk",
|
||||
"journeyId": "1",
|
||||
"image": null,
|
||||
"created_at": "2026-03-27T21:23:39.755057"
|
||||
}
|
||||
]
|
||||
@ -220,62 +220,9 @@
|
||||
</main>
|
||||
|
||||
<div id="toast" class="toast"></div>
|
||||
|
||||
|
||||
<script src="js/auth.js"></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 '&';
|
||||
if (m === '<') return '<';
|
||||
if (m === '>') return '>';
|
||||
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 ====================
|
||||
async function loadPosts() {
|
||||
try {
|
||||
|
||||
@ -238,63 +238,11 @@
|
||||
</main>
|
||||
|
||||
<div id="toast" class="toast"></div>
|
||||
|
||||
|
||||
<script src="js/auth.js"></script>
|
||||
<script>
|
||||
// ==================== AUTH ====================
|
||||
const API_BASE = 'http://127.0.0.1:5000/api';
|
||||
let currentUser = 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 '&';
|
||||
if (m === '<') return '<';
|
||||
if (m === '>') return '>';
|
||||
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 ====================
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const postId = urlParams.get('id');
|
||||
|
||||
67
js/auth.js
Normal file
67
js/auth.js
Normal 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 "&";
|
||||
if (m === "<") return "<";
|
||||
if (m === ">") return ">";
|
||||
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);
|
||||
}
|
||||
105
login.html
105
login.html
@ -154,51 +154,8 @@
|
||||
</div>
|
||||
<div id="toast" class="toast"></div>
|
||||
|
||||
<script src="js/auth.js"></script>
|
||||
<script>
|
||||
const API_BASE = 'http://127.0.0.1:5000/api';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
async function login(username, password) {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Login failed');
|
||||
showToast(`Welcome, ${data.username}!`);
|
||||
window.location.href = 'map-page.html';
|
||||
} catch (err) {
|
||||
showToast(err.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function register(username, password) {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Registration failed');
|
||||
showToast(`Registered as ${data.username}. Logging in...`);
|
||||
window.location.href = 'map-page.html';
|
||||
} catch (err) {
|
||||
showToast(err.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Tab switching
|
||||
document.querySelectorAll('.auth-tab').forEach(tab => {
|
||||
@ -211,30 +168,56 @@
|
||||
});
|
||||
});
|
||||
// Login button
|
||||
document.getElementById('login-submit').addEventListener('click', () => {
|
||||
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;
|
||||
}
|
||||
login(username, password);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Login failed');
|
||||
showToast(`Welcome, ${data.username}`);
|
||||
window.location.href = 'map-page.html';
|
||||
} catch (err) {
|
||||
showToast(err.message, true);
|
||||
}
|
||||
});
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
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;
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Registration failed');
|
||||
showToast(`Registered as ${data.username}. Logging in...`);
|
||||
window.location.href = 'map-page.html';
|
||||
} catch (err) {
|
||||
showToast(err.message, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -876,65 +876,10 @@
|
||||
</main>
|
||||
</div>
|
||||
<div id="toast" class="toast"><p id="toast-message"></p></div>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>
|
||||
// ==================== AUTH & REDIRECT ====================
|
||||
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 "&";
|
||||
if (m === "<") return "<";
|
||||
if (m === ">") return ">";
|
||||
return m;
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== MAP CODE (from your file, but with credentials added) ====================
|
||||
// ==================== MAP CODE ====================
|
||||
(function () {
|
||||
// ==================== CONFIG ====================
|
||||
// API_BASE is already defined outside
|
||||
|
||||
// ==================== STATE =====================
|
||||
let map;
|
||||
let currentJourney = {
|
||||
@ -1462,7 +1407,7 @@
|
||||
|
||||
// ==================== AUTHENTICATION CHECK ====================
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const authenticated = await checkAuth();
|
||||
const authenticated = await checkAuthAndRedirect();
|
||||
if (authenticated) {
|
||||
updateUserMenu();
|
||||
window.startMap(); // call the map initializer from the IIFE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user