Compare commits

..

33 Commits
a ... main

Author SHA1 Message Date
7667c5064b Update README.md 2024-10-26 18:42:45 +02:00
454c411121 Update README.md 2024-10-07 19:24:18 +02:00
a031e9d2be fixv2v3 2024-10-05 20:38:04 +02:00
26ea8020c3 fix2v2 2024-10-05 20:37:31 +02:00
25ce46a70b fix? 2024-10-05 20:37:08 +02:00
23c535d933 fix 2 2024-10-05 20:32:33 +02:00
f9d9e0e3c0 revert back 2024-10-05 20:31:49 +02:00
92ca08c1ed fix that anonymous pastebins returned user None instead of Anonymous 2024-10-05 20:29:58 +02:00
c3a8994769 allow user to reset api key 2024-09-16 14:41:26 +02:00
de1b826928 pastebin line offset fix 2024-09-16 14:21:48 +02:00
7f5c9202bb fix fix fix 2024-09-16 14:10:51 +02:00
2e5fd2024f this contains bug next commit is fix 2024-09-16 13:54:54 +02:00
a33bf8f665 fixes v69 2024-09-16 13:34:50 +02:00
052acdb394 minor fixes 2024-09-16 13:02:12 +02:00
3e40c79b81 raw embed fix 2024-09-16 12:53:05 +02:00
67bdc5c325 opengraph fix v2 2024-09-16 12:26:00 +02:00
a2544633e6 fixed that insatnt upload doesnt go on top of fiel upload model and other minor bugfixes 2024-09-16 12:09:50 +02:00
740300edaf password requirements and opengraph for discord,slack etc 2024-09-16 11:49:09 +02:00
79d0adfcf8 asdasd removed from the ------------ line 2024-09-15 20:44:29 +02:00
333c7f13c0 Merge pull request 'Update README.md' (#4) from testt into main
Reviewed-on: cgcristi/aCloud#4
2024-09-15 20:26:20 +02:00
29e26aa647 ctrl + v when file modal is open and drag file to instantly upload 2024-09-15 17:03:38 -04:00
24cb6cd848 Update README.md 2024-09-15 20:23:09 +02:00
fa373d6b46 this? 2024-09-15 16:01:29 +02:00
88099f5a54 is this how u iframe? 2024-09-15 16:00:46 +02:00
cd2c8323d5 sharexfix 2024-09-14 21:00:33 +02:00
c76ff11c26 i dont even remember what i did i just gonn apsuh because its not up to date and i dproably made some curical improvements that i will realsie ver time nd i cant speel because im alread yhigh 2024-09-14 20:36:31 +02:00
3edd810c36 this took me over 10 hours and my head hurts and im feeling so bad that i cant even explain what i did but like passwords redesigning and etc 2024-09-14 16:53:26 +02:00
d404490442 minor improvement to user dashboard 2024-09-11 19:29:29 +02:00
45699446e0 sharex support https 2024-09-10 20:21:22 +02:00
5c0d0413ef fix v2 2024-09-10 20:16:06 +02:00
5751297239 sharex fix 2024-09-10 19:56:15 +02:00
4b0ab996ab VERY VERY VERY BIG UPDATE i wont list here try it urself 2024-09-10 19:41:49 +02:00
0f444b7606 too many changes for me to list but basically user suppport file lsiting uploading deleting renaming moving copying etc and toggle if index.html presnet shw it to acces ur page /username and then ASKDJAKLSDl this is a development why do i have to document everything 2024-09-10 16:00:43 +02:00
22 changed files with 5153 additions and 1050 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# Ignore SQLite database
data.db
# Ignore uploads directory
/uploads/
# Python-related files
__pycache__/
*.py[cod]
*$py.class
# Virtual environment
venv/
env/
.env
# IDE-specific files
.vscode/
.idea/
# OS-specific files
.DS_Store
Thumbs.db
# Log files
*.log
# Temporary files
*.tmp

View File

@ -1,30 +1,2 @@
## Text sharing, File sharing, link shortener.
### frontend and backend, easily configurable (thru editing the html files and the .py)
----------------
1. Introduction
</br>
</br>
</br>
As said above, this is a **simple & private** backend (app.py) and frontend (in /templates), which has these features:
- File Sharing (get a short link to share files, and when the link is opened it will show some information)
- Text Sharing (kind of a pastebin, it will show the text and when the text was made, again in a short link)
- URL Shortener (when hosting this, it will short an url, and give you a link, and when you open it, it will automatically redirect you to that website)
2. This is 100% **anonymous**, no one can see who created the links, etc.
3. The UI is simple.
4. This is very easily self-hostable </br> </br>
Rookie Mistake: when you self-host it, you can open it in localhost:7123, when making a file/paste/shortener don't share that localhost link, localhost is only accessible on YOUR network. - instead, u can host it with your domain.
</br>
</br>
5. This project is **COMPLETLY** open source.
</br>
</br>
6. You might need to install some pip packages, if you dont have them installed already download this repo and go into the folder and execute ```pip install -r requirements.txt```
</br>
</br>
7. Demo:
<video src="https://cgcristi.xyz/demo.mp4" controls></video>
this repo is deprecated
https://github.com/spitkov/sxbin

1575
app.py

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,17 @@
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
api_key TEXT
);
CREATE TABLE IF NOT EXISTS content (
vanity TEXT PRIMARY KEY,
type TEXT NOT NULL,
data TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
user_id INTEGER,
is_private INTEGER DEFAULT 0,
password TEXT,
FOREIGN KEY (user_id) REFERENCES users (id)
);

