Compare commits
	
		
			No commits in common. "9dc9b7e1b5e86d1b9a5dc982129d12fe4983ce08" and "13368a0437571a20419e5a97b0fe796ec01aa310" have entirely different histories.
		
	
	
		
			9dc9b7e1b5
			...
			13368a0437
		
	
		
| @ -1,9 +0,0 @@ | ||||
| [bumpversion] | ||||
| current_version = "0.1.0" | ||||
| commit = true | ||||
| tag = true | ||||
| 
 | ||||
| [[bumpversion::file]] | ||||
| filename = "VERSION" | ||||
| search = "{current_version}" | ||||
| replace = "{new_version}" | ||||
							
								
								
									
										2
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								app.py
									
									
									
									
									
								
							| @ -4,7 +4,7 @@ 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_stream_names | ||||
| from lib.reqs import get_urls, get_user_accounts, add_user_account, delete_user_account, get_user_accounts_count, get_stream_names | ||||
| from flask import send_from_directory | ||||
| import requests | ||||
| import base64 | ||||
|  | ||||
							
								
								
									
										19
									
								
								backend/app.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								backend/app.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +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"]) | ||||
							
								
								
									
										37
									
								
								backend/lib/mysql.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								backend/lib/mysql.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +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 | ||||
| @ -3,10 +3,6 @@ | ||||
| class Config: | ||||
|     DEBUG = False | ||||
|     BASE_URL = ''  # Set your base URL here | ||||
|     HOST = '0.0.0.0' | ||||
|     PORT = 5000 | ||||
|     OCR_ENABLED = False | ||||
|     TEXT_INPUT_ENABLED = False | ||||
| 
 | ||||
| class DevelopmentConfig(Config): | ||||
|     DEBUG = True | ||||
|  | ||||
							
								
								
									
										11
									
								
								dockerfile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								dockerfile
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| # Builder stage | ||||
| FROM python:3.11-slim-bookworm AS builder | ||||
| FROM python:3.11-slim-bookworm as builder | ||||
| 
 | ||||
| WORKDIR /app | ||||
| 
 | ||||
| @ -7,10 +7,15 @@ COPY requirements.txt . | ||||
| RUN pip install --no-cache-dir --prefix=/install -r requirements.txt | ||||
| 
 | ||||
