0vergeared commited on
Commit
0a96fb2
·
verified ·
1 Parent(s): 178fce9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +184 -0
app.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template_string, redirect, session
2
+ from flask_limiter import Limiter
3
+ from flask_limiter.util import get_remote_address
4
+ from datetime import datetime, timedelta
5
+ import secrets
6
+ import sqlite3
7
+ import os
8
+
9
+ app = Flask(__name__)
10
+ app.secret_key = os.environ.get("FLASK_SECRET_KEY", "supersecret") # Needed for session
11
+
12
+ # --- Rate limiting ---
13
+ limiter = Limiter(get_remote_address, app=app, default_limits=["10 per minute"])
14
+
15
+ # --- Configurable credentials ---
16
+ API_KEY = os.environ.get("OTP_API_KEY", "secretapikey123")
17
+ ADMIN_USER = os.environ.get("ADMIN_USER", "admin")
18
+ ADMIN_PASS = os.environ.get("ADMIN_PASS", "admin123")
19
+
20
+ # --- DB init ---
21
+ def init_db():
22
+ conn = sqlite3.connect("database.db")
23
+ c = conn.cursor()
24
+ c.execute('''CREATE TABLE IF NOT EXISTS otp (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ code TEXT UNIQUE,
27
+ created_at TEXT,
28
+ expires_at TEXT,
29
+ used INTEGER DEFAULT 0,
30
+ used_at TEXT,
31
+ result TEXT,
32
+ ip TEXT
33
+ )''')
34
+ conn.commit()
35
+ conn.close()
36
+
37
+ init_db()
38
+
39
+ def generate_otp():
40
+ return str(secrets.randbelow(900000) + 100000)
41
+
42
+ def insert_otp(code, created_at, expires_at, result="generated", ip=""):
43
+ conn = sqlite3.connect("database.db")
44
+ c = conn.cursor()
45
+ c.execute("INSERT INTO otp (code, created_at, expires_at, result, ip) VALUES (?, ?, ?, ?, ?)",
46
+ (code, created_at, expires_at, result, ip))
47
+ conn.commit()
48
+ conn.close()
49
+
50
+ def mark_otp_used(code, result, ip):
51
+ now = datetime.utcnow().isoformat()
52
+ conn = sqlite3.connect("database.db")
53
+ c = conn.cursor()
54
+ c.execute("UPDATE otp SET used = 1, used_at = ?, result = ?, ip = ? WHERE code = ?",
55
+ (now, result, ip, code))
56
+ conn.commit()
57
+ conn.close()
58
+
59
+ # --- Routes ---
60
+
61
+ @app.route("/")
62
+ def home():
63
+ return "✅ OTP API is running."
64
+
65
+ @app.route("/generate-otp", methods=["POST"])
66
+ @limiter.limit("3 per minute")
67
+ def generate_api():
68
+ api_key = request.headers.get("X-API-Key")
69
+ if api_key != API_KEY:
70
+ return jsonify({"error": "Unauthorized"}), 401
71
+
72
+ otp = generate_otp()
73
+ now = datetime.utcnow()
74
+ expires = now + timedelta(minutes=5)
75
+ insert_otp(otp, now.isoformat(), expires.isoformat(), ip=request.remote_addr)
76
+
77
+ return jsonify({"otp": otp, "expires_at": expires.isoformat()})
78
+
79
+ @app.route("/verify-otp", methods=["POST"])
80
+ @limiter.limit("10 per minute")
81
+ def verify():
82
+ data = request.json
83
+ otp = data.get("otp")
84
+ if not otp:
85
+ return jsonify({"success": False, "message": "No OTP provided"}), 400
86
+
87
+ now = datetime.utcnow()
88
+ conn = sqlite3.connect("database.db")
89
+ c = conn.cursor()
90
+ c.execute("SELECT used, expires_at FROM otp WHERE code = ?", (otp,))
91
+ row = c.fetchone()
92
+
93
+ if not row:
94
+ insert_otp(otp, now.isoformat(), now.isoformat(), result="invalid", ip=request.remote_addr)
95
+ return jsonify({"success": False, "message": "Invalid OTP"}), 401
96
+
97
+ used, expires_at = row
98
+ expires_at = datetime.fromisoformat(expires_at)
99
+
100
+ if used:
101
+ return jsonify({"success": False, "message": "OTP already used"}), 403
102
+ if now > expires_at:
103
+ mark_otp_used(otp, result="expired", ip=request.remote_addr)
104
+ return jsonify({"success": False, "message": "OTP expired"}), 403
105
+
106
+ mark_otp_used(otp, result="success", ip=request.remote_addr)
107
+ return jsonify({"success": True})
108
+
109
+ @app.route("/export-log")
110
+ def export_log():
111
+ api_key = request.headers.get("X-API-Key")
112
+ if api_key != API_KEY:
113
+ return jsonify({"error": "Unauthorized"}), 401
114
+
115
+ conn = sqlite3.connect("database.db")
116
+ c = conn.cursor()
117
+ c.execute("SELECT * FROM otp")
118
+ rows = c.fetchall()
119
+ conn.close()
120
+
121
+ return jsonify({
122
+ "columns": ["id", "code", "created_at", "expires_at", "used", "used_at", "result", "ip"],
123
+ "data": rows
124
+ })
125
+
126
+ # --- UI with login ---
127
+
128
+ @app.route("/login", methods=["GET", "POST"])
129
+ def login():
130
+ error = ""
131
+ if request.method == "POST":
132
+ username = request.form.get("username")
133
+ password = request.form.get("password")
134
+ if username == ADMIN_USER and password == ADMIN_PASS:
135
+ session["logged_in"] = True
136
+ return redirect("/generate")
137
+ else:
138
+ error = "❌ Invalid login"
139
+ return render_template_string('''
140
+ <h2>🔐 Admin Login</h2>
141
+ <form method="post">
142
+ <input name="username" placeholder="Username" required><br><br>
143
+ <input name="password" type="password" placeholder="Password" required><br><br>
144
+ <input type="submit" value="Login">
145
+ </form>
146
+ <p>{{ error }}</p>
147
+ ''', error=error)
148
+
149
+ @app.route("/generate", methods=["GET", "POST"])
150
+ def generate_page():
151
+ if not session.get("logged_in"):
152
+ return redirect("/login")
153
+
154
+ message = ""
155
+ otp_data = None
156
+ if request.method == "POST":
157
+ otp = generate_otp()
158
+ now = datetime.utcnow()
159
+ expires = now + timedelta(minutes=5)
160
+ insert_otp(otp, now.isoformat(), expires.isoformat(), ip=request.remote_addr)
161
+ message = "✅ OTP generated!"
162
+ otp_data = {"otp": otp, "expires_at": expires.isoformat()}
163
+
164
+ return render_template_string("""
165
+ <h2>🎯 Generate OTP</h2>
166
+ <form method="post">
167
+ <input type="submit" value="Generate New OTP">
168
+ </form>
169
+ {% if message %}
170
+ <p><strong>{{ message }}</strong></p>
171
+ {% endif %}
172
+ {% if otp_data %}
173
+ <div>
174
+ <p><strong>OTP:</strong> {{ otp_data['otp'] }}</p>
175
+ <p><strong>Expires At:</strong> {{ otp_data['expires_at'] }}</p>
176
+ </div>
177
+ {% endif %}
178
+ <p><a href="/logout">Logout</a></p>
179
+ """, message=message, otp_data=otp_data)
180
+
181
+ @app.route("/logout")
182
+ def logout():
183
+ session.pop("logged_in", None)
184
+ return redirect("/login")