View File

@ -66,7 +66,13 @@
</br>
</br>
</br>
<p>Source code on: </p><a href="https://github.com/realcgcristi/aCloud" target="_blank">GitHub</a> | <a href="https://git.spitkov.hu/cgcristi/aCloud" target="_blank">Spitkov's Git</a>
<div class="footer">
<p>
Source code on: <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>
</div>
</div>
</body>
</html>

62
templates/api_docs.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Documentation - sxbin</title>
<style>
/* Add your styles here */
</style>
</head>
<body>
<h1>sxbin API Documentation</h1>
<h2>Authentication</h2>
<p>All API requests require an API key to be sent in the X-API-Key header.</p>
<h2>Endpoints</h2>
<h3>Upload File</h3>
<pre>
POST /api/upload/file
Headers:
X-API-Key: your_api_key
Body: multipart/form-data
file: (binary)
</pre>
<p>Returns: JSON with file URL and deletion URL</p>
<h3>Upload Pastebin</h3>
<pre>
POST /api/upload/pastebin
Headers:
X-API-Key: your_api_key
Content-Type: application/json
Body:
{
"content": "Your pastebin content here"
}
</pre>
<p>Returns: JSON with pastebin URL and deletion URL</p>
<h3>Shorten URL</h3>
<pre>
POST /api/shorten
Headers:
X-API-Key: your_api_key
Content-Type: application/json
Body:
{
"url": "https://example.com/your-long-url-here"
}
</pre>
<p>Returns: JSON with shortened URL and deletion URL</p>
<h2>Error Handling</h2>
<p>All errors are returned as JSON with an "error" field describing the issue.</p>
<footer>
<p>For more information or support, please contact our team.</p>
</footer>
</body>
</html>

View File

