modification of code and loggger apply and changes of update from.

This commit is contained in:
2026-02-18 18:00:20 +05:30
parent 0ba78a0bd1
commit 63bcbeb9a2
12 changed files with 361 additions and 138 deletions

26
.gitignore vendored
View File

@@ -1,30 +1,18 @@
# Python # Python
*.__pycache__ *.__pycache__
*.pyc *.pyc
*.pyo *.pyos
*.pyd *.pyd
__pycache__ __pycache__
.vscode/
.idea/
# Ingnor upload files # Ignore upload files
static/uploads/ static/uploads/
# Ignore files
venv
# Ignore Log files ss
logs/
# Environment variables # Environment variables
.env .env
venv
# Python cache # Ignore Log files
__pycache__/ logs/
*.pyc
# OS / Editor
.vscode/
.idea/
__pycache__/
*.pyc

View File

@@ -7,7 +7,12 @@ class FileHandler:
@staticmethod @staticmethod
def CHeckExistingOrCreateNewUploadFolder(): def CHeckExistingOrCreateNewUploadFolder():
#Wheteher path exists # Whether path exists
os.makedirs(FileHandler.UPLOAD_FOLDER, exist_ok=True) os.makedirs(FileHandler.UPLOAD_FOLDER, exist_ok=True)
return return
@staticmethod
def CheckExistingOrCreateNewLoggerFolder():
if not os.path.exists("logs"):
os.mkdir("logs")
return

View File

@@ -1,22 +1,49 @@
import os import os
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json import logging
from flask import current_app
from datetime import datetime from datetime import datetime
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user from flask import session, request, current_app
class LogHelper: class LogHelper:
@staticmethod
def setup_logger(app):
if not os.path.exists("logs"):
os.makedirs("logs")
formatter = logging.Formatter("%(asctime)s | %(levelname)s | User:%(user)s | IP:%(ip)s | %(message)s")
file_handler = logging.FileHandler("logs/app.log")
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
app.logger.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.addHandler(stream_handler)
# ---------------------------------------
# Log User Activity
# ---------------------------------------
@staticmethod
def log_request():
if request.endpoint and "static" not in request.endpoint:
user = session.get("user", "Anonymous")
ip = request.remote_addr
current_app.logger.info(
f"{request.method} {request.path}",
extra={"user": user, "ip": ip}
)
# ---------------------------------------
# Custom Action Logging
# ---------------------------------------
@staticmethod @staticmethod
def log_action(action, details=""): def log_action(action, details=""):
"""Log user actions with timestamp, user, action, and details.""" user = session.get("user", "Anonymous")
logData = LogData() ip = request.remote_addr
logData.WriteLog(action, details="") current_app.logger.info(f"{action} | {details}",extra={"user": user, "ip": ip})
class LogData:
filepath = ""
timestamp = None
def __init__(self):
self.filepath = os.path.join(current_app.root_path, 'activity.log')
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

View File

