diff --git a/.gitignore b/.gitignore
index a11ea35..87c1c1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-venv/
-config.py
+venv/
+config.py
**/*.pyc
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 669e8d6..7a7c9ea 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,17 +1,17 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "name": "Python Debugger: Current File",
- "type": "debugpy",
- "request": "launch",
- "program": "${file}",
- "console": "integratedTerminal",
- "justMyCode": false,
- "args": ["--host=0.0.0.0"]
- }
- ]
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Python Debugger: Current File",
+ "type": "debugpy",
+ "request": "launch",
+ "program": "${file}",
+ "console": "integratedTerminal",
+ "justMyCode": false,
+ "args": ["--host=0.0.0.0"]
+ }
+ ]
}
\ No newline at end of file
diff --git a/app.py b/app.py
index c6ff6e5..1e5b844 100644
--- a/app.py
+++ b/app.py
@@ -1,186 +1,186 @@
-# app.py
-from flask import Flask, render_template, request, redirect, url_for, session, send_file, jsonify
-from flask_caching import Cache
-import requests.auth
-import os
-from lib.datetime import filter_accounts_next_30_days, filter_accounts_expired
-from lib.reqs import get_urls, get_user_accounts, add_user_account, delete_user_account, get_user_accounts_count
-from flask import send_from_directory
-import requests
-import base64
-from flask import Flask
-from config import DevelopmentConfig
-from paddleocr import PaddleOCR
-from PIL import Image
-import numpy as np
-
-os.environ["OMP_NUM_THREADS"] = "1"
-os.environ["MKL_NUM_THREADS"] = "1"
-
-app = Flask(__name__)
-app.config.from_object(
- DevelopmentConfig
-)
-cache = Cache(app, config={"CACHE_TYPE": "SimpleCache"})
-
-ocr = PaddleOCR(use_angle_cls=True, lang='en') # Adjust language if needed
-
-app.config['SESSION_COOKIE_SECURE'] = True # Only send cookie over HTTPS
-app.config['SESSION_COOKIE_HTTPONLY'] = True # Prevent JavaScript access
-app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # Adjust for cross-site requests
-app.config['PERMANENT_SESSION_LIFETIME'] = 60 * 60 * 24 * 365 # 1 year in seconds
-cache.clear() # Clears all cache entries
-
-@app.before_request
-def make_session_permanent():
- session.permanent = True
-
-@app.route('/manifest.json')
-def serve_manifest():
- return send_file('manifest.json', mimetype='application/manifest+json')
-
-@app.route("/favicon.ico")
-def favicon():
- return send_from_directory(
- os.path.join(app.root_path, "static"),
- "favicon.ico",
- mimetype="image/vnd.microsoft.icon",
- )
-
-
-@app.route("/")
-def index():
- # If the user is logged in, redirect to a protected page like /accounts
- if session.get("logged_in"):
- return redirect(url_for("home"))
- return render_template("index.html")
-
-
-@app.route("/home")
-@cache.cached(timeout=60) # cache for 120 seconds
-def home():
- if session.get("logged_in"):
- base_url = app.config["BASE_URL"] # Access base_url from the config
- all_accounts = get_user_accounts(base_url, session["auth_credentials"])
- count = len(all_accounts)
- current_month_accounts = filter_accounts_next_30_days(all_accounts)
- expired_accounts = filter_accounts_expired(all_accounts)
- return render_template(
- "home.html",
- username=session["username"],
- accounts=count,
- current_month_accounts=current_month_accounts,
- expired_accounts=expired_accounts,
- )
- return render_template("index.html")
-
-
-@app.route("/login", methods=["POST"])
-def login():
- username = request.form["username"]
- password = request.form["password"]
-
- # Encode the username and password in Base64
- credentials = f"{username}:{password}"
- encoded_credentials = base64.b64encode(credentials.encode()).decode()
-
- base_url = app.config["BASE_URL"] # Access base_url from the config
- login_url = f"{base_url}/Login" # Construct the full URL
-
- # Send GET request to the external login API with Basic Auth
- response = requests.get(
- login_url, auth=requests.auth.HTTPBasicAuth(username, password)
- )
-
- # Check if login was successful
- if response.status_code == 200 and response.json().get("auth") == "Success":
- # Set session variable to indicate the user is logged in
- session["logged_in"] = True
- session["username"] = username
- session["auth_credentials"] = encoded_credentials
- return redirect(url_for("home")) # Redirect to the Accounts page
- else:
- # Show error on the login page
- error = "Invalid username or password. Please try again."
- return render_template("index.html", error=error)
-
-
-@app.route("/urls", methods=["GET"])
-@cache.cached(timeout=300) # cache for 5 minutes
-def urls():
- # Check if the user is logged in
- if not session.get("logged_in"):
- return redirect(url_for("home"))
- # Placeholder content for Accounts page
- base_url = app.config["BASE_URL"] # Access base_url from the config
- return render_template(
- "urls.html", urls=get_urls(base_url, session["auth_credentials"])
- )
-
-
-@app.route("/accounts", methods=["GET"])
-@cache.cached(timeout=120) # cache for 120 seconds
-def user_accounts():
- # Check if the user is logged in
- if not session.get("logged_in"):
- return redirect(url_for("home"))
- # Placeholder content for Accounts page
- base_url = app.config["BASE_URL"] # Access base_url from the config
- return render_template(
- "user_accounts.html",
- username=session["username"],
- user_accounts=get_user_accounts(base_url, session["auth_credentials"]),
- auth=session["auth_credentials"],
- )
-
-
-@app.route("/accounts/add", methods=["GET", "POST"])
-def add_account():
- base_url = app.config["BASE_URL"] # Access base_url from the config
- if request.method == "POST":
- username = request.form["username"]
- password = request.form["password"]
- stream = request.form["stream"]
-
- if add_user_account(
- base_url, session["auth_credentials"], username, password, stream
- ):
- cache.clear() # Clears all cache entries
- return redirect(url_for("user_accounts"))
- return render_template("add_account.html")
-
- return render_template("add_account.html")
-
-
-@app.route("/accounts/delete", methods=["POST"])
-def delete_account():
- stream = request.form.get("stream")
- username = request.form.get("username")
- base_url = app.config["BASE_URL"]
-
- if delete_user_account(base_url, session["auth_credentials"], stream, username):
- cache.clear() # Clears all cache entries
- return redirect(url_for("user_accounts"))
- return redirect(url_for("user_accounts"))
-
-
-@app.route('/OCRupload', methods=['POST'])
-def OCRupload():
- if 'image' not in request.files:
- return jsonify({"error": "No image file found"}), 400
- # Get the uploaded file
- file = request.files['image']
- try:
- image = Image.open(file.stream)
- image_np = np.array(image)
- result = ocr.ocr(image_np)
- # Extract text
- extracted_text = []
- for line in result[0]:
- extracted_text.append(line[1][0])
- return render_template("add_account.html", username=extracted_text[2], password=extracted_text[3])
- except Exception as e:
- return jsonify({"error": str(e)}), 500
-
-if __name__ == "__main__":
- app.run(debug=app.config["DEBUG"], host=app.config["HOST"], port=app.config["PORT"])
+# app.py
+from flask import Flask, render_template, request, redirect, url_for, session, send_file, jsonify
+from flask_caching import Cache
+import requests.auth
+import os
+from lib.datetime import filter_accounts_next_30_days, filter_accounts_expired
+from lib.reqs import get_urls, get_user_accounts, add_user_account, delete_user_account, get_user_accounts_count
+from flask import send_from_directory
+import requests
+import base64
+from flask import Flask
+from config import DevelopmentConfig
+from paddleocr import PaddleOCR
+from PIL import Image
+import numpy as np
+
+os.environ["OMP_NUM_THREADS"] = "1"
+os.environ["MKL_NUM_THREADS"] = "1"
+
+app = Flask(__name__)
+app.config.from_object(
+ DevelopmentConfig
+)
+cache = Cache(app, config={"CACHE_TYPE": "SimpleCache"})
+
+ocr = PaddleOCR(use_angle_cls=True, lang='en') # Adjust language if needed
+
+app.config['SESSION_COOKIE_SECURE'] = True # Only send cookie over HTTPS
+app.config['SESSION_COOKIE_HTTPONLY'] = True # Prevent JavaScript access
+app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # Adjust for cross-site requests
+app.config['PERMANENT_SESSION_LIFETIME'] = 60 * 60 * 24 * 365 # 1 year in seconds
+cache.clear() # Clears all cache entries
+
+@app.before_request
+def make_session_permanent():
+ session.permanent = True
+
+@app.route('/manifest.json')
+def serve_manifest():
+ return send_file('manifest.json', mimetype='application/manifest+json')
+
+@app.route("/favicon.ico")
+def favicon():
+ return send_from_directory(
+ os.path.join(app.root_path, "static"),
+ "favicon.ico",
+ mimetype="image/vnd.microsoft.icon",
+ )
+
+
+@app.route("/")
+def index():
+ # If the user is logged in, redirect to a protected page like /accounts
+ if session.get("logged_in"):
+ return redirect(url_for("home"))
+ return render_template("index.html")
+
+
+@app.route("/home")
+@cache.cached(timeout=60) # cache for 120 seconds
+def home():
+ if session.get("logged_in"):
+ base_url = app.config["BASE_URL"] # Access base_url from the config
+ all_accounts = get_user_accounts(base_url, session["auth_credentials"])
+ count = len(all_accounts)
+ current_month_accounts = filter_accounts_next_30_days(all_accounts)
+ expired_accounts = filter_accounts_expired(all_accounts)
+ return render_template(
+ "home.html",
+ username=session["username"],
+ accounts=count,
+ current_month_accounts=current_month_accounts,
+ expired_accounts=expired_accounts,
+ )
+ return render_template("index.html")
+
+
+@app.route("/login", methods=["POST"])
+def login():
+ username = request.form["username"]
+ password = request.form["password"]
+
+ # Encode the username and password in Base64
+ credentials = f"{username}:{password}"
+ encoded_credentials = base64.b64encode(credentials.encode()).decode()
+
+ base_url = app.config["BASE_URL"] # Access base_url from the config
+ login_url = f"{base_url}/Login" # Construct the full URL
+
+ # Send GET request to the external login API with Basic Auth
+ response = requests.get(
+ login_url, auth=requests.auth.HTTPBasicAuth(username, password)
+ )
+
+ # Check if login was successful
+ if response.status_code == 200 and response.json().get("auth") == "Success":
+ # Set session variable to indicate the user is logged in
+ session["logged_in"] = True
+ session["username"] = username
+ session["auth_credentials"] = encoded_credentials
+ return redirect(url_for("home")) # Redirect to the Accounts page
+ else:
+ # Show error on the login page
+ error = "Invalid username or password. Please try again."
+ return render_template("index.html", error=error)
+
+
+@app.route("/urls", methods=["GET"])
+@cache.cached(timeout=300) # cache for 5 minutes
+def urls():
+ # Check if the user is logged in
+ if not session.get("logged_in"):
+ return redirect(url_for("home"))
+ # Placeholder content for Accounts page
+ base_url = app.config["BASE_URL"] # Access base_url from the config
+ return render_template(
+ "urls.html", urls=get_urls(base_url, session["auth_credentials"])
+ )
+
+
+@app.route("/accounts", methods=["GET"])
+@cache.cached(timeout=120) # cache for 120 seconds
+def user_accounts():
+ # Check if the user is logged in
+ if not session.get("logged_in"):
+ return redirect(url_for("home"))
+ # Placeholder content for Accounts page
+ base_url = app.config["BASE_URL"] # Access base_url from the config
+ return render_template(
+ "user_accounts.html",
+ username=session["username"],
+ user_accounts=get_user_accounts(base_url, session["auth_credentials"]),
+ auth=session["auth_credentials"],
+ )
+
+
+@app.route("/accounts/add", methods=["GET", "POST"])
+def add_account():
+ base_url = app.config["BASE_URL"] # Access base_url from the config
+ if request.method == "POST":
+ username = request.form["username"]
+ password = request.form["password"]
+ stream = request.form["stream"]
+
+ if add_user_account(
+ base_url, session["auth_credentials"], username, password, stream
+ ):
+ cache.clear() # Clears all cache entries
+ return redirect(url_for("user_accounts"))
+ return render_template("add_account.html")
+
+ return render_template("add_account.html")
+
+
+@app.route("/accounts/delete", methods=["POST"])
+def delete_account():
+ stream = request.form.get("stream")
+ username = request.form.get("username")
+ base_url = app.config["BASE_URL"]
+
+ if delete_user_account(base_url, session["auth_credentials"], stream, username):
+ cache.clear() # Clears all cache entries
+ return redirect(url_for("user_accounts"))
+ return redirect(url_for("user_accounts"))
+
+
+@app.route('/OCRupload', methods=['POST'])
+def OCRupload():
+ if 'image' not in request.files:
+ return jsonify({"error": "No image file found"}), 400
+ # Get the uploaded file
+ file = request.files['image']
+ try:
+ image = Image.open(file.stream)
+ image_np = np.array(image)
+ result = ocr.ocr(image_np)
+ # Extract text
+ extracted_text = []
+ for line in result[0]:
+ extracted_text.append(line[1][0])
+ return render_template("add_account.html", username=extracted_text[2], password=extracted_text[3])
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+if __name__ == "__main__":
+ app.run(debug=app.config["DEBUG"], host=app.config["HOST"], port=app.config["PORT"])
diff --git a/backend/app.py b/backend/app.py
index 5328312..b711923 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,19 +1,19 @@
-from flask import Flask, jsonify
-from config import DevelopmentConfig
-from lib.mysql import execute_query
-
-app = Flask(__name__)
-app.config.from_object(DevelopmentConfig)
-
-@app.route('/getUserAccounts', methods=['GET'])
-def get_user_accounts():
- # Use the execute_query function to get user accounts
-
- data = execute_query("SELECT COUNT(*) AS account_count FROM userAccounts WHERE userID = %s;", (1,))
- if data is None:
- return jsonify({"error": "Database query failed"}), 500
- return jsonify(data), 200
-
-# Run the app
-if __name__ == '__main__':
- app.run(debug=app.config["DEBUG"], port=app.config["PORT"])
+from flask import Flask, jsonify
+from config import DevelopmentConfig
+from lib.mysql import execute_query
+
+app = Flask(__name__)
+app.config.from_object(DevelopmentConfig)
+
+@app.route('/getUserAccounts', methods=['GET'])
+def get_user_accounts():
+ # Use the execute_query function to get user accounts
+
+ data = execute_query("SELECT COUNT(*) AS account_count FROM userAccounts WHERE userID = %s;", (1,))
+ if data is None:
+ return jsonify({"error": "Database query failed"}), 500
+ return jsonify(data), 200
+
+# Run the app
+if __name__ == '__main__':
+ app.run(debug=app.config["DEBUG"], port=app.config["PORT"])
diff --git a/backend/lib/mysql.py b/backend/lib/mysql.py
index 7450411..6279714 100644
--- a/backend/lib/mysql.py
+++ b/backend/lib/mysql.py
@@ -1,37 +1,37 @@
-import mysql.connector
-from flask import current_app
-
-def execute_query(query, params=None, fetch_one=False):
- """Execute a SQL query and optionally fetch results."""
- try:
- # Get database configuration from the current app context
- db_config = {
- "host": current_app.config['DBHOST'],
- "user": current_app.config['DBUSER'],
- "password": current_app.config['DBPASS'],
- "database": current_app.config['DATABASE'],
- }
-
- # Establish database connection
- connection = mysql.connector.connect(**db_config)
- cursor = connection.cursor(dictionary=True)
-
- # Execute the query with optional parameters
- cursor.execute(query, params)
-
- # Fetch results if it's a SELECT query
- if query.strip().upper().startswith("SELECT"):
- result = cursor.fetchone() if fetch_one else cursor.fetchall()
- else:
- # Commit changes for INSERT, UPDATE, DELETE
- connection.commit()
- result = cursor.rowcount # Number of affected rows
-
- # Close the database connection
- cursor.close()
- connection.close()
-
- return result
- except mysql.connector.Error as err:
- print("Error: ", err)
- return None
+import mysql.connector
+from flask import current_app
+
+def execute_query(query, params=None, fetch_one=False):
+ """Execute a SQL query and optionally fetch results."""
+ try:
+ # Get database configuration from the current app context
+ db_config = {
+ "host": current_app.config['DBHOST'],
+ "user": current_app.config['DBUSER'],
+ "password": current_app.config['DBPASS'],
+ "database": current_app.config['DATABASE'],
+ }
+
+ # Establish database connection
+ connection = mysql.connector.connect(**db_config)
+ cursor = connection.cursor(dictionary=True)
+
+ # Execute the query with optional parameters
+ cursor.execute(query, params)
+
+ # Fetch results if it's a SELECT query
+ if query.strip().upper().startswith("SELECT"):
+ result = cursor.fetchone() if fetch_one else cursor.fetchall()
+ else:
+ # Commit changes for INSERT, UPDATE, DELETE
+ connection.commit()
+ result = cursor.rowcount # Number of affected rows
+
+ # Close the database connection
+ cursor.close()
+ connection.close()
+
+ return result
+ except mysql.connector.Error as err:
+ print("Error: ", err)
+ return None
diff --git a/config.py.sample b/config.py.sample
index fc4419e..863df00 100644
--- a/config.py.sample
+++ b/config.py.sample
@@ -1,11 +1,11 @@
-# config.py
-
-class Config:
- DEBUG = False
- BASE_URL = '' # Set your base URL here
-
-class DevelopmentConfig(Config):
- DEBUG = True
-
-class ProductionConfig(Config):
- BASE_URL = '' # Production base URL
+# config.py
+
+class Config:
+ DEBUG = False
+ BASE_URL = '' # Set your base URL here
+
+class DevelopmentConfig(Config):
+ DEBUG = True
+
+class ProductionConfig(Config):
+ BASE_URL = '' # Production base URL
diff --git a/lib/datetime.py b/lib/datetime.py
index dc85d2d..48efe5a 100644
--- a/lib/datetime.py
+++ b/lib/datetime.py
@@ -1,46 +1,46 @@
-from datetime import datetime, timedelta
-from typing import List, Dict
-
-def filter_accounts_next_30_days(accounts: List[Dict[str, int]]) -> List[Dict[str, int]]:
- """Filter accounts whose expiry date falls within the next 30 days.
-
- Args:
- accounts (List[Dict[str, int]]): A list of account dictionaries, each containing
- an 'expiaryDate' key with an epoch timestamp as its value.
-
- Returns:
- List[Dict[str, int]]: A list of accounts expiring within the next 30 days.
- """
- now = datetime.now()
- thirty_days_later = now + timedelta(days=30)
-
- # Convert current time and 30 days later to epoch timestamps
- now_timestamp = int(now.timestamp())
- thirty_days_later_timestamp = int(thirty_days_later.timestamp())
-
- # Filter accounts with expiryDate within the next 30 days
- return [
- account for account in accounts
- if now_timestamp <= account['expiaryDate'] < thirty_days_later_timestamp
- ]
-
-def filter_accounts_expired(accounts: List[Dict[str, int]]) -> List[Dict[str, int]]:
- """Filter accounts whose expiry date has passed.
-
- Args:
- accounts (List[Dict[str, int]]): A list of account dictionaries, each containing
- an 'expiaryDate' key with an epoch timestamp as its value.
-
- Returns:
- List[Dict[str, int]]: A list of accounts that have expired.
- """
- # Get the current epoch timestamp
- current_timestamp = int(datetime.now().timestamp())
-
- # Filter accounts where the current date is greater than the expiryDate
- expired_accounts = [
- account for account in accounts
- if account['expiaryDate'] < current_timestamp
- ]
-
- return expired_accounts
+from datetime import datetime, timedelta
+from typing import List, Dict
+
+def filter_accounts_next_30_days(accounts: List[Dict[str, int]]) -> List[Dict[str, int]]:
+ """Filter accounts whose expiry date falls within the next 30 days.
+
+ Args:
+ accounts (List[Dict[str, int]]): A list of account dictionaries, each containing
+ an 'expiaryDate' key with an epoch timestamp as its value.
+
+ Returns:
+ List[Dict[str, int]]: A list of accounts expiring within the next 30 days.
+ """
+ now = datetime.now()
+ thirty_days_later = now + timedelta(days=30)
+
+ # Convert current time and 30 days later to epoch timestamps
+ now_timestamp = int(now.timestamp())
+ thirty_days_later_timestamp = int(thirty_days_later.timestamp())
+
+ # Filter accounts with expiryDate within the next 30 days
+ return [
+ account for account in accounts
+ if now_timestamp <= account['expiaryDate'] < thirty_days_later_timestamp
+ ]
+
+def filter_accounts_expired(accounts: List[Dict[str, int]]) -> List[Dict[str, int]]:
+ """Filter accounts whose expiry date has passed.
+
+ Args:
+ accounts (List[Dict[str, int]]): A list of account dictionaries, each containing
+ an 'expiaryDate' key with an epoch timestamp as its value.
+
+ Returns:
+ List[Dict[str, int]]: A list of accounts that have expired.
+ """
+ # Get the current epoch timestamp
+ current_timestamp = int(datetime.now().timestamp())
+
+ # Filter accounts where the current date is greater than the expiryDate
+ expired_accounts = [
+ account for account in accounts
+ if account['expiaryDate'] < current_timestamp
+ ]
+
+ return expired_accounts
diff --git a/lib/reqs.py b/lib/reqs.py
index 69514b4..3e56419 100644
--- a/lib/reqs.py
+++ b/lib/reqs.py
@@ -1,107 +1,107 @@
-import requests
-import json
-from datetime import datetime
-from typing import List, Dict, Any
-
-
-def get_urls(base_url: str, auth: str) -> List[Dict[str, Any]]:
- """Retrieve user account streams from the specified base URL.
-
- Args:
- base_url (str): The base URL of the API.
- auth (str): The authorization token for accessing the API.
-
- Returns:
- List[Dict[str, Any]]: A list of user account streams.
- """
- url = f"{base_url}/getUserAccounts/streams"
- payload = {}
- headers = {"Authorization": f"Basic {auth}"}
-
- response = requests.request("GET", url, headers=headers, data=payload)
- return json.loads(response.text)
-
-
-def get_user_accounts(base_url: str, auth: str) -> List[Dict[str, Any]]:
- """Retrieve user accounts from the specified base URL.
-
- Args:
- base_url (str): The base URL of the API.
- auth (str): The authorization token for accessing the API.
-
- Returns:
- List[Dict[str, Any]]: A list of user accounts with their expiration dates rendered.
- """
- url = f"{base_url}/getUserAccounts"
- payload = {}
- headers = {"Authorization": f"Basic {auth}"}
-
- response = requests.request("GET", url, headers=headers, data=payload)
- res_json = json.loads(response.text)
-
- for account in res_json:
- account["expiaryDate_rendered"] = datetime.utcfromtimestamp(
- account["expiaryDate"]
- ).strftime("%d/%m/%Y")
-
- return res_json
-
-
-def delete_user_account(base_url: str, auth: str, stream: str, username: str) -> bool:
- """Delete a user account from the specified base URL.
-
- Args:
- base_url (str): The base URL of the API.
- auth (str): The authorization token for accessing the API.
- stream (str): The name of the stream associated with the user account.
- username (str): The username of the account to delete.
-
- Returns:
- bool: True if the account was deleted successfully, False otherwise.
- """
- url = f"{base_url}/deleteAccount"
- payload = {"stream": stream, "user": username}
- headers = {"Authorization": f"Basic {auth}"}
-
- response = requests.request("POST", url, headers=headers, data=payload)
- return "Deleted" in response.text
-
-
-def add_user_account(base_url: str, auth: str, username: str, password: str, stream: str) -> bool:
- """Add a user account to the specified base URL.
-
- Args:
- base_url (str): The base URL of the API.
- auth (str): The authorization token for accessing the API.
- username (str): The username of the account to add.
- password (str): The password of the account to add.
- stream (str): The name of the stream associated with the user account.
-
- Returns:
- bool: True if the account was added successfully, False otherwise.
- """
- url = f"{base_url}/addAccount"
- payload = {"username": username, "password": password, "stream": stream}
- headers = {"Authorization": f"Basic {auth}"}
-
- response = requests.request("POST", url, headers=headers, data=payload)
- return "Added successfully" in response.text
-
-
-def get_user_accounts_count(base_url: str, auth: str) -> int:
- """Get the count of user accounts from the specified base URL.
-
- Args:
- base_url (str): The base URL of the API.
- auth (str): The authorization token for accessing the API.
-
- Returns:
- int: The count of user accounts.
- """
- url = f"{base_url}/getUserAccounts/count"
- payload = {}
- headers = {"Authorization": f"Basic {auth}"}
-
- response = requests.request("GET", url, headers=headers, data=payload)
- res_json = json.loads(response.text)
- return res_json['count']
+import requests
+import json
+from datetime import datetime
+from typing import List, Dict, Any
+
+
+def get_urls(base_url: str, auth: str) -> List[Dict[str, Any]]:
+ """Retrieve user account streams from the specified base URL.
+
+ Args:
+ base_url (str): The base URL of the API.
+ auth (str): The authorization token for accessing the API.
+
+ Returns:
+ List[Dict[str, Any]]: A list of user account streams.
+ """
+ url = f"{base_url}/getUserAccounts/streams"
+ payload = {}
+ headers = {"Authorization": f"Basic {auth}"}
+
+ response = requests.request("GET", url, headers=headers, data=payload)
+ return json.loads(response.text)
+
+
+def get_user_accounts(base_url: str, auth: str) -> List[Dict[str, Any]]:
+ """Retrieve user accounts from the specified base URL.
+
+ Args:
+ base_url (str): The base URL of the API.
+ auth (str): The authorization token for accessing the API.
+
+ Returns:
+ List[Dict[str, Any]]: A list of user accounts with their expiration dates rendered.
+ """
+ url = f"{base_url}/getUserAccounts"
+ payload = {}
+ headers = {"Authorization": f"Basic {auth}"}
+
+ response = requests.request("GET", url, headers=headers, data=payload)
+ res_json = json.loads(response.text)
+
+ for account in res_json:
+ account["expiaryDate_rendered"] = datetime.utcfromtimestamp(
+ account["expiaryDate"]
+ ).strftime("%d/%m/%Y")
+
+ return res_json
+
+
+def delete_user_account(base_url: str, auth: str, stream: str, username: str) -> bool:
+ """Delete a user account from the specified base URL.
+
+ Args:
+ base_url (str): The base URL of the API.
+ auth (str): The authorization token for accessing the API.
+ stream (str): The name of the stream associated with the user account.
+ username (str): The username of the account to delete.
+
+ Returns:
+ bool: True if the account was deleted successfully, False otherwise.
+ """
+ url = f"{base_url}/deleteAccount"
+ payload = {"stream": stream, "user": username}
+ headers = {"Authorization": f"Basic {auth}"}
+
+ response = requests.request("POST", url, headers=headers, data=payload)
+ return "Deleted" in response.text
+
+
+def add_user_account(base_url: str, auth: str, username: str, password: str, stream: str) -> bool:
+ """Add a user account to the specified base URL.
+
+ Args:
+ base_url (str): The base URL of the API.
+ auth (str): The authorization token for accessing the API.
+ username (str): The username of the account to add.
+ password (str): The password of the account to add.
+ stream (str): The name of the stream associated with the user account.
+
+ Returns:
+ bool: True if the account was added successfully, False otherwise.
+ """
+ url = f"{base_url}/addAccount"
+ payload = {"username": username, "password": password, "stream": stream}
+ headers = {"Authorization": f"Basic {auth}"}
+
+ response = requests.request("POST", url, headers=headers, data=payload)
+ return "Added successfully" in response.text
+
+
+def get_user_accounts_count(base_url: str, auth: str) -> int:
+ """Get the count of user accounts from the specified base URL.
+
+ Args:
+ base_url (str): The base URL of the API.
+ auth (str): The authorization token for accessing the API.
+
+ Returns:
+ int: The count of user accounts.
+ """
+ url = f"{base_url}/getUserAccounts/count"
+ payload = {}
+ headers = {"Authorization": f"Basic {auth}"}
+
+ response = requests.request("GET", url, headers=headers, data=payload)
+ res_json = json.loads(response.text)
+ return res_json['count']
diff --git a/static/service-worker.js b/static/service-worker.js
index ce7f439..ea6e563 100644
--- a/static/service-worker.js
+++ b/static/service-worker.js
@@ -1,15 +1,15 @@
-self.addEventListener('install', e => {
- // console.log('[Service Worker] Installed');
-});
-
-self.addEventListener('activate', e => {
- // console.log('[Service Worker] Activated');
-});
-
-self.addEventListener('fetch', e => {
- // e.respondWith(
- // caches.match(e.request).then(res => {
- // return res || fetch(e.request);
- // })
- // );
+self.addEventListener('install', e => {
+ // console.log('[Service Worker] Installed');
+});
+
+self.addEventListener('activate', e => {
+ // console.log('[Service Worker] Activated');
+});
+
+self.addEventListener('fetch', e => {
+ // e.respondWith(
+ // caches.match(e.request).then(res => {
+ // return res || fetch(e.request);
+ // })
+ // );
});
\ No newline at end of file
diff --git a/static/styles.css b/static/styles.css
index ad5bb13..d401238 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -1,30 +1,30 @@
-/* Base styles */
-html, body {
- height: 100%; /* Ensure the body and html elements fill the viewport height */
- margin: 0; /* Remove default margin */
- display: flex;
- flex-direction: column;
-}
-
-body {
- font-family: Arial, sans-serif;
-}
-
-header {
- background-color: #4CAF50;
- color: white;
- padding: 1em;
- text-align: center;
-}
-
-main {
- flex: 1; /* Make the main content area grow to fill the available space */
- padding: 1em;
-}
-
-footer {
- background-color: #333;
- color: white;
- text-align: center;
- padding: 1em;
-}
+/* Base styles */
+html, body {
+ height: 100%; /* Ensure the body and html elements fill the viewport height */
+ margin: 0; /* Remove default margin */
+ display: flex;
+ flex-direction: column;
+}
+
+body {
+ font-family: Arial, sans-serif;
+}
+
+header {
+ background-color: #4CAF50;
+ color: white;
+ padding: 1em;
+ text-align: center;
+}
+
+main {
+ flex: 1; /* Make the main content area grow to fill the available space */
+ padding: 1em;
+}
+
+footer {
+ background-color: #333;
+ color: white;
+ text-align: center;
+ padding: 1em;
+}
diff --git a/templates/add_account.html b/templates/add_account.html
index 076cbce..0f88dc6 100644
--- a/templates/add_account.html
+++ b/templates/add_account.html
@@ -1,106 +1,106 @@
-
-
-
-
-
-
- Add Account - KTVManager
-
-
-
-
-
-
-
-
-
-
-
-
-
- Add Account
-
-
-
-
Load Details Via OCR
-
-
-
-
-
-
-
+
+
+
+
+
+
+
Add Account - KTVManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add Account
+
+
+
+
Load Details Via OCR
+
+
+
+
+
+
+
diff --git a/templates/home.html b/templates/home.html
index a3f31ad..223d590 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -1,93 +1,93 @@
-
-
-
-
-
-
KTVManager
-
-
-
-
-
-
-
-
-
-
- Welcome {{ username }}!
-
- You have {{ accounts }} active accounts
-
-
- {% if current_month_accounts %}
- Accounts Expiring Within 30 Days
-
-
-
- Stream Name |
- Username |
- Expiry Date |
-
-
-
- {% for account in current_month_accounts %}
-
- {{ account.stream }} |
- {{ account.username }} |
- {{ account.expiaryDate_rendered }} |
-
- {% endfor %}
-
-
- {% endif %}
- {% if expired_accounts %}
- Expired Accounts
-
-
-
- Stream Name |
- Username |
- Expiry Date |
-
-
-
- {% for account in expired_accounts %}
-
- {{ account.stream }} |
- {{ account.username }} |
- {{ account.expiaryDate_rendered }} |
-
- {% endfor %}
-
-
- {% endif %}
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
KTVManager
+
+
+
+
+
+
+
+
+
+
+ Welcome {{ username }}!
+
+ You have {{ accounts }} active accounts
+
+
+ {% if current_month_accounts %}
+ Accounts Expiring Within 30 Days
+
+
+
+ Stream Name |
+ Username |
+ Expiry Date |
+
+
+
+ {% for account in current_month_accounts %}
+
+ {{ account.stream }} |
+ {{ account.username }} |
+ {{ account.expiaryDate_rendered }} |
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% if expired_accounts %}
+ Expired Accounts
+
+
+
+ Stream Name |
+ Username |
+ Expiry Date |
+
+
+
+ {% for account in expired_accounts %}
+
+ {{ account.stream }} |
+ {{ account.username }} |
+ {{ account.expiaryDate_rendered }} |
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/index.html b/templates/index.html
index bfd3452..f171cc5 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,80 +1,80 @@
-
-
-
-
-
-
-
KTVManager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to KTV Manager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
KTVManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Welcome to KTV Manager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/urls.html b/templates/urls.html
index 9ceb893..f66187c 100644
--- a/templates/urls.html
+++ b/templates/urls.html
@@ -1,65 +1,65 @@
-
-
-
-
-
-
- KTVManager
-
-
-
-
-
-
-
-
-
-
-
- URLs
-
-
-
- # |
- URL |
-
-
-
- {% for url in urls %}
-
- {{ loop.index }} |
- {{ url }} |
-
- {% endfor %}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ KTVManager
+
+
+
+
+
+
+
+
+
+
+
+ URLs
+
+
+
+ # |
+ URL |
+
+
+
+ {% for url in urls %}
+
+ {{ loop.index }} |
+ {{ url }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/user_accounts.html b/templates/user_accounts.html
index 6b67591..919d4b9 100644
--- a/templates/user_accounts.html
+++ b/templates/user_accounts.html
@@ -1,120 +1,120 @@
-
-
-
-
-
- KTVManager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ username }}'s Accounts
-
-
-
-
-
- Username |
- Stream |
- Stream URL |
- Expiry Date |
- Password |
- Actions |
-
-
-
- {% for account in user_accounts %}
-
-
- {{ account.username }} |
- {{ account.stream }} |
- {{ account.streamURL }} |
- {{ account.expiaryDate_rendered }} |
- {{ account.password }} |
-
-
- |
-
- {% endfor %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ KTVManager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ username }}'s Accounts
+
+
+
+
+
+ Username |
+ Stream |
+ Stream URL |
+ Expiry Date |
+ Password |
+ Actions |
+
+
+
+ {% for account in user_accounts %}
+
+
+ {{ account.username }} |
+ {{ account.stream }} |
+ {{ account.streamURL }} |
+ {{ account.expiaryDate_rendered }} |
+ {{ account.password }} |
+
+
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+