@ -86,6 +86,8 @@
/* Ensure proper contrast for syntax highlighting */
.highlight pre {
color: var(--text-color);
white-space: pre-wrap;
word-wrap: break-word;
}
/* Override Pygments styles for dark mode */
@ -154,42 +156,65 @@
</head>
<body>
<div class="container">
<h1>Content</h1>
{% if created_at %}
<p>Created at: {{ created_at }}</p>
{% endif %}
{% if language %}
<p>Detected Language: {{ language }}</p>
{% endif %}
<div class="button-container">
<button id="copy-button" onclick="copyToClipboard()">Copy Raw</button>
<a href="{{ url_for('raw_vanity', vanity=vanity) }}" target="_blank"><button>View Raw</button></a>
</div>
<div class="highlight">
<h2>Content</h2>
{% if content %}
<p>Uploaded by: {{ 'Anonymous' if content.username == 'None' else content.username }}</p>
<p>Created at: {{ content.created_at }}</p>
{% if highlighted_content %}
{{ highlighted_content|safe }}
<style>{{ css|safe }}</style>
<div class="highlight">
{{ highlighted_content|safe }}
</div>
<button onclick="copyToClipboard()" class="btn">Copy</button>
<a href="{{ url_for('raw_vanity', vanity=content.vanity) }}" class="btn">View Raw</a>
{% if current_user.is_authenticated and current_user.id == content.user_id %}
<a href="{{ url_for('edit_content', vanity=content.vanity) }}" class="btn">Edit</a>
{% endif %}
{% elif url %}
<p>Shortened URL: <a href="{{ url }}">{{ url }}</a></p>
<button onclick="copyToClipboard()" class="btn">Copy URL</button>
{% elif raw_content %}
<pre style="white-space: pre-wrap; word-wrap: break-word;">{{ raw_content }}</pre>
<button onclick="copyToClipboard()" class="btn">Copy</button>
<a href="{{ url_for('raw_vanity', vanity=content.vanity) }}" class="btn">View Raw</a>
{% if current_user.is_authenticated and current_user.id == content.user_id %}
<a href="{{ url_for('edit_content', vanity=content.vanity) }}" class="btn">Edit</a>
{% endif %}
{% else %}
<pre>{{ content }}</pre>
<p>No content available</p>
{% endif %}
</div>
{% if current_user.is_authenticated and current_user.id == content.user_id %}
<form action="{{ url_for('delete_content', vanity=content.vanity) }}" method="post" style="display: inline;">
<button type="submit" class="btn">Delete</button>
</form>
{% endif %}
{% else %}
<p>No content found.</p>
{% endif %}
</div>
<button id="theme-toggle">Toggle Theme</button>
<script>
const rawContent = {{ raw_content|tojson }};
const rawContent = {{ raw_content|tojson|default('null') }};
const url = {{ url|tojson|default('null') }};
function copyToClipboard() {
navigator.clipboard.writeText(rawContent).then(() => {
const copyButton = document.getElementById("copy-button");
const originalText = copyButton.textContent;
copyButton.textContent = "Copied!";
setTimeout(() => {
copyButton.textContent = originalText;
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
});
let textToCopy = rawContent;
if (url) {
textToCopy = url;
}
if (textToCopy) {
navigator.clipboard.writeText(textToCopy).then(() => {
alert('Copied to clipboard!');
}).catch(err => {
console.error('Failed to copy text: ', err);
});
} else {
console.error('No content to copy');
}
}
const themeToggle = document.getElementById('theme-toggle');

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Content Info</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #1a1a1a;
color: #f0f0f0;
}
.container {
max-width: 800px;
margin: 0 auto;
}
h2 {
color: #4CAF50;
}
.info-item {
margin-bottom: 10px;
}
.btn {
display: inline-block;
background-color: #4CAF50;
color: white;
padding: 8px 12px;
text-decoration: none;
border-radius: 4px;
margin: 5px 0;
}
.btn:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<div class="container">
<h2>Content Information</h2>
<div class="info-item"><strong>Type:</strong> {{ info.type }}</div>
<div class="info-item"><strong>Vanity URL:</strong> {{ info.vanity }}</div>
<div class="info-item"><strong>Created At:</strong> {{ info.created_at }}</div>
<div class="info-item"><strong>Uploaded By:</strong> {{ info.username }}</div>
{% if info.type == 'file' %}
<div class="info-item"><strong>Filename:</strong> {{ info.data }}</div>
{% if info.file_size %}
<div class="info-item"><strong>File Size:</strong> {{ info.file_size }} bytes</div>
{% endif %}
<a href="{{ url_for('redirect_vanity', vanity=info.vanity)|replace('/download', '') }}" class="btn">View/Embed</a>
<a href="{{ url_for('redirect_vanity', vanity=info.vanity) ~ ('/download' if '/download' not in url_for('redirect_vanity', vanity=info.vanity) else '') }}" class="btn">Download</a>
{% elif info.type == 'url' %}
<div class="info-item"><strong>Target URL:</strong> {{ info.data }}</div>
<a href="{{ url_for('redirect_vanity', vanity=info.vanity) }}" class="btn">Visit URL</a>
{% elif info.type == 'pastebin' %}
<a href="{{ url_for('redirect_vanity', vanity=info.vanity) }}" class="btn">View Pastebin</a>
<a href="{{ url_for('raw_vanity', vanity=info.vanity) }}" class="btn">View Raw</a>
{% endif %}
{% if current_user.is_authenticated and current_user.username == info.username %}
{% if info.type != 'file' or not info.is_media %}
<a href="{{ url_for('edit_content', vanity=info.vanity) }}" class="btn">Edit</a>
{% endif %}
<form action="{{ url_for('delete_content', vanity=info.vanity) }}" method="post" style="display: inline;">
<button type="submit" class="btn">Delete</button>
</form>
{% endif %}
</div>
</body>
</html>

View File

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Content</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #1a1a1a;
color: #f0f0f0;
}
.container {
max-width: 800px;
margin: 0 auto;
}
h1 {
color: #4CAF50;
}
#editor {
width: 100%;
height: 400px;
font-family: monospace;
font-size: 14px;
background-color: #2a2a2a;
color: #f0f0f0;
border: 1px solid #4CAF50;
padding: 10px;
box-sizing: border-box;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
font-size: 16px;
margin-top: 10px;
}
input[type="submit"]:hover {
background-color: #45a049;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
</head>
<body>
<div class="container">
<h1>Edit Content</h1>
<form method="post">
<div id="editor">{{ content }}</div>
<input type="hidden" name="content" id="hidden-content">
<input type="submit" value="Save" onclick="updateContent()">
</form>
</div>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/text");
function updateContent() {
document.getElementById('hidden-content').value = editor.getValue();
}
</script>
</body>
</html>

View 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>Edit Password</title>
<style>
/* Add your styles here */
</style>
</head>
<body>
<div class="container">
<h2>Edit Password</h2>
<form method="POST">
<input type="password" name="new_password" placeholder="Enter new password">
<input type="hidden" name="action" value="update">
<button type="submit">Update Password</button>
</form>
<form method="POST">
<input type="hidden" name="action" value="remove">
<button type="submit">Remove Password</button>
</form>
<a href="{{ url_for('user_files', username=current_user.username) }}">Cancel</a>
</div>
</body>
</html>

165
templates/file_info.html Normal file
View 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>{{ 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_private %} | Password Protected{% endif %}">
{% if is_embeddable %}
<meta property="og:image" content="{{ raw_url }}">
{% endif %}
<meta property="og:site_name" content="sxbin">
<meta property="theme-color" content="#4CAF50">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #e0e0e0;
background-color: #1e1e1e;
margin: 0;
padding: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h2 {
color: #4CAF50;
}
.info-item {
margin-bottom: 10px;
}
.embed-container {
margin-top: 20px;
margin-bottom: 20px;
}
.embed-container img, .embed-container embed {
max-width: 100%;
max-height: 600px;
display: block;
margin: 0 auto;
}
.btn-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
}
.btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #45a049;
}
.home-button {
position: fixed;
top: 20px;
left: 20px;
font-size: 24px;
color: #4CAF50;
text-decoration: none;
}
.home-button:hover {
color: #45a049;
}
#theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background-color: #333;
color: #fff;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 4px;
}
footer {
text-align: center;
margin-top: 20px;
padding: 10px;
background-color: #2a2a2a;
color: #f0f0f0;
}
.edit-btn, .delete-btn {
background-color: #f44336;
}
.edit-btn:hover, .delete-btn:hover {
background-color: #d32f2f;
}
</style>
</head>
<body>
<a href="/" class="home-button">&#8962;</a>
<div class="container">
<h2>{{ filename }}</h2>
<div class="info-item"><strong>File size:</strong> {{ file_size|filesizeformat }}</div>
<div class="info-item"><strong>Uploaded by:</strong> {{ username }}</div>
<div class="info-item"><strong>Date:</strong> {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}</div>
<div class="info-item"><strong>File type:</strong> {{ filename.split('.')[-1].upper() if '.' in filename else 'Unknown' }}</div>
{% if is_embeddable %}
<div class="embed-container">
{% if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.svg')) %}
<img src="{{ raw_url }}" alt="{{ filename }}">
{% elif filename.lower().endswith('.pdf') %}
<embed src="{{ raw_url }}" type="application/pdf" width="100%" height="600px">
{% endif %}
</div>
{% endif %}
<div class="btn-container">
<a href="{{ file_url }}/download{% if password %}/{{ password }}{% endif %}" class="btn">Download</a>
<a href="{{ raw_url }}" class="btn">View Raw</a>
{% if current_user.is_authenticated and current_user.id == user_id %}
{% if filename.lower().endswith(('.txt', '.html', '.css', '.js', '.py', '.md')) or '.' not in filename %}
<a href="{{ url_for('edit_content', vanity=vanity) }}" class="btn edit-btn">Edit</a>
{% endif %}
<form action="{{ url_for('delete_content', vanity=vanity) }}" method="post" style="display: inline;">
<button type="submit" class="btn delete-btn" onclick="return confirm('Are you sure you want to delete this file?')">Delete</button>
</form>
{% endif %}
</div>
</div>
<button id="theme-toggle">Toggle Theme</button>
<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 themeToggle = document.getElementById('theme-toggle');
const body = document.body;
themeToggle.addEventListener('click', () => {
body.classList.toggle('light-mode');
localStorage.setItem('theme', body.classList.contains('light-mode') ? 'light' : 'dark');
});
// Check for saved theme preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light') {
body.classList.add('light-mode');
}
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,150 @@
<h2>Login</h2>
<form method="post">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<input type="submit" value="Login">
</form>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - sxbin</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #1a1a1a;
color: #f0f0f0;
}
.content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: #2a2a2a;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
width: 300px;
}
h2 {
text-align: center;
color: #4CAF50;
}
form {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 5px;
}
input[type="text"],
input[type="password"] {
padding: 8px;
margin-bottom: 10px;
border-radius: 4px;
border: 1px solid #4CAF50;
background-color: #333;
color: #f0f0f0;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
.remember-me {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.remember-me input {
margin-right: 5px;
}
p {
text-align: center;
margin-top: 15px;
}
a {
color: #4CAF50;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.back-button {
position: absolute;
top: 20px;
left: 20px;
font-size: 24px;
color: #4CAF50;
text-decoration: none;
}
.back-button:hover {
color: #45a049;
}
.footer {
text-align: center;
padding: 10px;
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">&#8592;</a>
<div class="content">
<div class="container">
<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</a></p>
</div>
</div>
<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>

22
templates/og_file.html Normal file
View File

@ -0,0 +1,22 @@
<!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_embeddable %}
<meta property="og:image" content="{{ file_url }}/raw">
{% endif %}
<meta property="og:site_name" content="sxbin">
<meta property="theme-color" content="#4CAF50">
</head>
<body>
<script>
window.location.href = "{{ file_url }}";
</script>
</body>
</html>

View 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>

View File

@ -0,0 +1,24 @@
<!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 - sxbin">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.url }}">
<meta property="og:description" content="Shortened URL {% if is_private %}(Password Protected){% endif %} | Created by: {{ username }} | Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}">
<meta property="og:site_name" content="sxbin">
<meta property="theme-color" content="#4CAF50">
</head>
<body>
<h1>Shortened URL</h1>
<p>Original URL: {{ long_url }}</p>
<p>Created by: {{ username }}</p>
<p>Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}</p>
{% if is_private %}
<p>This URL is password protected.</p>
{% endif %}
<a href="{{ url_for('redirect_vanity', vanity=vanity) }}">Access the URL</a>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Protected Content - sxbin</title>
<meta property="og:title" content="Password Protected Content - sxbin">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.url }}">
<meta property="og:description" content="This {{ content_type }} is password protected. Enter the password to view.">
<meta property="og:site_name" content="sxbin">
<meta property="theme-color" content="#4CAF50">
<style>
/* Add your styles here */
</style>
</head>
<body>
<h1>Password Protected Content</h1>
<p>This {{ content_type }} is password protected. Please enter the password to view.</p>
{% if error %}
<p style="color: red;">{{ error }}</p>
{% endif %}
<form method="POST">
<input type="password" name="password" required>
<button type="submit">Submit</button>
</form>
</body>
</html>

