0vergeared commited on
Commit
8e8453e
·
verified ·
1 Parent(s): 1503298

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +114 -133
server.py CHANGED
@@ -1,58 +1,24 @@
1
  import os
 
2
  import sqlite3
3
- from flask import Flask, request, jsonify, render_template_string, redirect, url_for, session
 
4
  from flask_limiter import Limiter
5
  from flask_limiter.util import get_remote_address
6
- from datetime import datetime, timedelta
7
- import random
8
- import string
9
- import threading
10
 
11
- # Configs from secrets
12
- ADMIN_USER = os.environ.get("ADMIN_USER", "admin")
13
- ADMIN_PASS = os.environ.get("ADMIN_PASS", "Welcome123")
14
- API_KEY = os.environ.get("OTP_API_KEY", "IDONTKNOWWHATIMDOING")
15
- FLASK_SECRET_KEY = os.environ.get("FLASK_SECRET_KEY", "IDONTKNOWWHATISTHIS")
16
 
17
  app = Flask(__name__)
18
- app.secret_key = FLASK_SECRET_KEY
19
-
20
- limiter = Limiter(get_remote_address, app=app)
21
-
22
- import pathlib
23
- # Ensure the /data folder exists
24
- pathlib.Path("/data").mkdir(parents=True, exist_ok=True)
25
- DATABASE = "/data/database.db"
26
-
27
- # HTML template
28
- template = """
29
- <!DOCTYPE html>
30
- <html>
31
- <head>
32
- <title>OTP Generator</title>
33
- </head>
34
- <body>
35
- {% if not session.get("logged_in") %}
36
- <h2>Login</h2>
37
- <form method="POST">
38
- <input name="username" placeholder="Username"><br>
39
- <input name="password" placeholder="Password" type="password"><br>
40
- <button type="submit">Login</button>
41
- </form>
42
- {% else %}
43
- <h2>Generate OTP</h2>
44
- <form method="POST">
45
- <button type="submit">Generate OTP</button>
46
- </form>
47
- {% if otp %}
48
- <p><strong>OTP:</strong> {{ otp }}</p>
49
- <p>Expires At: {{ expires_at }}</p>
50
- {% endif %}
51
- <p><a href="{{ url_for('logout') }}">Logout</a></p>
52
- {% endif %}
53
- </body>
54
- </html>
55
- """
56
 
57
  # Initialize DB
58
  def init_db():
@@ -71,106 +37,121 @@ def init_db():
71
 
72
  init_db()
73
 
74
- # Helper
75
- def generate_otp(length=6):
76
- return ''.join(random.choices(string.digits, k=length))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- # Web login
79
  @app.route("/login", methods=["GET", "POST"])
80
  def login():
81
  if request.method == "POST":
82
  if request.form["username"] == ADMIN_USER and request.form["password"] == ADMIN_PASS:
83
- session["logged_in"] = True
84
- return redirect(url_for("generate_page"))
85
- return render_template_string(template)
86
-
87
- @app.route("/logout")
88
- def logout():
89
- session.pop("logged_in", None)
90
- return redirect(url_for("login"))
 
 
 
 
 
 
 
 
 
 
91
 
92
- # Web generate UI
93
  @app.route("/generate", methods=["GET", "POST"])
94
  def generate_page():
95
- if not session.get("logged_in"):
96
- return redirect(url_for("login"))
 
97
  otp = None
98
- expires_at = None
99
  if request.method == "POST":
100
  otp = generate_otp()
101
  now = datetime.utcnow()
102
- expires = now + timedelta(minutes=5)
103
- expires_at = expires.isoformat()
104
- conn = sqlite3.connect(DATABASE)
105
- c = conn.cursor()
106
- c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
107
- (otp, now.isoformat(), expires_at, request.remote_addr, "generated"))
108
- conn.commit()
109
- conn.close()
110
- return render_template_string(template, otp=otp, expires_at=expires_at)
111
 
