"""Auth routes: register / login / logout / refresh.""" import uuid from flask import Blueprint, request, g, jsonify import os from auth import hash_password, verify_password, sign_token, require_auth import config from db import execute, query_one bp = Blueprint("auth", __name__) def registration_open(): """Whether an account may be created right now. Under the default "auto" policy the endpoint closes as soon as the first account exists, so a publicly reachable deployment cannot be signed up to by strangers. """ # Read at call time, not import time, so the policy can be changed without # a restart and so tests are not bound to whatever .env held at startup. policy = (os.environ.get("ALLOW_REGISTRATION") or config.ALLOW_REGISTRATION).lower() if policy == "true": return True if policy == "false": return False return query_one("SELECT id FROM users LIMIT 1") is None @bp.route("/registration-status", methods=["GET"]) def registration_status(): """Lets the UI hide the sign-up tab when registration is closed.""" return jsonify({"open": registration_open()}) @bp.route("/register", methods=["POST"]) def register(): if not registration_open(): return jsonify({"error": "注册已关闭:本实例已有账号"}), 403 data = request.get_json(silent=True) or {} email = (data.get("email") or "").strip() garmin_email = (data.get("garminEmail") or "").strip() garmin_password = data.get("garminPassword") or "" if not email or not garmin_email or not garmin_password: return jsonify({"error": "email, garminEmail, garminPassword 均为必填"}), 400 if query_one("SELECT id FROM users WHERE email = ?", [email]): return jsonify({"error": "该邮箱已注册"}), 409 uid = str(uuid.uuid4()) token = sign_token(uid) execute( "INSERT INTO users (id, email, garmin_email, garmin_password_hash, jwt_token) " "VALUES (?, ?, ?, ?, ?)", [uid, email, garmin_email, hash_password(garmin_password), token], ) return jsonify({"id": uid, "email": email, "token": token}), 201 @bp.route("/login", methods=["POST"]) def login(): data = request.get_json(silent=True) or {} email = (data.get("email") or "").strip() password = data.get("password") or "" user = query_one("SELECT * FROM users WHERE email = ?", [email]) if not user or not verify_password(password, user["garmin_password_hash"]): return jsonify({"error": "邮箱或密码错误"}), 401 token = sign_token(user["id"]) execute("UPDATE users SET jwt_token = ? WHERE id = ?", [token, user["id"]]) return jsonify({"id": user["id"], "email": user["email"], "token": token}) @bp.route("/logout", methods=["POST"]) @require_auth def logout(): execute("UPDATE users SET jwt_token = NULL WHERE id = ?", [g.user_id]) return jsonify({"message": "ok"}) @bp.route("/refresh", methods=["POST"]) @require_auth def refresh(): token = sign_token(g.user_id) execute("UPDATE users SET jwt_token = ? WHERE id = ?", [token, g.user_id]) return jsonify({"token": token})