2025-07-17 15:41:47 +01:00
|
|
|
from flask import Blueprint, jsonify, Response, request, current_app
|
2025-07-15 15:45:17 +01:00
|
|
|
from ktvmanager.lib.database import (
|
|
|
|
get_user_accounts,
|
|
|
|
get_stream_names,
|
|
|
|
single_check,
|
|
|
|
add_account,
|
|
|
|
delete_account,
|
|
|
|
get_user_id_from_username,
|
2025-07-17 15:41:47 +01:00
|
|
|
save_push_subscription,
|
|
|
|
get_push_subscriptions,
|
2025-07-15 15:45:17 +01:00
|
|
|
)
|
2025-07-13 19:40:04 +01:00
|
|
|
from ktvmanager.lib.get_urls import get_latest_urls_from_dns
|
2025-07-15 09:28:47 +01:00
|
|
|
from ktvmanager.lib.auth import requires_basic_auth, check_login
|
2025-07-15 15:42:47 +01:00
|
|
|
from ktvmanager.lib.checker import validate_account
|
2025-07-15 15:45:17 +01:00
|
|
|
from typing import Tuple
|
2025-07-17 15:41:47 +01:00
|
|
|
import json
|
|
|
|
from pywebpush import webpush, WebPushException
|
2025-07-13 19:40:04 +01:00
|
|
|
|
|
|
|
api_blueprint = Blueprint("api", __name__)
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/getUserAccounts")
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def get_user_accounts_route(username: str, password: str) -> Response:
|
|
|
|
"""Retrieves all accounts associated with a user.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response containing the user's accounts or an error message.
|
|
|
|
"""
|
2025-07-13 19:40:04 +01:00
|
|
|
user_id = get_user_id_from_username(username)
|
|
|
|
if user_id:
|
|
|
|
return get_user_accounts(user_id)
|
|
|
|
return jsonify({"message": "User not found"}), 404
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/getStreamNames")
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def get_stream_names_route(username: str, password: str) -> Response:
|
|
|
|
"""Retrieves all stream names.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response containing the list of stream names.
|
|
|
|
"""
|
2025-07-13 19:40:04 +01:00
|
|
|
return get_stream_names()
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/getUserAccounts/streams")
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def get_user_accounts_streams_route(username: str, password: str) -> Response:
|
|
|
|
"""Retrieves the latest stream URLs from DNS.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response containing the list of stream URLs.
|
|
|
|
"""
|
2025-07-13 19:40:04 +01:00
|
|
|
return jsonify(get_latest_urls_from_dns())
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/singleCheck", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def single_check_route(username: str, password: str) -> Response:
|
|
|
|
"""Performs a single account check.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response with the result of the check.
|
|
|
|
"""
|
2025-07-13 19:40:04 +01:00
|
|
|
return single_check()
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/addAccount", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def add_account_route(username: str, password: str) -> Response:
|
|
|
|
"""Adds a new account for the user.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response confirming the account was added or an error message.
|
|
|
|
"""
|
2025-07-14 19:18:47 +01:00
|
|
|
user_id = get_user_id_from_username(username)
|
|
|
|
return add_account(user_id)
|
2025-07-13 19:40:04 +01:00
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-13 19:40:04 +01:00
|
|
|
@api_blueprint.route("/deleteAccount", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def delete_account_route(username: str, password: str) -> Response:
|
|
|
|
"""Deletes an account for the user.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response confirming the account was deleted or an error message.
|
|
|
|
"""
|
2025-07-14 19:18:47 +01:00
|
|
|
user_id = get_user_id_from_username(username)
|
2025-07-15 09:28:47 +01:00
|
|
|
return delete_account(user_id)
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-15 15:12:44 +01:00
|
|
|
@api_blueprint.route("/validateAccount", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def validate_account_route(username: str, password: str) -> Tuple[Response, int]:
|
|
|
|
"""Validates an account.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user (used for authentication).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A tuple containing a Flask JSON response and an HTTP status code.
|
|
|
|
"""
|
2025-07-15 15:12:44 +01:00
|
|
|
return validate_account()
|
|
|
|
|
2025-07-15 15:45:17 +01:00
|
|
|
|
2025-07-15 09:28:47 +01:00
|
|
|
@api_blueprint.route("/Login")
|
|
|
|
@requires_basic_auth
|
2025-07-15 15:45:17 +01:00
|
|
|
def login_route(username: str, password: str) -> Response:
|
|
|
|
"""Logs a user in.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response with the result of the login attempt.
|
|
|
|
"""
|
2025-07-17 15:41:47 +01:00
|
|
|
return check_login(username, password)
|
|
|
|
|
|
|
|
|
|
|
|
@api_blueprint.route("/save-subscription", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
|
|
|
def save_subscription(username: str, password: str) -> Response:
|
|
|
|
"""Saves a push notification subscription.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response.
|
|
|
|
"""
|
|
|
|
user_id = get_user_id_from_username(username)
|
|
|
|
if not user_id:
|
|
|
|
return jsonify({"message": "User not found"}), 404
|
|
|
|
|
|
|
|
subscription_data = request.get_json()
|
|
|
|
if not subscription_data:
|
|
|
|
return jsonify({"message": "No subscription data provided"}), 400
|
|
|
|
|
|
|
|
save_push_subscription(user_id, json.dumps(subscription_data))
|
|
|
|
return jsonify({"message": "Subscription saved."})
|
|
|
|
|
|
|
|
|
|
|
|
def send_notification(subscription_info, message_body):
|
|
|
|
try:
|
|
|
|
webpush(
|
|
|
|
subscription_info=subscription_info,
|
|
|
|
data=message_body,
|
|
|
|
vapid_private_key=current_app.config["VAPID_PRIVATE_KEY"],
|
|
|
|
vapid_claims={"sub": "mailto:your-email@example.com"},
|
|
|
|
)
|
|
|
|
except WebPushException as ex:
|
|
|
|
print(f"Web push error: {ex}")
|
|
|
|
# You might want to remove the subscription if it's invalid
|
|
|
|
if ex.response and ex.response.status_code == 410:
|
|
|
|
print("Subscription is no longer valid, removing from DB.")
|
|
|
|
# Add logic to remove the subscription from your database
|
|
|
|
|
|
|
|
|
|
|
|
@api_blueprint.route("/send-expiry-notifications", methods=["POST"])
|
|
|
|
@requires_basic_auth
|
|
|
|
def send_expiry_notifications_route(username: str, password: str) -> Response:
|
|
|
|
"""Triggers the sending of expiry notifications.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username: The username of the user.
|
|
|
|
password: The password of the user.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A Flask JSON response.
|
|
|
|
"""
|
|
|
|
from ktvmanager.account_checker import send_expiry_notifications
|
|
|
|
send_expiry_notifications()
|
|
|
|
return jsonify({"message": "Expiry notifications sent."})
|