password requirements and opengraph for discord,slack etc
This commit is contained in:
parent
79d0adfcf8
commit
740300edaf
66
app.py
66
app.py
@ -264,20 +264,27 @@ def redirect_vanity(vanity, password=None):
|
||||
return render_template('password_prompt.html', vanity=vanity, error=None)
|
||||
|
||||
if content_type == 'url':
|
||||
return redirect(content_data)
|
||||
return render_template('og_shorturl.html', long_url=content_data, username=username, created_at=created_at)
|
||||
elif content_type == 'file':
|
||||
file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], content_data)
|
||||
app.logger.info(f"Attempting to serve file: {file_path}")
|
||||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], content_data)
|
||||
if os.path.exists(file_path):
|
||||
if 'download' in request.path:
|
||||
return send_file(file_path, as_attachment=True)
|
||||
else:
|
||||
return send_file(file_path)
|
||||
else:
|
||||
app.logger.error(f"File not found: {file_path}")
|
||||
return "File not found", 404
|
||||
file_size = os.path.getsize(file_path)
|
||||
file_extension = os.path.splitext(content_data)[1].lower()
|
||||
is_image = file_extension in ['.jpg', '.jpeg', '.png', '.gif']
|
||||
return render_template('og_file.html',
|
||||
filename=content_data,
|
||||
file_size=file_size,
|
||||
username=username,
|
||||
created_at=created_at,
|
||||
is_image=is_image,
|
||||
file_url=url_for('redirect_vanity', vanity=vanity, _external=True))
|
||||
elif content_type == 'pastebin':
|
||||
return render_pastebin(content_data, created_at, user_id, username, vanity, is_private)
|
||||
first_lines = '\n'.join(content_data.split('\n')[:5]) # Get first 5 lines
|
||||
return render_template('og_pastebin.html',
|
||||
vanity=vanity,
|
||||
first_lines=first_lines,
|
||||
username=username,
|
||||
created_at=created_at)
|
||||
|
||||
app.logger.error(f"Content not found for vanity: {vanity}")
|
||||
return "Not found", 404
|
||||
@ -330,17 +337,6 @@ def raw_vanity(vanity, password=None):
|
||||
|
||||
return content_data, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||
return 'Not Found', 404
|
||||
# Replace the LoginForm and RegistrationForm classes with simple classes
|
||||
class LoginForm:
|
||||
def __init__(self, username, password, remember):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.remember = remember
|
||||
|
||||
class RegistrationForm:
|
||||
def __init__(self, username, password):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
@ -348,16 +344,15 @@ def login():
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
remember = 'remember' in request.form
|
||||
form = LoginForm(username, password, remember)
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (form.username,))
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
|
||||
user = cursor.fetchone()
|
||||
if user and User.verify_password(user[2], form.password):
|
||||
if user and User.verify_password(user[2], password):
|
||||
user_obj = User(user[0], user[1], user[2], user[3])
|
||||
login_user(user_obj, remember=form.remember)
|
||||
return redirect(url_for('user_files', username=form.username))
|
||||
return "Invalid username or password"
|
||||
login_user(user_obj, remember=remember)
|
||||
return jsonify({'success': True, 'redirect': url_for('user_files', username=username)})
|
||||
return jsonify({'success': False, 'error': 'Invalid username or password'})
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
@ -365,23 +360,26 @@ def register():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
api_key = User.generate_api_key() # Generate API key
|
||||
|
||||
if len(password) < 5 or not any(c.isupper() for c in password):
|
||||
return jsonify({'success': False, 'error': 'Password does not meet requirements'})
|
||||
|
||||
api_key = User.generate_api_key()
|
||||
db = get_db()
|
||||
cursor = db.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
|
||||
if cursor.fetchone():
|
||||
return "Username already exists"
|
||||
return jsonify({'success': False, 'error': 'Username already exists'})
|
||||
hashed_password = User.hash_password(password)
|
||||
cursor.execute("INSERT INTO users (username, password_hash, api_key) VALUES (?, ?, ?)",
|
||||
(username, hashed_password, api_key))
|
||||
db.commit()
|
||||
|
||||
# Create user directory
|
||||
user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username)
|
||||
if not os.path.exists(user_folder):
|
||||
os.makedirs(user_folder)
|
||||
|
||||
return redirect(url_for('login'))
|
||||
return jsonify({'success': True, 'redirect': url_for('login')})
|
||||
return render_template('register.html')
|
||||
|
||||
@app.route('/logout')
|
||||
@ -819,7 +817,7 @@ def generate_sharex_config():
|
||||
base_url = request.url_root.replace('http://', 'https://', 1).rstrip('/')
|
||||
config = {
|
||||
"Version": "13.7.0",
|
||||
"Name": "aCloud",
|
||||
"Name": "sxbin",
|
||||
"DestinationType": "ImageUploader, TextUploader, FileUploader, URLShortener",
|
||||
"RequestMethod": "POST",
|
||||
"RequestURL": f"{base_url}/api/upload",
|
||||
@ -836,7 +834,7 @@ def generate_sharex_config():
|
||||
|
||||
response = make_response(json.dumps(config, indent=2))
|
||||
response.headers.set('Content-Type', 'application/json')
|
||||
response.headers.set('Content-Disposition', 'attachment', filename='aCloud_ShareX.sxcu')
|
||||
response.headers.set('Content-Disposition', 'attachment', filename='sxbin_ShareX.sxcu')
|
||||
return response
|
||||
|
||||
@app.route('/api/upload', methods=['POST'])
|
||||
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>aCloud - File Sharing Service</title>
|
||||
<title>sxbin - File Sharing Service</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
@ -110,6 +110,10 @@
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.cursor {
|
||||
display: inline-block;
|
||||
@ -399,7 +403,7 @@
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const message1 = "Welcome to aCloud.";
|
||||
const message1 = "Welcome to sxbin.gay";
|
||||
const message2 = "\nA simple toolbox for file uploading,\nURL shortening and pastebin.";
|
||||
const typewriterTextElement = document.getElementById('typewriter-text');
|
||||
const cursorElement = document.getElementById('cursor');
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - aCloud</title>
|
||||
<title>Login - sxbin</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
@ -97,37 +97,54 @@
|
||||
background-color: #2a2a2a;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
.error-message {
|
||||
background-color: #ff6b6b;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back-button">←</a>
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<h2>Login</h2>
|
||||
<form method="POST">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="remember-me">
|
||||
<input type="checkbox" id="remember" name="remember">
|
||||
<label for="remember">Remember me</label>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
<h2>Login to sxbin</h2>
|
||||
<div id="error-message" class="error-message"></div>
|
||||
<form id="login-form" method="POST">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
<label>
|
||||
<input type="checkbox" name="remember"> Remember me
|
||||
</label>
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
||||
<p>Don't have an account? <a href="{{ url_for('register') }}">Register here</a></p>
|
||||
<p>Don't have an account? <a href="{{ url_for('register') }}">Register</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<p>
|
||||
Source code: <a href="https://git.spitkov.hu/cgcristi/aCloud" target="_blank">Spitkov's Git</a> |
|
||||
<a href="https://office.bence.lol/form/#/2/form/view/z5Cf3CL6tZtPjzKsbcEPync6JE3iyMl22h6thUQg1a4/" target="_blank">Suggestions & Bugs</a> |
|
||||
<a href="https://office.bence.lol/kanban/#/2/kanban/view/hx6RTcpN0pR7hc1HHkMzG4awMoMdHjR2zbHjG7Xh+wU/embed/" target="_blank">Todo List</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.getElementById('login-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
fetch('/login', {
|
||||
method: 'POST',
|
||||
body: new FormData(this),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.href = data.redirect;
|
||||
} else {
|
||||
document.getElementById('error-message').textContent = data.error;
|
||||
document.getElementById('error-message').style.display = 'block';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
26
templates/og_file.html
Normal file
26
templates/og_file.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ filename }} - sxbin</title>
|
||||
<meta property="og:title" content="{{ filename }}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ request.url }}">
|
||||
<meta property="og:description" content="File size: {{ file_size|filesizeformat }} | Uploaded by: {{ username }} | Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}">
|
||||
{% if is_image %}
|
||||
<meta property="og:image" content="{{ file_url }}">
|
||||
{% endif %}
|
||||
<meta property="og:site_name" content="sxbin">
|
||||
<meta property="theme-color" content="#4CAF50">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ filename }}</h1>
|
||||
<p>File size: {{ file_size|filesizeformat }}</p>
|
||||
<p>Uploaded by: {{ username }}</p>
|
||||
<p>Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}</p>
|
||||
{% if is_image %}
|
||||
<img src="{{ file_url }}" alt="{{ filename }}">
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
20
templates/og_pastebin.html
Normal file
20
templates/og_pastebin.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pastebin {{ vanity }} - sxbin</title>
|
||||
<meta property="og:title" content="Pastebin {{ vanity }}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ request.url }}">
|
||||
<meta property="og:description" content="{{ first_lines|truncate(200) }}">
|
||||
<meta property="og:site_name" content="sxbin">
|
||||
<meta property="theme-color" content="#4CAF50">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Pastebin {{ vanity }}</h1>
|
||||
<pre>{{ first_lines }}</pre>
|
||||
<p>Created by: {{ username }}</p>
|
||||
<p>Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}</p>
|
||||
</body>
|
||||
</html>
|
23
templates/og_shorturl.html
Normal file
23
templates/og_shorturl.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Shortened URL - sxbin</title>
|
||||
<meta property="og:title" content="Shortened URL">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ request.url }}">
|
||||
<meta property="og:description" content="Shortened link created by {{ username }} on {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}">
|
||||
<meta property="og:site_name" content="sxbin">
|
||||
<meta property="theme-color" content="#4CAF50">
|
||||
<meta http-equiv="refresh" content="0;url={{ long_url }}">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Shortened URL</h1>
|
||||
<p>Redirecting to: {{ long_url }}</p>
|
||||
<p>Created by: {{ username }}</p>
|
||||
<p>Date: {{ created_at.strftime('%Y-%m-
|
||||
|
||||
%d %H:%M:%S') }}</p>
|
||||
</body>
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Register - aCloud</title>
|
||||
<title>Register - sxbin</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
@ -89,33 +89,83 @@
|
||||
background-color: #2a2a2a;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
.error-message {
|
||||
background-color: #ff6b6b;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.password-requirement {
|
||||
font-size: 0.9em;
|
||||
margin-top: 5px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.requirement-not-met {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.requirement-met {
|
||||
color: #4CAF50;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back-button">←</a>
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<h2>Register</h2>
|
||||
<form method="POST">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Register</button>
|
||||
<h2>Register for sxbin</h2>
|
||||
<div id="error-message" class="error-message"></div>
|
||||
<form id="register-form" method="POST">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
<div id="password-length" class="password-requirement requirement-not-met">At least 5 characters</div>
|
||||
<div id="password-uppercase" class="password-requirement requirement-not-met">At least one uppercase letter</div>
|
||||
<input type="submit" value="Register" id="submit-button" disabled>
|
||||
</form>
|
||||
<p>Already have an account? <a href="{{ url_for('login') }}">Login here</a></p>
|
||||
<p>Already have an account? <a href="{{ url_for('login') }}">Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<p>
|
||||
Source code: <a href="https://git.spitkov.hu/cgcristi/aCloud" target="_blank">Spitkov's Git</a> |
|
||||
<a href="https://office.bence.lol/form/#/2/form/view/z5Cf3CL6tZtPjzKsbcEPync6JE3iyMl22h6thUQg1a4/" target="_blank">Suggestions & Bugs</a> |
|
||||
<a href="https://office.bence.lol/kanban/#/2/kanban/view/hx6RTcpN0pR7hc1HHkMzG4awMoMdHjR2zbHjG7Xh+wU/embed/" target="_blank">Todo List</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
const passwordInput = document.getElementById('password');
|
||||
const lengthRequirement = document.getElementById('password-length');
|
||||
const uppercaseRequirement = document.getElementById('password-uppercase');
|
||||
const submitButton = document.getElementById('submit-button');
|
||||
|
||||
passwordInput.addEventListener('input', function() {
|
||||
const password = this.value;
|
||||
const meetsLengthRequirement = password.length >= 5;
|
||||
const meetsUppercaseRequirement = /[A-Z]/.test(password);
|
||||
|
||||
lengthRequirement.className = meetsLengthRequirement ? 'password-requirement requirement-met' : 'password-requirement requirement-not-met';
|
||||
uppercaseRequirement.className = meetsUppercaseRequirement ? 'password-requirement requirement-met' : 'password-requirement requirement-not-met';
|
||||
|
||||
submitButton.disabled = !(meetsLengthRequirement && meetsUppercaseRequirement);
|
||||
});
|
||||
|
||||
document.getElementById('register-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
fetch('/register', {
|
||||
method: 'POST',
|
||||
body: new FormData(this),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.href = data.redirect;
|
||||
} else {
|
||||
document.getElementById('error-message').textContent = data.error;
|
||||
document.getElementById('error-message').style.display = 'block';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user