@@ -1,4 +1,5 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash, session from flask import Blueprint, render_template, request, redirect, url_for, flash, session
import os
from functools import wraps from functools import wraps
from ldap3 import Server, Connection, ALL from ldap3 import Server, Connection, ALL
from ldap3.core.exceptions import LDAPException from ldap3.core.exceptions import LDAPException
@@ -9,76 +10,51 @@ class LoginAuth:
# Create Blueprint # Create Blueprint
self.bp = Blueprint("auth", __name__) self.bp = Blueprint("auth", __name__)
# ------------------------------- # LDAP CONFIG
# LDAP CONFIGURATION self.LDAP_SERVER = os.getenv("LDAP_SERVER", "ldap://host.docker.internal:389")
# ------------------------------- self.BASE_DN = "ou=users,dc=lcepl,dc=org"
self.LDAP_SERVER = "ldap://localhost:389"
self.BASE_DN = "ou=users,dc=lcepl,dc=org" # LDAP Users DN # Register Routes
self.bp.add_url_rule("/login", view_func=self.login, methods=["GET", "POST"])
self.bp.add_url_rule("/logout", view_func=self.logout)
# ------------------------------- # ================= LOGIN =================
# LOGIN ROUTE def login(self):
# ------------------------------- if request.method == "POST":
# @self.bp.route('/login', methods=['GET', 'POST']) username = request.form.get("username")
# def login(): password = request.form.get("password")
# if request.method == 'POST':
# username = request.form.get("username")
# password = request.form.get("password")
# if not username or not password:
# flash("Username and password are required!", "danger")
# return render_template("login.html")
# user_dn = f"uid={username},{self.BASE_DN}"
# server = Server(self.LDAP_SERVER, get_info=ALL)
# try:
# # Attempt LDAP bind
# conn = Connection(server, user=user_dn, password=password, auto_bind=True)
# if conn.bound:
# session['user'] = username
# flash(f"Login successful! Welcome {username}", "success")
# return redirect(url_for('welcome'))
# else:
# flash("Invalid username or password!", "danger")
# except LDAPException as e:
# flash(f"LDAP login failed: {str(e)}", "danger")
# finally:
# if 'conn' in locals():
# conn.unbind()
# # GET request: show login form
# return render_template("login.html")
if not username or not password:
flash("Username and password are required!", "danger")
return render_template("login.html")
# LOGIN ROUTE user_dn = f"uid={username},{self.BASE_DN}"
@self.bp.route('/login', methods=['GET', 'POST']) server = Server(self.LDAP_SERVER, get_info=ALL)
def login():
if request.method == 'POST': try:
username = request.form.get("username") conn = Connection(server, user=user_dn, password=password, auto_bind=True)
password = request.form.get("password")
# Dummy validation — REPLACE with DB check later if conn.bound:
if username == "admin" and password == "admin123": session["user"] = username
session['user'] = username flash(f"Login successful! Welcome {username}", "success")
flash("Login successful!", "success") conn.unbind()
return redirect(url_for('welcome')) return redirect(url_for("welcome"))
else: else:
flash("Invalid username or password!", "danger") flash("Invalid username or password!", "danger")
return render_template("login.html")
except LDAPException as e:
flash(f"LDAP login failed: {str(e)}", "danger")
# ------------------------------- return render_template("login.html")
# LOGOUT ROUTE
# -------------------------------
@self.bp.route('/logout')
def logout():
session.clear()
flash("Logged out successfully!", "success")
return redirect(url_for('auth.login'))
# =================================================== # ================= LOGOUT =================
# LOGIN REQUIRED DECORATOR INSIDE CLASS def logout(self):
# =================================================== session.clear()
flash("Logged out successfully!", "success")
return redirect(url_for("auth.login"))
# ================= LOGIN REQUIRED =================
def login_required(self, f): def login_required(self, f):
"""
Protect routes: redirect to login if user not authenticated.
"""
@wraps(f) @wraps(f)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if "user" not in session: if "user" not in session:

View File

@@ -13,7 +13,7 @@ class MatCreditHandler:
def fetch_all(self): def fetch_all(self):
try: try:
self.cursor.callproc("GetMatCedit") self.cursor.callproc("GetMatCredit")
result_sets = self.cursor.stored_results() result_sets = self.cursor.stored_results()
mat_rows = next(result_sets).fetchall() mat_rows = next(result_sets).fetchall()
utilization_rows = next(result_sets).fetchall() utilization_rows = next(result_sets).fetchall()

View File

@@ -1,22 +1,36 @@
# -------------- development's Dockerfile ----------------
# FROM python:3.11-slim
# # Prevent Python buffering
# ENV PYTHONDONTWRITEBYTECODE=1
# ENV PYTHONUNBUFFERED=1
# WORKDIR /app
# # Install system deps (if needed later)
# RUN apt-get update && apt-get install -y \
# build-essential \
# && rm -rf /var/lib/apt/lists/*
# COPY requirements.txt .
# RUN pip install --no-cache-dir -r requirements.txt
# COPY . .
# EXPOSE 5000
# CMD ["python", "main.py"]
# -------------- Production Dockerfile ----------------
FROM python:3.11-slim FROM python:3.11-slim
# Prevent Python buffering
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app WORKDIR /app
# Install system deps (if needed later)
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY . . COPY . .
EXPOSE 5000 EXPOSE 5010
CMD ["python", "main.py"]
CMD ["gunicorn", "--bind", "0.0.0.0:5010", "main:app"]
# end