112
- # API generate OTP
113
- @app.route("/generate-otp", methods=["POST"])
114
- @limiter.limit("5 per minute")
115
- def api_generate_otp():
116
- if request.headers.get("X-API-Key") != API_KEY:
117
- return jsonify({"error": "Unauthorized"}), 401
118
- otp = generate_otp()
119
- now = datetime.utcnow()
120
- expires = now + timedelta(minutes=5)
121
- conn = sqlite3.connect(DATABASE)
122
- c = conn.cursor()
123
- c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
124
- (otp, now.isoformat(), expires.isoformat(), request.remote_addr, "generated"))
125
- conn.commit()
126
- conn.close()
127
- return jsonify({"otp": otp, "expires_at": expires.isoformat()})
128
-
129
- # API verify OTP
130
- @app.route("/verify-otp", methods=["POST"])
131
- @limiter.limit("10 per minute")
132
- def verify_otp():
133
- data = request.get_json()
134
- otp_input = data.get("otp", "")
135
- now = datetime.utcnow()
136
- conn = sqlite3.connect(DATABASE)
137
- c = conn.cursor()
138
- c.execute("SELECT id, expires_at, used_at FROM otps WHERE otp=? AND status='generated'", (otp_input,))
139
- row = c.fetchone()
140
- if not row:
141
- return jsonify({"success": False, "message": "Invalid or already used OTP"}), 400
142
- otp_id, expires_at, used_at = row
143
- if datetime.fromisoformat(expires_at) < now:
144
- c.execute("UPDATE otps SET status='expired' WHERE id=?", (otp_id,))
145
- conn.commit()
146
- conn.close()
147
- return jsonify({"success": False, "message": "OTP expired"}), 400
148
- c.execute("UPDATE otps SET used_at=?, status='used' WHERE id=?", (now.isoformat(), otp_id))
149
- conn.commit()
150
- conn.close()
151
- return jsonify({"success": True, "message": "OTP valid"})
152
-
153
- # Audit log
154
- @app.route("/export-log")
155
- def export_log():
156
- conn = sqlite3.connect(DATABASE)
157
- c = conn.cursor()
158
- c.execute("SELECT * FROM otps ORDER BY id DESC")
159
- rows = c.fetchall()
160
- conn.close()
161
- return jsonify(rows)
162
-
163
- # Root
164
- @app.route("/")
165
- def home():
166
- return "OTP API is running"
167
-
168
- # Run in HF Space
169
- def run():
170
- app.run(host="0.0.0.0", port=7860)
171
 
172
- threading.Thread(target=run).start()
173
 
 
 
 
 
 
 
174
  @app.route("/debug")
175
  def debug():
176
  return f"Database exists: {os.path.exists(DATABASE)}"
 
 
 
 
1
  import os
2
+ import random
3
  import sqlite3
4
+ from datetime import datetime, timedelta
5
+ from flask import Flask, request, jsonify, redirect, session, render_template_string
6
  from flask_limiter import Limiter
7
  from flask_limiter.util import get_remote_address
 
 
 
 
8
 
9
+ # Environment variables
10
+ OTP_API_KEY = os.getenv("OTP_API_KEY", "default-api-key")
11
+ ADMIN_USER = os.getenv("ADMIN_USER", "admin")
12
+ ADMIN_PASS = os.getenv("ADMIN_PASS", "password")
13
+ SECRET_KEY = os.getenv("FLASK_SECRET_KEY", "supersecretkey")
14
 
15
  app = Flask(__name__)
16
+ app.secret_key = SECRET_KEY
17
+
18
+ limiter = Limiter(app, key_func=get_remote_address)
19
+
20
+ # Writable DB path
21
+ DATABASE = "/tmp/database.db"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  # Initialize DB
24
  def init_db():
 
37
 
38
  init_db()
39
 