| # Final stage | ||||
| FROM python:3.11-slim-bookworm AS final | ||||
| FROM python:3.11-slim-bookworm as final | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     libglib2.0-0 \ | ||||
|     libsm6 \ | ||||
|     libxrender1 \ | ||||
|     libxext6 \ | ||||
|     libgomp1 \ | ||||
|     libgl1 \ | ||||
|  && apt-get clean \ | ||||
|  && rm -rf /var/lib/apt/lists/* | ||||
| 
 | ||||
| @ -38,5 +43,5 @@ USER appuser | ||||
| 
 | ||||
| EXPOSE 5000 | ||||
| 
 | ||||
| ENV FLASK_ENV=production | ||||
| ENV FLASK_ENV production | ||||
| CMD ["./run.sh"] | ||||
|  | ||||
							
								
								
									
										19
									
								
								lib/reqs.py
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								lib/reqs.py
									
									
									
									
									
								
							| @ -88,6 +88,25 @@ def add_user_account(base_url: str, auth: str, username: str, password: str, str | ||||
|     return response.status_code == 200 | ||||
| 
 | ||||
| 
 | ||||
| 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'] | ||||
| 
 | ||||
| 
 | ||||
| def get_stream_names(base_url: str, auth: str) -> List[str]: | ||||
|     """Get a list of stream names from the API. | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								requirements.txt
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								requirements.txt
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -1,8 +1,13 @@ | ||||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block title %}Add Account - KTVManager{% endblock %} | ||||
| 
 | ||||
| {% block head_content %} | ||||
| <!-- templates/add_account.html --> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>Add Account - KTVManager</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" /> | ||||
|     <link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v={{ version }}" /> | ||||
|     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.css" /> | ||||
|     <style> | ||||
|         /* Hide the spinner by default */ | ||||
| @ -11,9 +16,29 @@ | ||||
|             display: none; | ||||
|         } | ||||
|     </style> | ||||
| {% endblock %} | ||||
| </head> | ||||
| <body> | ||||
|     <!-- Navbar --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/">Home</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/accounts">Accounts</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/urls">URLs</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
| {% block sub_nav %} | ||||
|     <!-- Sub-navigation for Accounts --> | ||||
|     <div class="bg-light py-2"> | ||||
|         <div class="container"> | ||||
| @ -27,54 +52,57 @@ | ||||
|             </ul> | ||||
|         </div> | ||||
|     </div> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h1>Add Account</h1> | ||||
|     <div> | ||||
|     <form action="/accounts/add" method="POST" onsubmit="showLoading()"> | ||||
|         <div class="form-group"> | ||||
|             <label for="username">Username</label> | ||||
|             <input type="text" class="form-control" id="username" name="username" value="{{ username }}" required> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <label for="password">Password</label> | ||||
|             <input type="text" class="form-control" id="password" name="password" value="{{ password }}"  required> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <label for="stream">Stream Name</label> | ||||
|             <input type="text" class="form-control" id="stream" name="stream" required> | ||||
|         </div> | ||||
|         <button type="submit" class="btn btn-primary" id="submitButton"> | ||||
|             <span class="spinner-border spinner-border-sm" id="loadingSpinner" role="status" aria-hidden="true"></span> | ||||
|             <span id="buttonText">Add Account</span> | ||||
|         </button> | ||||
|     </form> | ||||
|     {% if ocr_enabled %} | ||||
|     <hr> | ||||
|     <h2>Load Details Via OCR</h2> | ||||
|         <form action="/OCRupload" method="POST" enctype="multipart/form-data" onsubmit="showLoadingOCR()"> | ||||
|     <!-- Main Content --> | ||||
|     <main class="container mt-5"> | ||||
|         <h1>Add Account</h1> | ||||
|         <div> | ||||
|         <form action="/accounts/add" method="POST" onsubmit="showLoading()"> | ||||
|             <div class="form-group"> | ||||
|                 <label for="image">Select Image</label> | ||||
|                 <input type="file" class="form-control-file" id="image" name="image" accept="image/*" required> | ||||
|                 <label for="username">Username</label> | ||||
|                 <input type="text" class="form-control" id="username" name="username" value="{{ username }}" required> | ||||
|             </div> | ||||
|             <button type="submit" class="btn btn-success" id="ocrButton"> | ||||
|                 <span class="spinner-border spinner-border-sm" id="ocrLoadingSpinner" role="status" aria-hidden="true"></span> | ||||
|                 <span id="ocrButtonText">Load Details</span> | ||||
|             <div class="form-group"> | ||||
|                 <label for="password">Password</label> | ||||
|                 <input type="text" class="form-control" id="password" name="password" value="{{ password }}"  required> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                 <label for="stream">Stream Name</label> | ||||
|                 <input type="text" class="form-control" id="stream" name="stream" required> | ||||
|             </div> | ||||
|             <button type="submit" class="btn btn-primary" id="submitButton"> | ||||
|                 <span class="spinner-border spinner-border-sm" id="loadingSpinner" role="status" aria-hidden="true"></span> | ||||
|                 <span id="buttonText">Add Account</span> | ||||
|             </button> | ||||
|         </form> | ||||
|     {% endif %} | ||||
|     {% if text_input_enabled %} | ||||
|     <hr> | ||||
|     <h2>Load Details Via Text</h2> | ||||
|     <div class="form-group"> | ||||
|         <label for="accountDetails">Paste Account Details</label> | ||||
|         <textarea class="form-control" id="accountDetails" rows="4"></textarea> | ||||
|     </div> | ||||
|     {% endif %} | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block scripts %} | ||||
|         {% if ocr_enabled %} | ||||
|         <hr> | ||||
|         <h2>Load Details Via OCR</h2> | ||||
|             <form action="/OCRupload" method="POST" enctype="multipart/form-data" onsubmit="showLoadingOCR()"> | ||||
|                 <div class="form-group"> | ||||
|                     <label for="image">Select Image</label> | ||||
|                     <input type="file" class="form-control-file" id="image" name="image" accept="image/*" required> | ||||
|                 </div> | ||||
|                 <button type="submit" class="btn btn-success" id="ocrButton"> | ||||
|                     <span class="spinner-border spinner-border-sm" id="ocrLoadingSpinner" role="status" aria-hidden="true"></span> | ||||
|                     <span id="ocrButtonText">Load Details</span> | ||||
|                 </button> | ||||
|             </form> | ||||
|         {% endif %} | ||||
|         {% if text_input_enabled %} | ||||
|         <hr> | ||||
|         <h2>Load Details Via Text</h2> | ||||
|         <div class="form-group"> | ||||
|             <label for="accountDetails">Paste Account Details</label> | ||||
|             <textarea class="form-control" id="accountDetails" rows="4"></textarea> | ||||
|         </div> | ||||
|         {% endif %} | ||||
|      | ||||
|     </main> | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p></p> | ||||
|     </footer> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.js"></script> | ||||
|     <script> | ||||
|         function showLoading() { | ||||
| @ -147,4 +175,5 @@ | ||||
|             } | ||||
|         }); | ||||
|     </script> | ||||
| {% endblock %} | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,57 +0,0 @@ | ||||
| <!-- templates/base.html --> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>{% block title %}KTVManager{% endblock %}</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon-96x96.png') }}" sizes="96x96" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" /> | ||||
|     <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" /> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='apple-touch-icon.png') }}" /> | ||||
|     <meta name="apple-mobile-web-app-title" content="kTvManager" /> | ||||
|     <link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v={{ version }}" /> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" /> | ||||
|     {% block head_content %}{% endblock %} | ||||
| </head> | ||||
| <body> | ||||
| 
 | ||||