View File

@ -3,52 +3,230 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pastebin Content</title>
<title>Pastebin {{ vanity }} - sxbin</title>
<meta property="og:title" content="Pastebin {{ vanity }} - sxbin">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.url }}">
<meta property="og:description" content="Pastebin{% if is_private %} (Password Protected){% endif %} | Uploaded by: {{ content.username }} | Date: {{ created_at.strftime('%Y-%m-%d %H:%M:%S') }}">
<meta property="og:site_name" content="sxbin">
<meta property="theme-color" content="#4CAF50">
<style>
{{ css|safe }}
/* Add any additional styles here */
:root {
--bg-color: #1e1e1e;
--text-color: #e0e0e0;
--highlight-bg: #2d2d2d;
--highlight-border: #444;
--button-bg: #3a3a3a;
--button-text: #e0e0e0;
}
.light-mode {
--bg-color: #ffffff;
--text-color: #333333;
--highlight-bg: #f8f8f8;
--highlight-border: #ccc;
--button-bg: #f0f0f0;
--button-text: #333333;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: var(--bg-color);
transition: background-color 0.3s, color 0.3s;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1, h2 {
margin-bottom: 20px;
}
.button-container {
margin-bottom: 10px;
margin-bottom: 20px;
}
.button-container button {
button {
background-color: var(--button-bg);
color: var(--button-text);
border: none;
padding: 10px 15px;
margin-right: 10px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
button:hover {
opacity: 0.8;
}
.highlight {
background-color: #f8f8f8;
border: 1px solid #ccc;
background-color: var(--highlight-bg);
border: 1px solid var(--highlight-border);
border-radius: 4px;
padding: 1em;
overflow: auto;
position: relative;
}
.highlight pre {
color: var(--text-color);
white-space: pre;
word-wrap: normal;
overflow-x: auto;
margin: 0;
padding: 0;
}
.highlight .linenos {
color: #999;
text-align: right;
padding-right: 10px;
border-right: 1px solid var(--highlight-border);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.highlight .code {
padding-left: 10px;
}
#theme-toggle {
position: fixed;
top: 20px;
right: 20px;
}
a {
color: var(--text-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.btn-container {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
background-color: var(--button-bg);
color: var(--button-text);
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s, opacity 0.3s;
text-decoration: none;
display: inline-block;
flex: 1;
text-align: center;
font-size: 14px;
}
.btn:hover {
opacity: 0.8;
}
form {
flex: 1;
display: flex;
}
form .btn {
width: 100%;
}
.home-button {
position: fixed;
top: 20px;
left: 20px;
font-size: 24px;
color: var(--text-color);
text-decoration: none;
z-index: 1001;
}
.home-button:hover {
color: #4CAF50;
}
{{ css|safe }}
</style>
</head>
<body>
<h1>Pastebin Content</h1>
<p>Created at: {{ created_at }}</p>
<p>Detected Language: {{ language }}</p>
<div class="button-container">
<button id="copy-button" onclick="copyToClipboard()">Copy Raw</button>
<a href="{{ url_for('raw_vanity', vanity=vanity) }}" target="_blank"><button>View Raw</button></a>
</div>
<div class="highlight">
{{ content|safe }}
<a href="/" class="home-button">&#8962;</a>
<div class="container">
<h2>Content</h2>
<p>Uploaded by: {{ 'Anonymous' if content.username == 'None' else content.username }}</p>
<p>Created at: {{ created_at }}</p>
<div class="highlight">
{{ highlighted_content|safe }}
</div>
<div class="btn-container">
<button onclick="copyToClipboard()" class="btn">Copy</button>
<a href="{{ url_for('raw_vanity', vanity=vanity) }}" class="btn">View Raw</a>
{% if current_user.is_authenticated and current_user.id == content.user_id %}
<a href="{{ url_for('edit_content', vanity=vanity) }}" class="btn">Edit</a>
{% if is_private %}
<button onclick="openEditPasswordModal()" class="btn">Edit Password</button>
{% else %}
<button onclick="openAddPasswordModal()" class="btn">Add Password</button>
{% endif %}
<form action="{{ url_for('delete_content', vanity=vanity) }}" method="post">
<button type="submit" class="btn">Delete</button>
</form>
{% endif %}
</div>
</div>
<button id="theme-toggle">Toggle Theme</button>
<footer style="text-align: center; margin-top: 20px; padding: 10px; background-color: #2a2a2a; color: #f0f0f0;">
<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 rawContent = {{ raw_content|tojson }};
function copyToClipboard() {
navigator.clipboard.writeText(rawContent).then(() => {
const copyButton = document.getElementById("copy-button");
const originalText = copyButton.textContent;
copyButton.textContent = "Copied!";
setTimeout(() => {
copyButton.textContent = originalText;
}, 2000);
alert('Copied to clipboard!');
}).catch(err => {
console.error('Failed to copy text: ', err);
});
}
const themeToggle = document.getElementById('theme-toggle');
const html = document.documentElement;
function toggleTheme() {
html.classList.toggle('light-mode');
localStorage.setItem('lightMode', html.classList.contains('light-mode'));
}
themeToggle.addEventListener('click', toggleTheme);
// Check for saved theme preference
if (localStorage.getItem('lightMode') === 'true') {
html.classList.add('light-mode');
}
</script>
</body>
</html>

View File

@ -1,6 +1,171 @@
<h2>Register</h2>
<form method="post">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<input type="submit" value="Register">
</form>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - sxbin</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #1a1a1a;
color: #f0f0f0;
}
.content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: #2a2a2a;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
width: 300px;
}
h2 {
text-align: center;
color: #4CAF50;
}
form {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 5px;
}
input[type="text"],
input[type="password"] {
padding: 8px;
margin-bottom: 10px;
border-radius: 4px;
border: 1px solid #4CAF50;
background-color: #333;
color: #f0f0f0;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
p {
text-align: center;
margin-top: 15px;
}
a {
color: #4CAF50;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.back-button {
position: absolute;
top: 20px;
left: 20px;
font-size: 24px;
color: #4CAF50;
text-decoration: none;
}
.back-button:hover {
color: #45a049;
}
.footer {
text-align: center;
padding: 10px;
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">&#8592;</a>
<div class="content">
<div class="container">
<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</a></p>
</div>
</div>
<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>

File diff suppressed because it is too large Load Diff

View File

@ -16,12 +16,19 @@
h2 {
color: #4CAF50;
}
ul {
list-style-type: none;
.file-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 0;
list-style-type: none;
}
li {
margin-bottom: 10px;
.file-item {
background-color: #2a2a2a;
padding: 10px;
border-radius: 5px;
display: flex;
align-items: center;
}
a {
color: #4CAF50;
@ -30,13 +37,34 @@
a:hover {
text-decoration: underline;
}
.folder {
font-weight: bold;
}
.file-icon {
margin-right: 5px;
}
</style>
</head>
<body>
<h2>{{ username }}'s Files</h2>
<ul>
<p>Current folder: {{ current_folder or 'Root' }}</p>
{% if parent_url is not none %}
<p><a href="{{ parent_url }}">Parent Directory</a></p>
{% endif %}
<ul class="file-list">
{% for folder in folders %}
<li class="file-item folder">
<span class="file-icon">📁</span>
<a href="{{ url_for('serve_user_page', username=username, filename=folder.path) }}">{{ folder.name }}</a>
</li>
{% endfor %}
{% for file in files %}
<li><a href="{{ url_for('download_folder_file', vanity=username, file_name=file) }}">{{ file }}</a></li>
<li class="file-item">
<span class="file-icon">📄</span>
<a href="{{ url_for('serve_user_page', username=username, filename=file.path) }}">{{ file.name }}</a>
</li>
{% endfor %}
</ul>
</body>

View File

@ -1,27 +0,0 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>&#80;&#121;&#116;&#104;&#111;&#110;&#32;&#73;&#68;&#69;</title><script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython_stdlib.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/python/python.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/addon/hint/show-hint.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/addon/hint/python-hint.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/theme/monokai.min.css"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/addon/hint/show-hint.css"><style>body{background-color:#1e1e1e;color:#d4d4d4;font-family:'Consolas','Courier New',monospace;margin:0;padding:20px}h1{color:#569cd6}#code{width:100%;height:300px}#output{width:100%;height:200px;background-color:#1e1e1e;border:1px solid #3c3c3c;padding:10px;margin-top:10px;overflow-y:auto;font-family:'Consolas','Courier New',monospace}#run-button,#save-button{background-color:#0e639c;color:white;border:none;padding:10px 20px;margin-top:10px;cursor:pointer}#run-button:hover,#save-button:hover{background-color:#1177bb}.toolbar{margin-bottom:10px}.toolbar button{background-color:#3c3c3c;color:#d4d4d4;border:none;padding:5px 10px;margin-right:5px;cursor:pointer}.toolbar button:hover{background-color:#4e4e4e}</style></head><body onload="brython()"><h1>&#80;&#121;&#116;&#104;&#111;&#110;&#32;&#73;&#68;&#69;</h1><div class="toolbar"><button id="undo-button">&#85;&#110;&#100;&#111;</button><button id="redo-button">&#82;&#101;&#100;&#111;</button><button id="save-button">&#83;&#97;&#118;&#101;</button></div><textarea id="code" placeholder="&#69;&#110;&#116;&#101;&#114;&#32;&#121;&#111;&#117;&#114;&#32;&#80;&#121;&#116;&#104;&#111;&#110;&#32;&#99;&#111;&#100;&#101;&#32;&#104;&#101;&#114;&#101;"></textarea><br><button id="run-button">&#82;&#117;&#110;</button><div id="output"></div><script type="text/python">from browser import document,window
import sys
class _0x1234:
def __init__(_0x5678):_0x5678._0x9abc=document["output"]
def write(_0x5678,_0xdef0):_0x5678._0x9abc.innerHTML+=_0xdef0
sys.stdout=_0x1234()
sys.stderr=_0x1234()
def _0x2345(_0x6789):
document["output"].innerHTML=""
_0xabcd=window.editor.getValue()
try:exec(_0xabcd)
except Exception as _0xef01:print(f"Error: {str(_0xef01)}")
document["run-button"].bind("click",_0x2345)
def _0x3456(_0x789a):window.editor.undo()
def _0x4567(_0x89ab):window.editor.redo()
document["undo-button"].bind("click",_0x3456)
document["redo-button"].bind("click",_0x4567)
def _0x5678(_0x9abc):
_0xbcde=window.editor.getValue()
_0xcdef=window.Blob.new([_0xbcde],{'type':'text/plain'})
_0xdef0=window.URL.createObjectURL(_0xcdef)
_0xef01=document.createElement('a')
_0xef01.href=_0xdef0
_0xef01.download='python_code.py'
_0xef01.click()
window.URL.revokeObjectURL(_0xdef0)
document["save-button"].bind("click",_0x5678)</script><script>var _0x1234=CodeMirror.fromTextArea(document.getElementById("code"),{mode:"python",theme:"monokai",lineNumbers:!0,autoCloseBrackets:!0,matchBrackets:!0,indentUnit:4,tabSize:4,indentWithTabs:!1,extraKeys:{"Ctrl-Space":"autocomplete","Tab":function(_0x5678){_0x5678.somethingSelected()?_0x5678.indentSelection("add"):_0x5678.replaceSelection(_0x5678.getOption("indentWithTabs")?"\t":Array(_0x5678.getOption("indentUnit")+1).join(" "),"end","+input")}},hintOptions:{completeSingle:!1}});_0x1234.on("inputRead",function(_0x2345,_0x3456){if("+input"===_0x3456.origin){var _0x4567=_0x2345.getCursor(),_0x5678=_0x2345.getTokenAt(_0x4567);("variable"===_0x5678.type||"."===_0x5678.string)&&_0x2345.showHint({completeSingle:!1})}})</script></body></html>