View File

View File

@@ -1,29 +1,42 @@
version: "3.9" version: "3.9"
services: services:
# Database connection
db: db:
image: mysql:8.0 image: mysql:8
container_name: income_tax_db container_name: tax-mysql
restart: always restart: always
environment: environment:
MYSQL_ROOT_PASSWORD: root MYSQL_ROOT_PASSWORD: tiger
MYSQL_DATABASE: test_income_taxdb MYSQL_DATABASE: income_tax_db
ports:
- "3307:3306"
volumes: volumes:
- mysql_data:/var/lib/mysql - mysql_data:/var/lib/mysql
- ./db/income_tax.sql:/docker-entrypoint-initdb.d/income_tax.sql
web: # Application config
flaskapp:
build: . build: .
container_name: income_tax_web container_name: tax-flask
restart: always restart: always
ports: ports:
- "5010:5010" - "5010:5010"
env_file:
- .env
depends_on: depends_on:
- db - db
environment:
DB_HOST: db
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: tiger
DB_NAME: income_tax_db
FLASK_HOST: 0.0.0.0
FLASK_PORT: 5010
FLASK_DEBUG: "false"
SECRET_KEY: secret1234
LDAP_SERVER: ldap://host.docker.internal:389
LOG_VIEW_SECRET: super-log-2026
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./logs:/app/logs
volumes: volumes:
mysql_data: mysql_data:

107
main.py
View File

