This commit is contained in:
zaqxs123456 2024-10-21 18:17:56 +08:00
parent 06238cfb8c
commit c19cf78c20
22 changed files with 465 additions and 0 deletions

7
.directory Normal file
View File

@ -0,0 +1,7 @@
[Dolphin]
Timestamp=2024,10,21,18,10,45.565
Version=4
ViewMode=1
[Settings]
HiddenFilesShown=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

103
app.py Normal file
View File

@ -0,0 +1,103 @@
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session
from flask_socketio import SocketIO, emit
import os
from werkzeug.security import generate_password_hash, check_password_hash
from PIL import Image
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key' # Replace with a strong secret key
socketio = SocketIO(app)
app.config['UPLOAD_FOLDER'] = 'images' # Folder to store images
app.config['CACHE_FOLDER'] = 'cache' # Folder to store cached resized images
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
# Fixed username and password
USERNAME = 'user'
PASSWORD = generate_password_hash('password') # Hashed password
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
def resize_image(image_path, cache_path, size=(800, 450)):
with Image.open(image_path) as img:
img.thumbnail(size)
img.save(cache_path)
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username == USERNAME and check_password_hash(PASSWORD, password):
session['logged_in'] = True
return redirect(url_for('gallery'))
else:
error = 'Invalid credentials'
return render_template('login.html', error=error)
return render_template('login.html')
@app.route('/logout')
def logout():
session.pop('logged_in', None)
return redirect(url_for('login'))
@app.route('/gallery')
def gallery():
if not session.get('logged_in'):
return redirect(url_for('login'))
images = os.listdir(app.config['UPLOAD_FOLDER'])
cached_images = []
if not os.path.exists(app.config['CACHE_FOLDER']):
os.makedirs(app.config['CACHE_FOLDER'])
for image in images:
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image)
cache_path = os.path.join(app.config['CACHE_FOLDER'], image)
if not os.path.exists(cache_path):
resize_image(image_path, cache_path)
cached_images.append(image)
return render_template('gallery.html', images=cached_images)
@app.route('/images/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['CACHE_FOLDER'], filename)
def emit_gallery_update():
socketio.emit('update_gallery')
class Watcher(FileSystemEventHandler):
def on_modified(self, event):
if not event.is_directory:
emit_gallery_update()
def on_created(self, event):
if not event.is_directory:
emit_gallery_update()
def on_deleted(self, event):
if not event.is_directory:
emit_gallery_update()
if __name__ == '__main__':
observer = Observer()
observer.schedule(Watcher(), path=app.config['UPLOAD_FOLDER'], recursive=False)
observer.start()
try:
context = ('cert.pem', 'key.pem') # Replace with your self-signed certificate and key files
socketio.run(app, debug=True, ssl_context=context)
except KeyboardInterrupt:
observer.stop()
observer.join()

BIN
cache/101260155_p0.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
cache/97144634_p0.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

BIN
cache/nextsec.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

BIN
cache/unnamed.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
cache/5月壁紙.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

31
cert.pem Normal file
View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUb0qNrwGOHXUFLcFfOLwTwVXNv0cwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDEwMjEwODI1MDdaFw0yNTEw
MjEwODI1MDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDO1dlQYMEErxhxJkPh6CQK4cZN0So9BP9d+ga85z0d
5Oooe0Z3KnJF/Eb4EFsMlFOYgXmqt4g5FbAyleYhi/m6yBSCNYidFuVPnuMQrTx5
vr9/X8ZwiKVEJEqAS7fBN2BCu4qZPKj3R5fPnuvrUcMkdjpNGzXX9KVdNZEVoXfi
yGs25E1ksn+pLZQ+im/1jS9pIh+30Ush3YWFUM+jjZYlPnZjaz2hD/penUpySUUl
Oi90uC30HC1arPxAlS9zY0ja2kPeRKe77Q5WJi1ov0xSg8MYuGFRIc0oqh8EAeL6
ZYODB46QMpXx90u9pwh/KtuxJ5Ai+A+6KHpGUP4Oihl20UnmuDMZCeonoE6fEH30
eCol/5n0RUiYLQlJpvizfzN+a8PJhr/+xqcIeiCwfXE0yu1naU++voD1iCsql5pN
qA5AMHSb4Fnhw69UlQflDiMC/Eb5V7be0yRN8SXCakzzVD2WfUvVttrPkM/VucJw
4W7cZKJClefBzRwx3peBtzVFOT/be+8Lkawm4B/ZqJ4kNzhdVAgCK9cT56cA1LSH
cM+xRZWzTJvB140naIkTgdVtWGFqJN7Po0cGU+PjQQNvMIkUKL1EGmFQJAx06zYJ
QYOQ235jDdcEmYa8AhcIU/p0JeCaBv1/YfoInmoENsR2t6C/Uy8ZublfoUaAcTNM
NQIDAQABo1MwUTAdBgNVHQ4EFgQUA0DKfoR9KP3BCmEhahfkPfKBLskwHwYDVR0j
BBgwFoAUA0DKfoR9KP3BCmEhahfkPfKBLskwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAi0GuzhdF9gXYaUTHtHmHXyYbsjJXECc5ZH5ezJbHYjEz
PzWaMo34O91Ik3lSM8bWVYC7aovpigo81fKxGiRMdaBs6T2IROQDpiN+fR/kr2b+
ZMYsS2ldd5jr9qnmDEWaPpxVeg4zTAGeMZIiQoHseMQUG9SDXpz/Pt/kYRiXRlp1
WYV0aryUO6l2JEd92WjM5ULZYvpZgOMR/gWFTZ8U88A3OwbVifi7f+ntLSgA0YB2
r5z2utlHhJTpdQsNl1yUxT4sX+/Rs0rmQoY6SXKySq+ZpslOfsumYSNIQ4gWA7xg
d3YWv0zu8AUp5Sx+t/zhpnuesd8HzbN1vM5bW5aqPxRl3bFaKRNNUKvmEc+YDPM+
ZtuvQ8uINRHgedNCMaAruAqs8p3zc9gzuSvAhBIKYcjOT6VLlZwdHeE5KV624Iz8
chbX5bvAKXbI3NALDvgKJPXiWGNSHMKkQwhXFk9xjrDaxCU/2NCw2W8aPq6PmWvZ
44aV6oNEPIbEJ6PcOOGVMalPDhrh0YgzXKlogDdECwTtAjMt7SiI/+E4OmmsiWw6
txInD/nm/nDGBJ+jvu+gAN5c5VhpTt1Cib2C3U6+EqNen+wj5klFIoI0Zgf/H/se
7baUlqlSC2iZin2DqkQgIQsfnMscrhQa4pfgckfXn/8TaKSg4BLKw9xxmB/4ulo=
-----END CERTIFICATE-----

BIN
images/101260155_p0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

BIN
images/97144634_p0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

BIN
images/nextsec.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
images/unnamed.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

BIN
images/5月壁紙.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

52
key.pem Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
XSganpYkLqJCS1lgaReUZcgjcBvBkco=
-----END PRIVATE KEY-----

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
Flask
Werkzeug

85
static/gallery_styles.css Normal file
View File

@ -0,0 +1,85 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #ffeeee 25%, #dce2ff 75%);
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
h1 {
color: #333;
margin: 20px 0;
font-size: 24px;
text-align: center;
}
.gallery-container {
display: grid;
gap: 10px;
padding: 20px;
width: 100%;
max-width: 1200px;
box-sizing: border-box;
}
/* Media queries for responsive padding */
@media (max-width: 1199px) {
.gallery-container {
padding-left: 40px;
padding-right: 40px;
}
}
@media (max-width: 991px) {
.gallery-container {
padding-left: 30px;
padding-right: 30px;
}
}
@media (max-width: 767px) {
.gallery-container {
padding-left: 20px;
padding-right: 20px;
}
}
.responsive-img {
width: 100%;
height: auto;
border: 2px solid #ddd;
border-radius: 5px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
aspect-ratio: 16 / 9;
object-fit: cover;
opacity: 0;
transition: opacity 0.5s ease-in-out, transform 0.2s;
}
.responsive-img.loaded {
opacity: 1;
}
.responsive-img:hover {
transform: scale(1.05);
}
/* Media queries for responsive columns */
@media (min-width: 1200px) {
.gallery-container {
grid-template-columns: repeat(4, 1fr);
}
}
@media (min-width: 992px) and (max-width: 1199px) {
.gallery-container {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 768px) and (max-width: 991px) {
.gallery-container {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 767px) {
.gallery-container {
grid-template-columns: 1fr;
}
}

62
static/login_styles.css Normal file
View File

@ -0,0 +1,62 @@
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
form h1 {
color: #333;
margin-bottom: 20px;
font-size: 24px;
text-align: center;
}
form {
background-color: #fff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 350px;
display: flex;
flex-direction: column;
align-items: center;
}
form div {
margin-bottom: 20px;
width: 100%;
}
label {
margin-bottom: 5px;
color: #555;
font-size: 14px;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
font-size: 16px;
}
button {
padding: 12px;
background-color: #007BFF;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
font-size: 16px;
width: 100%;
}
button:hover {
background-color: #0056b3;
transform: scale(1.02);
}
.error {
color: red;
margin-bottom: 15px;
font-size: 14px;
text-align: center;
}

65
static/styles.css Normal file
View File

@ -0,0 +1,65 @@
/* Common styles */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #ffeeee 25%, #dce2ff 75%);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
h1 {
color: #333;
margin: 20px 0;
font-size: 24px;
text-align: center;
}
form {
background-color: #fff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 350px;
display: flex;
flex-direction: column;
align-items: center;
}
form div {
margin-bottom: 20px;
width: 100%;
}
label {
margin-bottom: 5px;
color: #555;
font-size: 14px;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
font-size: 16px;
}
button {
padding: 12px;
background-color: #007BFF;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
font-size: 16px;
width: 100%;
}
button:hover {
background-color: #0056b3;
transform: scale(1.02);
}
.error {
color: red;
margin-bottom: 15px;
font-size: 14px;
text-align: center;
}

34
templates/gallery.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Image Gallery</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='gallery_styles.css') }}">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
</head>
<body>
<h1>Image Gallery</h1>
<div class="gallery-container" id="gallery-container">
{% for image in images %}
<img class="responsive-img" src="{{ url_for('uploaded_file', filename=image) }}" alt="{{ image }}">
{% endfor %}
</div>
<script>
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('update_gallery', function() {
location.reload();
});
document.addEventListener("DOMContentLoaded", function() {
var images = document.querySelectorAll('.responsive-img');
images.forEach(function(img) {
img.addEventListener('load', function() {
img.classList.add('loaded');
});
if (img.complete) {
img.classList.add('loaded');
}
});
});
</script>
</body>
</html>

24
templates/login.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='login_styles.css') }}">
</head>
<body>
<form method="POST">
<h1>Login</h1>
{% if error %}
<p class="error"><strong>Error:</strong> {{ error }}</p>
{% endif %}
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required>
</div>
<button type="submit">Login</button>
</form>
</body>
</html>