40
+ def generate_otp():
41
+ return str(random.randint(100000, 999999))
42
+
43
+ @app.route("/generate-otp", methods=["POST"])
44
+ @limiter.limit("5/minute")
45
+ def api_generate():
46
+ if request.headers.get("X-API-Key") != OTP_API_KEY:
47
+ return jsonify({"error": "Unauthorized"}), 401
48
+
49
+ otp = generate_otp()
50
+ now = datetime.utcnow()
51
+ expires_at = now + timedelta(minutes=10)
52
+
53
+ with sqlite3.connect(DATABASE) as conn:
54
+ c = conn.cursor()
55
+ c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
56
+ (otp, now.isoformat(), expires_at.isoformat(), request.remote_addr, "generated"))
57
+ conn.commit()
58
+
59
+ return jsonify({"otp": otp, "expires_at": expires_at.isoformat()})
60
+
61
+ @app.route("/verify-otp", methods=["POST"])
62
+ @limiter.limit("10/minute")
63
+ def verify_otp():
64
+ data = request.get_json()
65
+ otp = data.get("otp")
66
+
67
+ with sqlite3.connect(DATABASE) as conn:
68
+ c = conn.cursor()
69
+ c.execute("SELECT id, expires_at, used_at, status FROM otps WHERE otp = ?", (otp,))
70
+ row = c.fetchone()
71
+
72
+ if not row:
73
+ return jsonify({"valid": False, "reason": "Invalid OTP"}), 400
74
+
75
+ otp_id, expires_at, used_at, status = row
76
+ now = datetime.utcnow()
77
+
78
+ if used_at:
79
+ return jsonify({"valid": False, "reason": "OTP already used"}), 400
80
+ if datetime.fromisoformat(expires_at) < now:
81
+ return jsonify({"valid": False, "reason": "OTP expired"}), 400
82
+
83
+ c.execute("UPDATE otps SET used_at = ?, status = ? WHERE id = ?",
84
+ (now.isoformat(), "used", otp_id))
85
+ conn.commit()
86
+
87
+ return jsonify({"valid": True})
88
+
89
+ # Admin login UI
90
+ LOGIN_TEMPLATE = """
91
+ <!DOCTYPE html><html><body>
92
+ <h2>Admin Login</h2>
93
+ <form method="post">
94
+ <input type="text" name="username" placeholder="Username" required><br>
95
+ <input type="password" name="password" placeholder="Password" required><br>
96
+ <button type="submit">Login</button>
97
+ </form>
98
+ </body></html>
99
+ """
100
 
 
101
  @app.route("/login", methods=["GET", "POST"])
102
  def login():
103
  if request.method == "POST":
104
  if request.form["username"] == ADMIN_USER and request.form["password"] == ADMIN_PASS:
105
+ session["admin"] = True
106
+ return redirect("/generate")
107
+ return "Invalid login", 403
108
+ return render_template_string(LOGIN_TEMPLATE)
109
+
110
+ # Admin OTP generation UI
111
+ GEN_TEMPLATE = """
112
+ <!DOCTYPE html><html><body>
113
+ <h2>Generate OTP</h2>
114
+ <form method="post">
115
+ <button type="submit">Generate OTP</button>
116
+ </form>
117
+ {% if otp %}
118
+ <p>OTP: <b>{{ otp }}</b> (expires at {{ expires }})</p>
119
+ {% endif %}
120
+ <a href="/logout">Logout</a>
121
+ </body></html>
122
+ """
123
 
 
124
  @app.route("/generate", methods=["GET", "POST"])
125
  def generate_page():
126
+ if not session.get("admin"):
127
+ return redirect("/login")
128
+
129
  otp = None
130
+ expires = None
131
  if request.method == "POST":
132
  otp = generate_otp()
133
  now = datetime.utcnow()
134
+ expires_at = now + timedelta(minutes=10)
 
 
 
 
 
 
 
 
135
 
136
+ with sqlite3.connect(DATABASE) as conn:
137
+ c = conn.cursor()
138
+ c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
139
+ (otp, now.isoformat(), expires_at.isoformat(), request.remote_addr, "generated"))
140
+ conn.commit()
141
+
142
+ expires = expires_at.isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
+ return render_template_string(GEN_TEMPLATE, otp=otp, expires=expires)
145
 
146
+ @app.route("/logout")
147
+ def logout():
148
+ session.clear()
149
+ return redirect("/login")
150
+
151
+ # Optional debug route
152
  @app.route("/debug")
153
  def debug():
154
  return f"Database exists: {os.path.exists(DATABASE)}"
155
+
156
+ if __name__ == "__main__":
157
+ app.run(host="0.0.0.0", port=7860)