changed repo structure
This commit is contained in:
11
2025/catthegrey/ezpz/dist-oops/app/Dockerfile
Normal file
11
2025/catthegrey/ezpz/dist-oops/app/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM python:3.13-slim
|
||||
# Copy files
|
||||
COPY . .
|
||||
|
||||
# Install requirements
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["gunicorn", "-w", "8", "--bind", "0.0.0.0:5000", "--log-level", "debug", "app:app"]
|
||||
103
2025/catthegrey/ezpz/dist-oops/app/app.py
Normal file
103
2025/catthegrey/ezpz/dist-oops/app/app.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, jsonify
|
||||
import sqlite3
|
||||
import string
|
||||
import random
|
||||
import os
|
||||
import socket
|
||||
|
||||
ADMIN_HOST = "web-oops-admin"
|
||||
ADMIN_PORT = 3001
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
|
||||
# Database setup
|
||||
def init_db():
|
||||
conn = sqlite3.connect('urls.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS urls
|
||||
(id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
original_url TEXT NOT NULL,
|
||||
short_code TEXT UNIQUE NOT NULL,
|
||||
clicks INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def generate_short_code(length=6):
|
||||
characters = string.ascii_letters + string.digits
|
||||
return ''.join(random.choice(characters) for _ in range(length))
|
||||
|
||||
def get_db_connection():
|
||||
conn = sqlite3.connect('urls.db')
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
message = None
|
||||
shortened_url = None
|
||||
|
||||
if request.method == 'POST':
|
||||
original_url = request.form['original_url']
|
||||
|
||||
|
||||
url = original_url.lower()
|
||||
while "script" in url:
|
||||
url = url.replace("script", "")
|
||||
|
||||
# Generate unique short code
|
||||
while True:
|
||||
short_code = generate_short_code()
|
||||
conn = get_db_connection()
|
||||
existing = conn.execute('SELECT id FROM urls WHERE short_code = ?',
|
||||
(short_code,)).fetchone()
|
||||
if not existing:
|
||||
break
|
||||
conn.close()
|
||||
|
||||
# Save to database
|
||||
conn = get_db_connection()
|
||||
conn.execute('INSERT INTO urls (original_url, short_code) VALUES (?, ?)',
|
||||
(original_url, short_code))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
shortened_url = request.host_url + short_code
|
||||
message = "URL shortened successfully!"
|
||||
|
||||
return render_template("index.html",
|
||||
message=message,
|
||||
shortened_url=shortened_url)
|
||||
|
||||
@app.post('/report')
|
||||
def report():
|
||||
submit_id = request.form["submit_id"]
|
||||
submit_id = submit_id.split("/")[-1]
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((ADMIN_HOST, ADMIN_PORT))
|
||||
s.sendall(submit_id.encode())
|
||||
s.close()
|
||||
return render_template("index.html",
|
||||
report_message="Reported successfully.")
|
||||
|
||||
|
||||
@app.route('/<short_code>')
|
||||
def redirect_url(short_code):
|
||||
conn = get_db_connection()
|
||||
url_data = conn.execute('SELECT original_url FROM urls WHERE short_code = ?',
|
||||
(short_code,)).fetchone()
|
||||
|
||||
if url_data:
|
||||
# Increment click counter
|
||||
conn.execute('UPDATE urls SET clicks = clicks + 1 WHERE short_code = ?',
|
||||
(short_code,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return render_template("redir.html", url=url_data["original_url"]), 200
|
||||
else:
|
||||
conn.close()
|
||||
return render_template("not_found.html"), 404
|
||||
|
||||
init_db()
|
||||
2
2025/catthegrey/ezpz/dist-oops/app/requirements.txt
Normal file
2
2025/catthegrey/ezpz/dist-oops/app/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask
|
||||
gunicorn
|
||||
165
2025/catthegrey/ezpz/dist-oops/app/templates/index.html
Normal file
165
2025/catthegrey/ezpz/dist-oops/app/templates/index.html
Normal file
@@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>URL Shortener</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
}
|
||||
.form-control {
|
||||
border-radius: 25px;
|
||||
border: 2px solid #e9ecef;
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
.url-item {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.url-item:hover {
|
||||
background: rgba(255,255,255,0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.copy-btn {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.stats-badge {
|
||||
background: rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body p-5">
|
||||
<h1 class="text-center mb-4">
|
||||
<i class="fas fa-link text-primary"></i>
|
||||
URL Shortener
|
||||
</h1>
|
||||
|
||||
{% if message %}
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle"></i> {{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{{ url_for('index') }}">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-globe"></i>
|
||||
</span>
|
||||
<input type="url" class="form-control" name="original_url"
|
||||
placeholder="Enter your long URL here..." required>
|
||||
<button class="btn btn-primary px-4" type="submit">
|
||||
<i class="fas fa-compress-alt"></i> Shorten
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if shortened_url %}
|
||||
<div class="alert alert-info">
|
||||
<h5><i class="fas fa-magic"></i> Your shortened URL:</h5>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" value="{{ shortened_url }}"
|
||||
id="shortenedUrl" readonly>
|
||||
<button class="btn btn-outline-primary copy-btn" type="button"
|
||||
onclick="copyToClipboard()">
|
||||
<i class="fas fa-copy"></i> Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body p-5">
|
||||
<h1 class="text-center mb-4">
|
||||
<i class="fas fa-flag text-primary"></i>
|
||||
Report URL
|
||||
</h1>
|
||||
|
||||
{% if report_message %}
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle"></i> {{ report_message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{{ url_for('report') }}">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-globe"></i>
|
||||
</span>
|
||||
<input type="url" class="form-control" name="submit_id"
|
||||
placeholder="Enter the sus URL here..." required>
|
||||
<button class="btn btn-primary px-4" type="submit">
|
||||
<i class="fas fa-hammer"></i> Report
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function copyToClipboard(text) {
|
||||
const textToCopy = text || document.getElementById('shortenedUrl').value;
|
||||
navigator.clipboard.writeText(textToCopy).then(function() {
|
||||
// Show success feedback
|
||||
const btn = event.target.closest('button');
|
||||
const originalHTML = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-check"></i> Copied!';
|
||||
btn.classList.remove('btn-outline-primary');
|
||||
btn.classList.add('btn-success');
|
||||
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = originalHTML;
|
||||
btn.classList.remove('btn-success');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
25
2025/catthegrey/ezpz/dist-oops/app/templates/not_found.html
Normal file
25
2025/catthegrey/ezpz/dist-oops/app/templates/not_found.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>URL Not Found</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<h1 class="text-danger">404</h1>
|
||||
<h3>URL Not Found</h3>
|
||||
<p>The shortened URL you're looking for doesn't exist.</p>
|
||||
<a href="{{ url_for('index') }}" class="btn btn-primary">
|
||||
Go Back Home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
3
2025/catthegrey/ezpz/dist-oops/app/templates/redir.html
Normal file
3
2025/catthegrey/ezpz/dist-oops/app/templates/redir.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<script>
|
||||
location.href = "{{url}}"
|
||||
</script>
|
||||
Reference in New Issue
Block a user