|     <!-- Navbar --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/">Home</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/accounts">Accounts</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/urls">URLs</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
|     {% block sub_nav %}{% endblock %} | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <main class="container mt-5"> | ||||
|         {% block content %}{% endblock %} | ||||
|     </main> | ||||
| 
 | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p>Version: {{ version }}</p> | ||||
|     </footer> | ||||
| 
 | ||||
|     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||||
|     {% block scripts %}{% endblock %} | ||||
| </body> | ||||
| </html> | ||||
| @ -1,61 +1,98 @@ | ||||
| {% extends "base.html" %} | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>KTVManager</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" /> | ||||
|     <link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v={{ version }}" /> | ||||
| </head> | ||||
| <body> | ||||
|     <script> | ||||
|         if ('serviceWorker' in navigator) { | ||||
|             navigator.serviceWorker.register('{{ url_for("static", filename="service-worker.js") }}') | ||||
|         } | ||||
|     </script> | ||||
|     <!-- Navbar --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/">Home</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/accounts">Accounts</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/urls">URLs</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
| {% block title %}KTVManager{% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h1>Welcome {{ username }}!</h1> | ||||
|     <br> | ||||
|     <h2>You have {{ accounts }} active accounts</h2> | ||||
|     <br> | ||||
|      | ||||
|     {% if current_month_accounts %} | ||||
|         <h3>Accounts Expiring Within 30 Days</h3> | ||||
|         <table class="table table-bordered table-striped"> | ||||
|             <thead class="thead-dark"> | ||||
|                 <tr> | ||||
|                     <th>Stream Name</th> | ||||
|                     <th>Username</th> | ||||
|                     <th>Expiry Date</th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|                 {% for account in current_month_accounts %} | ||||
|     <!-- Main Content --> | ||||
|     <main class="container mt-5"> | ||||
|         <h1>Welcome {{ username }}!</h1> | ||||
|         <br> | ||||
|         <h2>You have {{ accounts }} active accounts</h2> | ||||
|         <br> | ||||
|          | ||||
|         {% if current_month_accounts %} | ||||
|             <h3>Accounts Expiring Within 30 Days</h3> | ||||
|             <table class="table table-bordered table-striped"> | ||||
|                 <thead class="thead-dark"> | ||||
|                     <tr> | ||||
|                         <td>{{ account.stream }}</td> | ||||
|                         <td>{{ account.username }}</td> | ||||
|                         <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                         <th>Stream Name</th> | ||||
|                         <th>Username</th> | ||||
|                         <th>Expiry Date</th> | ||||
|                     </tr> | ||||
|                 {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|     {% endif %} | ||||
|     {% if expired_accounts %} | ||||
|         <h3>Expired Accounts</h3> | ||||
|         <table class="table table-bordered table-striped"> | ||||
|             <thead class="thead-dark"> | ||||
|                 <tr> | ||||
|                     <th>Stream Name</th> | ||||
|                     <th>Username</th> | ||||
|                     <th>Expiry Date</th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|                 {% for account in expired_accounts %} | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                     {% for account in current_month_accounts %} | ||||
|                         <tr> | ||||
|                             <td>{{ account.stream }}</td> | ||||
|                             <td>{{ account.username }}</td> | ||||
|                             <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                         </tr> | ||||
|                     {% endfor %} | ||||
|                 </tbody> | ||||
|             </table> | ||||
|         {% endif %} | ||||
|         {% if expired_accounts %} | ||||
|             <h3>Expired Accounts</h3> | ||||
|             <table class="table table-bordered table-striped"> | ||||
|                 <thead class="thead-dark"> | ||||
|                     <tr> | ||||
|                         <td>{{ account.stream }}</td> | ||||
|                         <td>{{ account.username }}</td> | ||||
|                         <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                         <th>Stream Name</th> | ||||
|                         <th>Username</th> | ||||
|                         <th>Expiry Date</th> | ||||
|                     </tr> | ||||
|                 {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|     {% endif %} | ||||
| {% endblock %} | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                     {% for account in expired_accounts %} | ||||
|                         <tr> | ||||
|                             <td>{{ account.stream }}</td> | ||||
|                             <td>{{ account.username }}</td> | ||||
|                             <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                         </tr> | ||||
|                     {% endfor %} | ||||
|                 </tbody> | ||||
|             </table> | ||||
|         {% endif %} | ||||
|     </main> | ||||
| 
 | ||||
| {% block scripts %} | ||||
| <script> | ||||
|     if ('serviceWorker' in navigator) { | ||||
|         navigator.serviceWorker.register('{{ url_for("static", filename="service-worker.js") }}') | ||||
|     } | ||||
| </script> | ||||
| {% endblock %} | ||||
|     <!-- Footer --> | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p>Version: {{ version }}</p> | ||||
|     </footer> | ||||
| 
 | ||||
|     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,36 +1,80 @@ | ||||
| {% extends "base.html" %} | ||||
| <!-- templates/index.html --> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>KTVManager</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon-96x96.png') }}" sizes="96x96" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" /> | ||||
|     <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" /> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='apple-touch-icon.png') }}" /> | ||||
|     <meta name="apple-mobile-web-app-title" content="kTvManager" /> | ||||
|     <link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v={{ version }}" /> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />     | ||||
| </head> | ||||
| <body> | ||||
| 
 | ||||
| {% block title %}KTVManager{% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h1>Welcome to KTV Manager</h1> | ||||
| 
 | ||||
|     <!-- Login Form --> | ||||
|     <form action="/login" method="post" class="mt-3"> | ||||
|         <div class="form-group"> | ||||
|             <label for="username">Username:</label> | ||||
|             <input type="text" class="form-control" id="username" name="username" required> | ||||
|     <!-- Navbar --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/">Home</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/accounts">Accounts</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/urls">URLs</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <label for="password">Password:</label> | ||||
|             <input type="password" class="form-control" id="password" name="password" required> | ||||
|         </div> | ||||
|         <button type="submit" class="btn btn-primary">Login</button> | ||||
|         {% if error %} | ||||
|             <div class="alert alert-danger mt-3">{{ error }}</div> | ||||
|         {% endif %} | ||||
|     </form> | ||||
| {% endblock %} | ||||
|     </nav> | ||||
| 
 | ||||
| {% block scripts %} | ||||
| <script> | ||||
|     if ('serviceWorker' in navigator) { | ||||
|         navigator.serviceWorker.register('{{ url_for("static", filename="service-worker.js") }}') | ||||
|         // .then(reg => { | ||||
|         // 		console.log('Service worker:', reg); | ||||
|         // .catch(err => { | ||||
|         // 	console.log('Service worker:', err); | ||||
|         // }); | ||||
|     } | ||||
| </script> | ||||
| {% endblock %} | ||||
|     <!-- Main Content --> | ||||
|     <main div class="container mt-5"> | ||||
|         <h1>Welcome to KTV Manager</h1> | ||||
| 
 | ||||
|         <!-- Login Form --> | ||||
|         <form action="/login" method="post" class="mt-3"> | ||||
|             <div class="form-group"> | ||||
|                 <label for="username">Username:</label> | ||||
|                 <input type="text" class="form-control" id="username" name="username" required> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                 <label for="password">Password:</label> | ||||
|                 <input type="password" class="form-control" id="password" name="password" required> | ||||
|             </div> | ||||
|             <button type="submit" class="btn btn-primary">Login</button> | ||||
|             {% if error %} | ||||
|                 <div class="alert alert-danger mt-3">{{ error }}</div> | ||||
|             {% endif %} | ||||
|         </form> | ||||
|     </div> | ||||
|     </main> | ||||
| 
 | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p></p> | ||||
|     </footer> | ||||
| 
 | ||||
|     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||||
|     <script> | ||||
|         if ('serviceWorker' in navigator) { | ||||
|             navigator.serviceWorker.register('{{ url_for("static", filename="service-worker.js") }}') | ||||
|             // .then(reg => { | ||||
|             // 		console.log('Service worker:', reg); | ||||
|             // .catch(err => { | ||||
|             // 	console.log('Service worker:', err); | ||||
|             // }); | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,23 +1,65 @@ | ||||
| {% extends "base.html" %} | ||||
| <!-- templates/urls.html --> | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>KTVManager</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />     | ||||
| </head> | ||||
| <body> | ||||
| 
 | ||||
| {% block title %}URLs - KTVManager{% endblock %} | ||||
|     <!-- Navbar (same as index.html) --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/">Home</a> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/accounts">Accounts</a> <!-- Link to the URLs page --> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <a class="nav-link" href="/urls">URLs</a> <!-- Link to the URLs page --> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h2>URLs</h2> | ||||
|     <table class="table table-striped"> | ||||
|         <thead> | ||||
|             <tr> | ||||
|                 <th>#</th> | ||||
|                 <th>URL</th> | ||||
|             </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|             {% for url in urls %} | ||||
|             <tr> | ||||
|                 <td>{{ loop.index }}</td> | ||||
|                 <td><a href="{{ url }}" target="_blank">{{ url }}</a></td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|         </tbody> | ||||
|     </table> | ||||
| {% endblock %} | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <main div class="container mt-5"> | ||||
|         <h2>URLs</h2> | ||||
|         <table class="table table-striped"> | ||||
|             <thead> | ||||
|                 <tr> | ||||
|                     <th>#</th> | ||||
|                     <th>URL</th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|                 {% for url in urls %} | ||||
|                 <tr> | ||||
|                     <td>{{ loop.index }}</td> | ||||
|                     <td><a href="{{ url }}" target="_blank">{{ url }}</a></td> | ||||
|                 </tr> | ||||
|                 {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|     </div> | ||||
|     </main> | ||||
|      | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p></p> | ||||
|     </footer> | ||||
| 
 | ||||
|     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| @ -1,13 +1,30 @@ | ||||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% block title %}Accounts - KTVManager{% endblock %} | ||||
| 
 | ||||
| {% block head_content %} | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>KTVManager</title> | ||||
|     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||||
|     <link rel="stylesheet" href="https://cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css"> | ||||
|     <link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.2.9/css/responsive.dataTables.min.css"> | ||||
| {% endblock %} | ||||
|     <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />     | ||||
| </head> | ||||
| <body> | ||||
|     <!-- Navbar --> | ||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
|         <a class="navbar-brand" href="/">KTVManager</a> | ||||
|         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|             <span class="navbar-toggler-icon"></span> | ||||
|         </button> | ||||
|         <div class="collapse navbar-collapse" id="navbarNav"> | ||||
|             <ul class="navbar-nav ml-auto"> | ||||
|                 <li class="nav-item"><a class="nav-link" href="/">Home</a></li> | ||||
|                 <li class="nav-item"><a class="nav-link" href="/accounts">Accounts</a></li> | ||||
|                 <li class="nav-item"><a class="nav-link" href="/urls">URLs</a></li> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
| {% block sub_nav %} | ||||
|     <!-- Sub-navigation for Accounts --> | ||||
|     <div class="bg-light py-2"> | ||||
|         <div class="container"> | ||||
| @ -21,49 +38,56 @@ | ||||
|             </ul> | ||||
|         </div> | ||||
|     </div> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h2>{{ username }}'s Accounts</h2> | ||||
|     <div class="table-responsive"> | ||||
|         <table class="table table-striped" id="accountsTable"> | ||||
|             <thead> | ||||
|                 <tr> | ||||
|                     <!-- <th>#</th> --> | ||||
|                     <th>Username</th> | ||||
|                     <th>Stream</th> | ||||
|                     <th>Stream URL</th> | ||||
|                     <th>Expiry Date</th> | ||||
|                     <th>Password</th> | ||||
|                     <th>Actions</th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|                 {% for account in user_accounts %} | ||||
|                 <tr> | ||||
|                     <!-- <td>{{ loop.index }}</td> --> | ||||
|                     <td>{{ account.username }}</td> | ||||
|                     <td>{{ account.stream }}</td> | ||||
|                     <td><a href="{{ account.streamURL }}" target="_blank">{{ account.streamURL }}</a></td> | ||||
|                     <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                     <td>{{ account.password }}</td> | ||||
|                     <td> | ||||
|                         <form action="/accounts/delete" method="POST" style="display:inline;"> | ||||
|                             <input type="hidden" name="stream" value="{{ account.stream }}"> | ||||
|                             <input type="hidden" name="username" value="{{ account.username }}"> | ||||
|                             <button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this account?');"> | ||||
|                                 Delete | ||||
|                             </button> | ||||
|                         </form> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|                 {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|     <!-- Main Content --> | ||||
|     <main div class="container mt-5"> | ||||
|         <h2>{{ username }}'s Accounts</h2> | ||||
|         <div class="table-responsive"> | ||||
|             <table class="table table-striped" id="accountsTable"> | ||||
|                 <thead> | ||||
|                     <tr> | ||||
|                         <!-- <th>#</th> --> | ||||
|                         <th>Username</th> | ||||
|                         <th>Stream</th> | ||||
|                         <th>Stream URL</th> | ||||
|                         <th>Expiry Date</th> | ||||
|                         <th>Password</th> | ||||
|                         <th>Actions</th> | ||||
|                     </tr> | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                     {% for account in user_accounts %} | ||||
|                     <tr> | ||||
|                         <!-- <td>{{ loop.index }}</td> --> | ||||
|                         <td>{{ account.username }}</td> | ||||
|                         <td>{{ account.stream }}</td> | ||||
|                         <td><a href="{{ account.streamURL }}" target="_blank">{{ account.streamURL }}</a></td> | ||||
|                         <td>{{ account.expiaryDate_rendered }}</td> | ||||
|                         <td>{{ account.password }}</td> | ||||
|                         <td> | ||||
|                             <form action="/accounts/delete" method="POST" style="display:inline;"> | ||||
|                                 <input type="hidden" name="stream" value="{{ account.stream }}"> | ||||
|                                 <input type="hidden" name="username" value="{{ account.username }}"> | ||||
|                                 <button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this account?');"> | ||||
|                                     Delete | ||||
|                                 </button> | ||||
|                             </form> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                     {% endfor %} | ||||
|                 </tbody> | ||||
|             </table> | ||||
|         </div> | ||||
|     </div> | ||||
| {% endblock %} | ||||
|     </main> | ||||
| 
 | ||||
| {% block scripts %} | ||||
|     <footer class="bg-dark text-white text-center py-3 mt-5"> | ||||
|         <p></p> | ||||
|     </footer> | ||||
| 
 | ||||
|     <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> | ||||
|     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||||
|     <script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script> | ||||
|     <script src="https://cdn.datatables.net/responsive/2.2.9/js/dataTables.responsive.min.js"></script> | ||||
|     <script> | ||||
| @ -90,4 +114,7 @@ | ||||
|             }); | ||||
|         }); | ||||
|     </script> | ||||
| {% endblock %} | ||||
|      | ||||
|      | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user