Skip to content

Commit 54a1df8

Browse files
committed
dummy app.py removed
1 parent 198f769 commit 54a1df8

2 files changed

Lines changed: 202 additions & 211 deletions

File tree

backend/app.py

Lines changed: 202 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,207 @@
1-
from flask import Flask
2-
app = Flask(__name__)
1+
import os
2+
import uuid
3+
from flask import Flask, render_template, request, redirect, url_for, session, flash
4+
from flask_wtf.csrf import CSRFProtect
5+
from werkzeug.security import generate_password_hash, check_password_hash
6+
from pymongo import MongoClient
7+
from bson import ObjectId
8+
from datetime import datetime
9+
from functools import wraps
10+
from dotenv import load_dotenv
11+
from azure.storage.blob import BlobServiceClient
312

13+
load_dotenv()
14+
15+
app = Flask(__name__, template_folder="../frontend", static_folder="../static")
16+
app.secret_key = os.environ.get("SECRET_KEY", "dev-secret")
17+
app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024 # 10 MB
18+
19+
csrf = CSRFProtect(app)
20+
21+
# ── COSMOS DB / MONGODB ATLAS ──────────────────────────────
22+
client = MongoClient(os.environ.get("COSMOS_URI"))
23+
db = client["Picsource_db"]
24+
images_col = db["image_metadata"]
25+
users_col = db["user"]
26+
27+
# ── AZURE BLOB STORAGE (connection string) ─────────────────
28+
BLOB_CONTAINER = os.environ.get("BLOB_CONTAINER", "images")
29+
AZURE_STORAGE_ACCOUNT = os.environ.get("AZURE_STORAGE_ACCOUNT")
30+
31+
conn_str = os.environ.get("AZURE_STORAGE_CONNECTION_STRING")
32+
blob_service = BlobServiceClient.from_connection_string(conn_str)
33+
container_client = blob_service.get_container_client(BLOB_CONTAINER)
34+
35+
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "webp", "tiff", "bmp", "heic"}
36+
37+
def allowed_file(filename):
38+
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
39+
40+
def get_blob_url(filename):
41+
return f"https://{AZURE_STORAGE_ACCOUNT}.blob.core.windows.net/{BLOB_CONTAINER}/{filename}"
42+
43+
def login_required(f):
44+
@wraps(f)
45+
def decorated(*args, **kwargs):
46+
if "username" not in session:
47+
flash("Please log in first.", "warning")
48+
return redirect(url_for("login"))
49+
return f(*args, **kwargs)
50+
return decorated
51+
52+
# ── REGISTER ───────────────────────────────────────────────
53+
@app.route("/register", methods=["GET", "POST"])
54+
def register():
55+
if request.method == "POST":
56+
username = request.form.get("username", "").strip()
57+
password = request.form.get("password", "").strip()
58+
if users_col.find_one({"username": username}):
59+
flash("Username already taken.", "error")
60+
return redirect(url_for("register"))
61+
users_col.insert_one({
62+
"username": username,
63+
"password": generate_password_hash(password),
64+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
65+
})
66+
flash("Account created! Please log in.", "success")
67+
return redirect(url_for("login"))
68+
return render_template("register.html")
69+
70+
# ── LOGIN ──────────────────────────────────────────────────
71+
@app.route("/login", methods=["GET", "POST"])
72+
def login():
73+
if request.method == "POST":
74+
username = request.form.get("username", "").strip()
75+
password = request.form.get("password", "").strip()
76+
77+
if username == os.environ.get("ADMIN_USERNAME") and \
78+
password == os.environ.get("ADMIN_PASSWORD"):
79+
session["username"] = username
80+
session["is_admin"] = True
81+
flash("Welcome, Admin!", "success")
82+
return redirect(url_for("index"))
83+
84+
user = users_col.find_one({"username": username})
85+
if not user or not check_password_hash(user["password"], password):
86+
flash("Invalid username or password.", "error")
87+
return redirect(url_for("login"))
88+
session["username"] = user["username"]
89+
session["is_admin"] = False
90+
flash(f"Welcome, {user['username']}!", "success")
91+
return redirect(url_for("index"))
92+
return render_template("login.html")
93+
94+
# ── LOGOUT ─────────────────────────────────────────────────
95+
@app.route("/logout")
96+
def logout():
97+
session.clear()
98+
flash("Logged out.", "info")
99+
return redirect(url_for("login"))
100+
101+
# ── GALLERY ────────────────────────────────────────────────
4102
@app.route("/")
5103
def index():
6-
return "PicSource is alive!"
104+
images = list(images_col.find())
105+
return render_template("index.html", images=images)
106+
107+
# ── UPLOAD ─────────────────────────────────────────────────
108+
@app.route("/upload-page")
109+
@login_required
110+
def upload_page():
111+
return render_template("upload.html")
112+
113+
@app.route("/upload", methods=["POST"])
114+
@login_required
115+
def handle_upload():
116+
if "file" not in request.files:
117+
flash("No file part.", "error")
118+
return redirect(url_for("upload_page"))
119+
file = request.files["file"]
120+
if file.filename == "":
121+
flash("No file selected.", "error")
122+
return redirect(url_for("upload_page"))
123+
if not allowed_file(file.filename):
124+
flash("Invalid file type.", "error")
125+
return redirect(url_for("upload_page"))
126+
127+
ext = os.path.splitext(file.filename)[1].lower()
128+
unique_name = f"{uuid.uuid4()}{ext}"
129+
130+
blob_client = container_client.get_blob_client(unique_name)
131+
file_bytes = file.read()
132+
blob_client.upload_blob(file_bytes, overwrite=True)
133+
134+
size_bytes = len(file_bytes)
135+
tags = [t.strip() for t in request.form.get("tags", "").split(",") if t.strip()]
136+
137+
images_col.insert_one({
138+
"filename": unique_name,
139+
"image_url": get_blob_url(unique_name),
140+
"title": request.form.get("title", "").strip(),
141+
"description": request.form.get("description", "").strip(),
142+
"category": request.form.get("category", "").strip(),
143+
"type": request.form.get("type", "").strip(),
144+
"format": request.form.get("format", "").strip(),
145+
"resolution": request.form.get("resolution", "").strip(),
146+
"photographer": request.form.get("photographer", "").strip(),
147+
"camera": request.form.get("camera", "").strip(),
148+
"location": request.form.get("location", "").strip(),
149+
"tags": tags,
150+
"size_kb": round(size_bytes / 1024, 2),
151+
"size_mb": round(size_bytes / (1024 * 1024), 2),
152+
"upload_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
153+
"uploaded_by": session.get("username", "unknown")
154+
})
155+
flash("Image uploaded!", "success")
156+
return redirect(url_for("index"))
157+
158+
# ── DELETE ─────────────────────────────────────────────────
159+
@app.route("/delete/<filename>", methods=["POST"])
160+
@login_required
161+
def delete_image(filename):
162+
if not session.get("is_admin"):
163+
flash("Only admin can delete images.", "error")
164+
return redirect(url_for("index"))
165+
img = images_col.find_one({"filename": filename})
166+
if not img:
167+
flash("Image not found.", "error")
168+
return redirect(url_for("index"))
169+
try:
170+
container_client.get_blob_client(filename).delete_blob()
171+
except Exception:
172+
pass
173+
images_col.delete_one({"filename": filename})
174+
flash("Image deleted.", "info")
175+
return redirect(url_for("index"))
176+
177+
# ── UPDATE (metadata only) ─────────────────────────────────
178+
@app.route("/update/<filename>", methods=["POST"])
179+
@login_required
180+
def update_image(filename):
181+
if not session.get("is_admin"):
182+
flash("Only admin can edit images.", "error")
183+
return redirect(url_for("index"))
184+
img = images_col.find_one({"filename": filename})
185+
if not img:
186+
flash("Image not found.", "error")
187+
return redirect(url_for("index"))
188+
tags = [t.strip() for t in request.form.get("tags", "").split(",") if t.strip()]
189+
images_col.update_one({"filename": filename}, {"$set": {
190+
"title": request.form.get("title", "").strip(),
191+
"description": request.form.get("description", "").strip(),
192+
"category": request.form.get("category", "").strip(),
193+
"type": request.form.get("type", "").strip(),
194+
"format": request.form.get("format", "").strip(),
195+
"resolution": request.form.get("resolution", "").strip(),
196+
"photographer": request.form.get("photographer", "").strip(),
197+
"camera": request.form.get("camera", "").strip(),
198+
"location": request.form.get("location", "").strip(),
199+
"tags": tags,
200+
}})
201+
flash("Image updated!", "success")
202+
return redirect(url_for("index"))
7203

8204
if __name__ == "__main__":
9-
app.run()
205+
debug = os.environ.get("FLASK_DEBUG", "false").lower() == "true"
206+
port = int(os.environ.get("PORT", 5001))
207+
app.run(debug=debug, port=port)

0 commit comments

Comments
 (0)