diff options
| author | Pinapelz <yukais@pinapelz.com> | 2025-09-08 15:56:06 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2025-09-08 16:21:57 -0700 |
| commit | f155194356a8231b0c190d7276f203aca49ff028 (patch) | |
| tree | 20c4e7ecb633ce2b70ca65a7840f29901d10d2e6 /api | |
| parent | 6ad0c786a3e53f437e24ae7eb92e69648ca8751c (diff) | |
redirect root page to new demo
Diffstat (limited to 'api')
| -rw-r--r-- | api/app.py | 137 | ||||
| -rw-r--r-- | api/database.py | 124 | ||||
| -rw-r--r-- | api/static/index.css | 113 | ||||
| -rw-r--r-- | api/static/index.js | 114 | ||||
| -rw-r--r-- | api/static/server_auth.js | 100 | ||||
| -rw-r--r-- | api/templates/index.html | 39 | ||||
| -rw-r--r-- | api/templates/server_auth.html | 22 |
7 files changed, 128 insertions, 521 deletions
@@ -1,7 +1,6 @@ -from flask import Flask, render_template, jsonify, request +from flask import Flask, render_template, jsonify, request, redirect from flask_cors import CORS -import psycopg2 -from psycopg2 import Error +from .database import PostgresHandler import os import secrets from dotenv import load_dotenv @@ -11,130 +10,6 @@ load_dotenv() app = Flask(__name__) CORS(app) -class PostgresHandler: - def __init__(self, username: str, password: str, host_name: str, port: int, database: str): - db_params = { - "dbname": database, - "user": username, - "password": password, - "host": host_name, - "port": port - } - self._connection = psycopg2.connect(**db_params) - print("Handler Success") - - - def create_table(self, name: str, column: str): - cursor = self._connection.cursor() - cursor.execute(f"CREATE TABLE IF NOT EXISTS {name} ({column})") - self._connection.commit() - cursor.close() - - def clear_table(self, name: str): - cursor = self._connection.cursor() - cursor.execute(f"DELETE FROM {name}") - self._connection.commit() - cursor.close() - - def check_row_exists(self, table_name: str, column_name: str, value: str): - cursor = self._connection.cursor() - query = f"SELECT 1 FROM {table_name} WHERE {column_name} = %s" - cursor.execute(query, (value,)) - result = cursor.fetchone() - cursor.close() - - if result is not None: - return True - else: - return False - - def insert_row(self, table_name, column, data): - try: - cursor = self._connection.cursor() - placeholders = ', '.join(['%s'] * len(data)) - query = f"INSERT INTO {table_name} ({column}) VALUES ({placeholders})" - cursor.execute(query, data) - self._connection.commit() - print("Data Inserted:", data) - except Error as err: - self._connection.rollback() - print("Error inserting data") - print(err) - if "duplicate key" not in str(err).lower(): - return False - return True - - def get_rows(self, table_name: str, column: str, value: str): - try: - cursor = self._connection.cursor() - query = f"SELECT * FROM {table_name} WHERE {column} = %s" - cursor.execute(query, (value,)) - result = cursor.fetchall() - return result - except Error as e: - self._connection.rollback() - print(f"Failed to fetch row from {table_name} WHERE {column} is {value}") - print(e) - return False - - def get_random_row(self, table_name: str, count: int, condition: str = None): - if condition is None: - condition = "1 = 1" - try: - cursor = self._connection.cursor() - query = f"SELECT * FROM {table_name} WHERE {condition} ORDER BY RANDOM() LIMIT {str(count)}" - cursor.execute(query) - result = cursor.fetchall() - return result - except Error as e: - self._connection.rollback() - print(f"Failed to select random rows from {table_name}") - print(e) - return False - - def check_health(self): - cursor = self._connection.cursor() - cursor.execute("SELECT 1") - result = cursor.fetchone() - cursor.close() - if result is not None: - return True - else: - return False - - def delete_row(self, table_name: str, column: str, value: str): - try: - cursor = self._connection.cursor() - query = f"DELETE FROM {table_name} WHERE {column} = %s" - cursor.execute(query, (value,)) - self._connection.commit() - print("Data Deleted:", value) - except Error as e: - self._connection.rollback() - print(f"Failed to delete row from {table_name} WHERE {column} is {value}") - print(e) - return False - return True - - def get_distinct_col(self, table_name: str, column: str): - try: - cursor = self._connection.cursor() - query = f"SELECT DISTINCT {column} FROM {table_name}" - cursor.execute(query) - result = cursor.fetchall() - cursor.close() - return [row[0] for row in result] - except Error as e: - self._connection.rollback() - print(f"Failed to get unique values from {column} in {table_name}") - print(e) - return False - - - - def close_connection(self): - self._connection.close() - def create_database_connection(): """ Creates a database connection using the environment variables @@ -154,11 +29,7 @@ initialize_auth_database() @app.route('/') def index_demo(): - return render_template('index.html') - -@app.route('/server_auth') -def server_side_auth_demo(): - return render_template('server_auth.html') + return redirect("https://captcha.moekyun.me", code=302) # Demo Site @app.route('/api/list_orgs') def get_available_orgs(): @@ -239,7 +110,7 @@ def verify_answers(): def clear_sessions(): auth = request.headers.get("Authorization") cron_secret = os.environ.get("CRON_SECRET") - print("Recieved Request to Clear Session: Checking if " + auth + " matches " + cron_secret) + print("Recieved Request to Clear Session: Checking if " + str(auth) + " matches " + str(cron_secret)) if auth.strip() != cron_secret.strip(): return jsonify({"error": "Unauthorized"}), 401 server = create_database_connection() diff --git a/api/database.py b/api/database.py new file mode 100644 index 0000000..14ac611 --- /dev/null +++ b/api/database.py @@ -0,0 +1,124 @@ +import psycopg2 +from psycopg2 import Error + +class PostgresHandler: + def __init__(self, username: str, password: str, host_name: str, port: int, database: str): + db_params = { + "dbname": database, + "user": username, + "password": password, + "host": host_name, + "port": port + } + self._connection = psycopg2.connect(**db_params) + print("Handler Success") + + + def create_table(self, name: str, column: str): + cursor = self._connection.cursor() + cursor.execute(f"CREATE TABLE IF NOT EXISTS {name} ({column})") + self._connection.commit() + cursor.close() + + def clear_table(self, name: str): + cursor = self._connection.cursor() + cursor.execute(f"DELETE FROM {name}") + self._connection.commit() + cursor.close() + + def check_row_exists(self, table_name: str, column_name: str, value: str): + cursor = self._connection.cursor() + query = f"SELECT 1 FROM {table_name} WHERE {column_name} = %s" + cursor.execute(query, (value,)) + result = cursor.fetchone() + cursor.close() + + if result is not None: + return True + else: + return False + + def insert_row(self, table_name, column, data): + try: + cursor = self._connection.cursor() + placeholders = ', '.join(['%s'] * len(data)) + query = f"INSERT INTO {table_name} ({column}) VALUES ({placeholders})" + cursor.execute(query, data) + self._connection.commit() + print("Data Inserted:", data) + except Error as err: + self._connection.rollback() + print("Error inserting data") + print(err) + if "duplicate key" not in str(err).lower(): + return False + return True + + def get_rows(self, table_name: str, column: str, value: str): + try: + cursor = self._connection.cursor() + query = f"SELECT * FROM {table_name} WHERE {column} = %s" + cursor.execute(query, (value,)) + result = cursor.fetchall() + return result + except Error as e: + self._connection.rollback() + print(f"Failed to fetch row from {table_name} WHERE {column} is {value}") + print(e) + return False + + def get_random_row(self, table_name: str, count: int, condition: str = None): + if condition is None: + condition = "1 = 1" + try: + cursor = self._connection.cursor() + query = f"SELECT * FROM {table_name} WHERE {condition} ORDER BY RANDOM() LIMIT {str(count)}" + cursor.execute(query) + result = cursor.fetchall() + return result + except Error as e: + self._connection.rollback() + print(f"Failed to select random rows from {table_name}") + print(e) + return False + + def check_health(self): + cursor = self._connection.cursor() + cursor.execute("SELECT 1") + result = cursor.fetchone() + cursor.close() + if result is not None: + return True + else: + return False + + def delete_row(self, table_name: str, column: str, value: str): + try: + cursor = self._connection.cursor() + query = f"DELETE FROM {table_name} WHERE {column} = %s" + cursor.execute(query, (value,)) + self._connection.commit() + print("Data Deleted:", value) + except Error as e: + self._connection.rollback() + print(f"Failed to delete row from {table_name} WHERE {column} is {value}") + print(e) + return False + return True + + def get_distinct_col(self, table_name: str, column: str): + try: + cursor = self._connection.cursor() + query = f"SELECT DISTINCT {column} FROM {table_name}" + cursor.execute(query) + result = cursor.fetchall() + cursor.close() + return [row[0] for row in result] + except Error as e: + self._connection.rollback() + print(f"Failed to get unique values from {column} in {table_name}") + print(e) + return False + + def close_connection(self): + self._connection.close() diff --git a/api/static/index.css b/api/static/index.css deleted file mode 100644 index 6499589..0000000 --- a/api/static/index.css +++ /dev/null @@ -1,113 +0,0 @@ -#recaptcha-container { - border-radius: 5px; - border: 1px solid #ccc; - padding: 20px; - width: 440px; - margin: 50px auto; - background-color: #f4f4f4; - } - - .recaptcha-header { - background-color: #0079c1; - color: #fff; - padding: 10px 15px; - border-radius: 5px; - font-weight: bold; - margin-bottom: 20px; - text-align: center; - } - - .recaptcha-row { - display: flex; - justify-content: space-between; - margin-bottom: 5px; - } - - .submit-btn { - display: block; - margin: 10px auto 20px; - /* This will center the button horizontally and give it a margin at the bottom */ - padding: 10px 15px; - background-color: #0079c1; - color: #fff; - border: none; - border-radius: 5px; - cursor: pointer; - transition: background-color 0.3s ease; - } - - .submit-btn:hover { - background-color: #005a91; - } - - .recaptcha-image { - width: 90px; - height: 90px; - margin: 2px; - border: 1px solid transparent; - cursor: pointer; - transition: transform 0.3s ease, border-color 0.3s ease; - background-size: cover; - background-position: center; - /* Ensure the image is centered */ - position: relative; - overflow: hidden; - } - - .regenerate-btn { - display: block; - margin: 10px auto; - /* This will center the button horizontally */ - } - - .recaptcha-image > svg { - display: none; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 30px; - height: 30px; - } - - .selected { - border-color: blue; - transform: scale(0.8); - } - - .selected > svg { - display: block; - } - - #answer-table-container { - margin-top: 20px; - border: 1px solid #ddd; - width: 100%; - max-width: 800px; - margin-left: auto; - margin-right: auto; -} - -#answer-table { - width: 100%; - border-collapse: collapse; -} - -#answer-table th, #answer-table td { - border: 1px solid #ddd; - padding: 8px 12px; - text-align: left; -} - -#answer-table th { - background-color: #f2f2f2; -} - -#answer-table tbody tr:hover { - background-color: #eaeaea; -} - -#answer-table td img { - max-width: 100%; - height: auto; -} diff --git a/api/static/index.js b/api/static/index.js deleted file mode 100644 index 1b5e85e..0000000 --- a/api/static/index.js +++ /dev/null @@ -1,114 +0,0 @@ -let captchaData; - -document.addEventListener("DOMContentLoaded", function () { - fetchCaptchaImages(); -}); - -function toggleSelection(element) { - element.classList.toggle("selected"); -} - -function populateCaptcha(data) { - const container = document.getElementById("recaptcha-container"); - const title = document.getElementById("recaptcha-title"); - title.innerHTML = data.title; - const oldRows = container.getElementsByClassName("recaptcha-row"); - while (oldRows.length > 0) { - oldRows[0].parentNode.removeChild(oldRows[0]); - } - - const images = data.questions; - - let rows = Math.ceil(images.length / 4); - let htmlContent = ""; - for (let r = 0; r < rows; r++) { - htmlContent += '<div class="recaptcha-row">'; - for (let i = 0; i < 4; i++) { - const index = r * 4 + i; - if (images[index]) { - htmlContent += ` - <div class="recaptcha-image" style="background-image: url('${images[index].image}');" onclick="toggleSelection(this)"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <path d="M20.285 2l-1.272.02L6.72 15.466l-5.644-5.58L0 12.352l7.218 7.15L24 2.083z"/> - </svg> - </div> - `; - } - } - htmlContent += "</div>"; - } - - title.insertAdjacentHTML("afterend", htmlContent); - captchaData = data; -} - -async function verifyCaptcha() { - const selectedImages = document.querySelectorAll(".recaptcha-image.selected"); - const answers = []; - const answerTableBody = document.querySelector("#answer-table tbody"); - const endMessageDiv = document.querySelector("#end-message"); - answerTableBody.innerHTML = ""; - - selectedImages.forEach((img) => { - const backgroundImageURL = img.style.backgroundImage; - const imageURL = backgroundImageURL.slice(5, backgroundImageURL.length - 2); - const question = captchaData.questions.find((q) => q.image === imageURL); - answers.push(question.id); - const row = document.createElement("tr"); - const imgCell = document.createElement("td"); - const idCell = document.createElement("td"); - const nameCell = document.createElement("td"); - imgCell.innerHTML = `<img src="${question.image}" alt="Selected Image" width="50">`; - idCell.textContent = question.answer; - nameCell.textContent = question.name; - row.appendChild(imgCell); - row.appendChild(nameCell); - row.appendChild(idCell); - answerTableBody.appendChild(row); - }); - num_correct = 0 - for (let i = 0; i < captchaData.questions.length; i++) { - if (captchaData.questions[i].answer == true) { - num_correct += 1 - } - } - endMessageDiv.textContent = `You selected ${selectedImages.length} image(s). There are ${num_correct} correct image(s).`; - - const captchaAnswer = { - session: captchaData.session, - answer: answers.join(","), - }; - try { - const response = await fetch("/api/verify", { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams(captchaAnswer).toString() // Convert JSON object to form-urlencoded data - }); - const data = await response.json(); - - if (data.success) { - endMessageDiv.textContent = "You have successfully completed the captcha!"; - } else { - fetchCaptchaImages(); - } - } catch (error) { - console.error("Error verifying captcha:", error); - } -} - - -const submitButton = document.querySelector(".submit-btn"); -submitButton.addEventListener("click", verifyCaptcha); - -function fetchCaptchaImages() { - fetch("/api/affiliation/Hololive") - .then((response) => response.json()) - .then((data) => { - populateCaptcha(data); - }) - .catch((error) => { - console.error("There was an error fetching the captcha:", error); - }); -}
\ No newline at end of file diff --git a/api/static/server_auth.js b/api/static/server_auth.js deleted file mode 100644 index 34fd4fd..0000000 --- a/api/static/server_auth.js +++ /dev/null @@ -1,100 +0,0 @@ -let captchaData; - -document.addEventListener("DOMContentLoaded", function () { - fetchCaptchaImages(); -}); - -function toggleSelection(element) { - element.classList.toggle("selected"); -} - -function populateCaptcha(data) { - const container = document.getElementById("recaptcha-container"); - const title = document.getElementById("recaptcha-title"); - title.innerHTML = data.title; - const oldRows = container.getElementsByClassName("recaptcha-row"); - while (oldRows.length > 0) { - oldRows[0].parentNode.removeChild(oldRows[0]); - } - - const images = data.questions; - - let rows = Math.ceil(images.length / 4); - let htmlContent = ""; - for (let r = 0; r < rows; r++) { - htmlContent += '<div class="recaptcha-row">'; - for (let i = 0; i < 4; i++) { - const index = r * 4 + i; - if (images[index]) { - htmlContent += ` - <div class="recaptcha-image" style="background-image: url('${images[index].image}');" onclick="toggleSelection(this)"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <path d="M20.285 2l-1.272.02L6.72 15.466l-5.644-5.58L0 12.352l7.218 7.15L24 2.083z"/> - </svg> - </div> - `; - } - } - htmlContent += "</div>"; - } - - title.insertAdjacentHTML("afterend", htmlContent); - captchaData = data; - session_id = data.session_id; -} - -async function verifyCaptcha() { - const selectedImages = document.querySelectorAll( - ".recaptcha-image.selected", - ); - const answers = []; - selectedImages.forEach((img) => { - const backgroundImageURL = img.style.backgroundImage; - const imageURL = backgroundImageURL.slice( - 5, - backgroundImageURL.length - 2, - ); - answers.push( - captchaData.questions.find( - (question) => question.image === imageURL, - ).id, - ); - }); - const captchaAnswer = { - session: captchaData.session, - answer: answers.join(","), - }; - try { - const response = await fetch("/api/verify", { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams(captchaAnswer).toString(), // Convert JSON object to form-urlencoded data - }); - const data = await response.json(); - - if (data.success) { - alert("Captcha verification successful!"); - } else { - alert("Sorry you got some wrong. Please try again."); - fetchCaptchaImages(); - } - } catch (error) { - console.error("Error verifying captcha:", error); - } -} - -const submitButton = document.querySelector(".submit-btn"); -submitButton.addEventListener("click", verifyCaptcha); - -function fetchCaptchaImages() { - fetch("/api/affiliation/Phase%20Connect?auth=server") - .then((response) => response.json()) - .then((data) => { - populateCaptcha(data); - }) - .catch((error) => { - console.error("There was an error fetching the captcha:", error); - }); -} diff --git a/api/templates/index.html b/api/templates/index.html deleted file mode 100644 index ea8199e..0000000 --- a/api/templates/index.html +++ /dev/null @@ -1,39 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='index.css') }}"> - <title>VTuber Captcha Demo</title> - </head> - - <body> - <div id="recaptcha-container"> - <div class="recaptcha-header" id="recaptcha-title"> - </div> - <button class="regenerate-btn" onclick="fetchCaptchaImages()"> - Regenerate Captcha - </button> - <button class="submit-btn">Submit</button> - </div> - <div id="end-message"> - <!-- Will be populated with JS -->> - </div> - <div id="answer-table-container"> - <table id="answer-table"> - <thead> - <tr> - <th>Image</th> - <th>Name</th> - <th>Answer (ID)</th> - </tr> - </thead> - <tbody> - <!-- Answers will be populated here --> - </tbody> - </table> - </div> - <p>Go to /server_auth to test out sever side authentications</p> - <script src="{{ url_for('static', filename='index.js') }}"></script> - </body> -</html>
\ No newline at end of file diff --git a/api/templates/server_auth.html b/api/templates/server_auth.html deleted file mode 100644 index 1f4cb33..0000000 --- a/api/templates/server_auth.html +++ /dev/null @@ -1,22 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='index.css') }}"> - <title>VTuber Captcha Server Auth</title> - </head> - - <body> - <div id="recaptcha-container"> - <div class="recaptcha-header" id="recaptcha-title"> - </div> - <button class="regenerate-btn" onclick="fetchCaptchaImages()"> - Regenerate Captcha - </button> - <button class="submit-btn">Submit</button> - </div> - <script src="{{ url_for('static', filename='server_auth.js') }}"></script> - </body> - -</html>
\ No newline at end of file |
