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