@@ -1,7 +1,6 @@
from flask import Flask, render_template, request, redirect, url_for, flash,send_file ,jsonify from flask import Flask, render_template, request, redirect, url_for, flash,send_file ,jsonify, session
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv()
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from datetime import date from datetime import date
from AppCode.Config import DBConfig from AppCode.Config import DBConfig
@@ -14,7 +13,12 @@ from AppCode.AOHandler import AOHandler
from AppCode.CITHandler import CITHandler from AppCode.CITHandler import CITHandler
from AppCode.ITATHandler import ITATHandler from AppCode.ITATHandler import ITATHandler
from AppCode.MatCreditHandler import MatCreditHandler from AppCode.MatCreditHandler import MatCreditHandler
import logging
import sys
from AppCode.Log import LogHelper
# Loading env file
load_dotenv()
# Server # Server
app = Flask(__name__) app = Flask(__name__)
@@ -24,6 +28,13 @@ app.secret_key=os.getenv("SECRET_KEY")
auth = LoginAuth() auth = LoginAuth()
app.register_blueprint(auth.bp) app.register_blueprint(auth.bp)
# LOGGING SETUP
LogHelper.setup_logger(app)
@app.before_request
def log_all_requests():
LogHelper.log_request()
# welcome page # welcome page
@app.route('/') @app.route('/')
@@ -46,6 +57,7 @@ def upload_file():
FileHandler.CHeckExistingOrCreateNewUploadFolder() FileHandler.CHeckExistingOrCreateNewUploadFolder()
docHandler = DocumentHandler() docHandler = DocumentHandler()
docHandler.Upload(request=request) docHandler.Upload(request=request)
LogHelper.log_action("UPLOAD", "Document uploaded")
return redirect(url_for('view_documents')) return redirect(url_for('view_documents'))
return render_template('upload.html') return render_template('upload.html')
@@ -68,7 +80,7 @@ def uploaded_file(filename):
if not os.path.exists(filepath): if not os.path.exists(filepath):
flash("Unsupported file type for viewing", "warning") flash("Unsupported file type for viewing", "warning")
return redirect(url_for('view_documents')) return redirect(url_for('view_documents'))
LogHelper.log_action("VIEW FILE", filename)
file_ext = filename.rsplit('.', 1)[-1].lower() file_ext = filename.rsplit('.', 1)[-1].lower()
# --- View Mode --- # --- View Mode ---
if mode == 'view': if mode == 'view':
@@ -124,7 +136,7 @@ def add_itr():
mat_utilized=float(request.form.get("mat_credit_utilized", 0)), mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Created via ITR" remarks="Created via ITR"
) )
LogHelper.log_action("ADD ITR Record", f"Year: {request.form['year']}")
# flash("ITR record added successfully!", "success") # flash("ITR record added successfully!", "success")
flash("ITR record and documents uploaded successfully!", "success") flash("ITR record and documents uploaded successfully!", "success")
return redirect(url_for('display_itr')) return redirect(url_for('display_itr'))
@@ -138,6 +150,7 @@ def delete_itr(id):
itr = ITRHandler() itr = ITRHandler()
itr.delete_itr_by_id(id=id) itr.delete_itr_by_id(id=id)
itr.close() itr.close()
LogHelper.log_action("ITR record deleted successfully!", id)
return redirect(url_for('display_itr')) return redirect(url_for('display_itr'))
## 3. UPDATE an existing ITR record ## 3. UPDATE an existing ITR record
@@ -150,6 +163,17 @@ def update_itr(id):
data = {k: request.form.get(k, 0) for k in request.form} data = {k: request.form.get(k, 0) for k in request.form}
itr.update(id, data) itr.update(id, data)
itr.close() itr.close()
mat = MatCreditHandler()
# AUTO SAVE MAT FROM ITR
mat.save_from_itr(
year=request.form["year"],
mat_created=float(request.form.get("mat_credit_created", 0)),
opening_balance=float(request.form.get("opening_balance", 0)),
mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Updated via ITR"
)
LogHelper.log_action("ITR record updated successfully!", data)
return redirect(url_for('display_itr')) return redirect(url_for('display_itr'))
record = itr.get_itr_by_id(id) record = itr.get_itr_by_id(id)
@@ -193,7 +217,7 @@ def add_ao():
mat_utilized=float(request.form.get("mat_credit_utilized", 0)), mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Created via ao" remarks="Created via ao"
) )
LogHelper.log_action("AO record added successfully!", "")
flash("AO record added successfully!", "success") flash("AO record added successfully!", "success")
return redirect(url_for('display_ao')) return redirect(url_for('display_ao'))
return render_template('add_ao.html',current_date=date.today().isoformat()) return render_template('add_ao.html',current_date=date.today().isoformat())
@@ -212,6 +236,15 @@ def update_ao(id):
data = request.form.to_dict() data = request.form.to_dict()
ao.update_ao(id, data) ao.update_ao(id, data)
ao.close() ao.close()
mat = MatCreditHandler()
mat.save_from_itr(
year=request.form["year"],
mat_created=float(request.form.get("mat_credit_created", 0)),
opening_balance=float(request.form.get("opening_balance", 0)),
mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Created via ao"
)
LogHelper.log_action("AO record updated successfully!", data)
flash("AO record updated successfully!", "success") flash("AO record updated successfully!", "success")
return redirect(url_for('display_ao')) return redirect(url_for('display_ao'))
@@ -226,6 +259,7 @@ def delete_ao(id):
ao = AOHandler() ao = AOHandler()
ao.delete_ao_by_id(id=id) ao.delete_ao_by_id(id=id)
ao.close() ao.close()
LogHelper.log_action("AO deleted successfully!", id)
flash("AO deleted successfully!", "success") flash("AO deleted successfully!", "success")
return redirect(url_for('display_ao')) return redirect(url_for('display_ao'))
@@ -266,6 +300,7 @@ def add_cit():
mat_utilized=float(request.form.get("mat_credit_utilized", 0)), mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Created via cit" remarks="Created via cit"
) )
LogHelper.log_action("CIT record added successfully!", "")
flash("CIT record added successfully!", "success") flash("CIT record added successfully!", "success")
return redirect(url_for('display_cit')) return redirect(url_for('display_cit'))
@@ -278,6 +313,7 @@ def delete_cit(id):
cit = CITHandler() cit = CITHandler()
cit.delete_cit(id) cit.delete_cit(id)
cit.close() cit.close()
LogHelper.log_action("CIT record deleted successfully!", id)
flash("CIT record deleted successfully!", "success") flash("CIT record deleted successfully!", "success")
return redirect(url_for('display_cit')) return redirect(url_for('display_cit'))
@@ -296,6 +332,16 @@ def update_cit(id):
data = {k: request.form.get(k, 0) for k in request.form} data = {k: request.form.get(k, 0) for k in request.form}
cit.update_cit(id, data) cit.update_cit(id, data)
cit.close() cit.close()
mat = MatCreditHandler()
# AUTO SAVE MAT FROM ITR
mat.save_from_itr(
year=request.form["year"],
mat_created=float(request.form.get("mat_credit_created", 0)),
opening_balance=float(request.form.get("opening_balance", 0)),
mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Updated via cit"
)
LogHelper.log_action("CIT record updated successfully!", data)
return redirect(url_for('display_cit')) return redirect(url_for('display_cit'))
cit.close() cit.close()
@@ -336,9 +382,9 @@ def add_itat():
mat_created=float(request.form.get("mat_credit_created", 0)), mat_created=float(request.form.get("mat_credit_created", 0)),
opening_balance=float(request.form.get("opening_balance", 0)), opening_balance=float(request.form.get("opening_balance", 0)),
mat_utilized=float(request.form.get("mat_credit_utilized", 0)), mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Created via ITR" remarks="Created via ITAT"
) )
LogHelper.log_action("ITAT record added successfully!", data)
flash("ITAT record added successfully!", "success") flash("ITAT record added successfully!", "success")
return redirect(url_for('display_itat')) return redirect(url_for('display_itat'))
@@ -358,6 +404,16 @@ def update_itat(id):
if request.method == 'POST': if request.method == 'POST':
itat.update_itat(id, request.form) itat.update_itat(id, request.form)
itat.close() itat.close()
mat = MatCreditHandler()
mat.save_from_itr(
year=request.form["year"],
mat_created=float(request.form.get("mat_credit_created", 0)),
opening_balance=float(request.form.get("opening_balance", 0)),
mat_utilized=float(request.form.get("mat_credit_utilized", 0)),
remarks="Updated via ITAT"
)
LogHelper.log_action("ITAT Record Updated!", id)
flash("ITAT Record Updated!", "success") flash("ITAT Record Updated!", "success")
return redirect(url_for('display_itat')) return redirect(url_for('display_itat'))
@@ -370,6 +426,7 @@ def update_itat(id):
def delete_itat(id): def delete_itat(id):
itat = ITATHandler() itat = ITATHandler()
itat.delete_itat_by_id(id) itat.delete_itat_by_id(id)
LogHelper.log_action("itat record of by id:", id)
itat.close() itat.close()
flash("ITAT Record Deleted!", "success") flash("ITAT Record Deleted!", "success")
return redirect(url_for('display_itat')) return redirect(url_for('display_itat'))
@@ -398,7 +455,7 @@ def itr_report():
if output is None: if output is None:
return "No records found for the selected year." return "No records found for the selected year."
LogHelper.log_action("itr report download", selected_year)
return send_file( return send_file(
output, output,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
@@ -425,7 +482,7 @@ def ao_report():
if output is None: if output is None:
return "No records found for the selected year." return "No records found for the selected year."
LogHelper.log_action("ao report download", selected_year)
return send_file( return send_file(
output, output,
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
@@ -453,7 +510,7 @@ def cit_report():
if output is None: if output is None:
return "No records found for the selected year." return "No records found for the selected year."
LogHelper.log_action("cit report download", selected_year)
return send_file( return send_file(
output, output,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
@@ -482,7 +539,7 @@ def itat_report():
if output is None: if output is None:
return "No records found for the selected year." return "No records found for the selected year."
LogHelper.log_action("itat report download", selected_year)
return send_file( return send_file(
output, output,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
@@ -512,7 +569,7 @@ def download_summary():
return "Year parameter is required", 400 return "Year parameter is required", 400
docHandler = DocumentHandler() docHandler = DocumentHandler()
# reuse your existing Summary_report method LogHelper.log_action("/summary/download | download summary sheet !",year_raw)
return docHandler.Summary_report(request=request) return docHandler.Summary_report(request=request)
@@ -564,6 +621,7 @@ def mat_credit():
utilization_map.setdefault( utilization_map.setdefault(
u["mat_credit_id"], {} u["mat_credit_id"], {}
)[u["utilized_year"]] = u["utilized_amount"] )[u["utilized_year"]] = u["utilized_amount"]
LogHelper.log_action("/mat_credit| Save mat credit !",all_years)
return render_template( return render_template(
"mat_credit.html", "mat_credit.html",
@@ -579,6 +637,7 @@ def save_mat_row():
mat = MatCreditHandler() mat = MatCreditHandler()
try: try:
mat.save_single(request.json) mat.save_single(request.json)
LogHelper.log_action("/save_mat_row", "Save Mat row!")
return jsonify({"message": "Row saved successfully"}) return jsonify({"message": "Row saved successfully"})
except Exception as e: except Exception as e:
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
@@ -602,6 +661,30 @@ def summary_preview_route():
# except Exception as e: # except Exception as e:
# return jsonify({"error": str(e)}), 500 # return jsonify({"error": str(e)}), 500
@app.route("/view_logs", methods=["GET", "POST"])
@auth.login_required
def view_logs():
secret = os.getenv("LOG_VIEW_SECRET")
if request.method == "POST":
entered = request.form.get("secret")
if entered != secret:
flash("Invalid secret!", "danger")
return render_template("view_logs_auth.html")
try:
with open("logs/app.log", "r") as f:
logs = f.readlines()
except FileNotFoundError:
logs = ["Log file not found"]
return render_template("view_logs.html", logs=logs)
return render_template("view_logs_auth.html")
# run server # run server
if __name__ == '__main__': if __name__ == '__main__':
app.run( app.run(

View File

@@ -13,3 +13,5 @@ xlrd==2.0.1
gunicorn==21.2.0 gunicorn==21.2.0
XlsxWriter==3.2.0 XlsxWriter==3.2.0
ldap3

28
templates/view_logs.html Normal file
View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
background: black;
color: #00ff00;
font-family: monospace;
}
.log-box {
white-space: pre-wrap;
height: 90vh;
overflow-y: scroll;
}
</style>
</head>
<body>
<h2>Application Logs</h2>
<div class="log-box">{% for line in logs %} {{ line }} {% endfor %}</div>
</body>
</html>

View File

@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>View Logs - Authorization</title>
<style>
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
background: linear-gradient(135deg, #0d47a1, #1976d2);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background: #ffffff;
padding: 40px;
width: 350px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
text-align: center;
}
h2 {
margin-bottom: 25px;
color: #0d47a1;
}
input[type="password"] {
width: 100%;
padding: 12px;
margin-bottom: 20px;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 14px;
outline: none;
transition: border 0.3s;
}
input[type="password"]:focus {
border: 1px solid #1976d2;
}
button {
width: 100%;
padding: 12px;
background-color: #1976d2;
color: white;
border: none;
border-radius: 6px;
font-size: 15px;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background-color: #0d47a1;
}
.flash-message {
margin-top: 15px;
font-size: 14px;
color: red;
}
</style>
</head>
<body>
<div class="container">
<h2>Enter Secret to View Logs</h2>
<form method="POST">
<input type="password" name="secret" placeholder="Enter Secret Password" required>
<button type="submit">Open Logs</button>
</form>
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="flash-message">{{ message }}</div>
{% endfor %}
{% endwith %}
</div>
</body>
</html>