aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-09-08 15:56:06 -0700
committerPinapelz <yukais@pinapelz.com>2025-09-08 16:21:57 -0700
commitf155194356a8231b0c190d7276f203aca49ff028 (patch)
tree20c4e7ecb633ce2b70ca65a7840f29901d10d2e6 /api
parent6ad0c786a3e53f437e24ae7eb92e69648ca8751c (diff)
redirect root page to new demo
Diffstat (limited to 'api')
-rw-r--r--api/app.py137
-rw-r--r--api/database.py124
-rw-r--r--api/static/index.css113
-rw-r--r--api/static/index.js114
-rw-r--r--api/static/server_auth.js100
-rw-r--r--api/templates/index.html39
-rw-r--r--api/templates/server_auth.html22
7 files changed, 128 insertions, 521 deletions
diff --git a/api/app.py b/api/app.py
index 5074c36..3bb8f95 100644
--- a/api/app.py
+++ b/api/app.py
@@ -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
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage