zetarmany commited on
Commit
fdd0871
·
verified ·
1 Parent(s): cd37043

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -241
app.py CHANGED
@@ -6,46 +6,34 @@ from flask_login import LoginManager, UserMixin, login_user, login_required, log
6
  from werkzeug.security import generate_password_hash, check_password_hash
7
  from werkzeug.utils import secure_filename
8
  from datetime import datetime
9
- import functools
10
 
11
  app = Flask(__name__)
12
  app.config['SECRET_KEY'] = 'angkatan-rahasia-2024-2026'
13
  app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///gallery.db'
14
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
15
  app.config['UPLOAD_FOLDER'] = 'static/uploads'
16
- app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB max file size
17
- app.config['REMEMBER_COOKIE_DURATION'] = 60 * 60 * 24 * 7 # 7 hari
18
 
19
- # Buat folder upload jika belum ada
20
  os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
21
 
22
- # Database initialization
23
  db = SQLAlchemy(app)
24
 
25
  # Login manager
26
  login_manager = LoginManager()
27
  login_manager.init_app(app)
28
- login_manager.login_view = 'login'
29
- login_manager.login_message = 'Silakan login terlebih dahulu!'
30
- login_manager.login_message_category = 'warning'
31
- login_manager.refresh_view = 'login'
32
- login_manager.needs_refresh_message = 'Sesi habis, silakan login ulang'
33
- login_manager.needs_refresh_message_category = 'warning'
34
 
35
- # MODELS
36
  class User(UserMixin, db.Model):
37
  __tablename__ = 'users'
38
-
39
  id = db.Column(db.Integer, primary_key=True)
40
  username = db.Column(db.String(80), unique=True, nullable=False)
41
  password_hash = db.Column(db.String(200), nullable=False)
42
  is_admin = db.Column(db.Boolean, default=False)
43
  created_at = db.Column(db.DateTime, default=datetime.utcnow)
44
 
45
- # Relasi
46
- photos = db.relationship('Photo', backref='uploader', lazy=True)
47
- likes = db.relationship('Like', backref='user', lazy=True)
48
-
49
  def set_password(self, password):
50
  self.password_hash = generate_password_hash(password)
51
 
@@ -54,7 +42,6 @@ class User(UserMixin, db.Model):
54
 
55
  class Photo(db.Model):
56
  __tablename__ = 'photos'
57
-
58
  id = db.Column(db.Integer, primary_key=True)
59
  filename = db.Column(db.String(200), nullable=False)
60
  caption = db.Column(db.String(500))
@@ -62,341 +49,204 @@ class Photo(db.Model):
62
  uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
63
  likes_count = db.Column(db.Integer, default=0)
64
 
65
- # Relasi
66
- likes = db.relationship('Like', backref='photo', lazy=True, cascade='all, delete-orphan')
67
 
68
  class Like(db.Model):
69
  __tablename__ = 'likes'
70
-
71
  id = db.Column(db.Integer, primary_key=True)
72
  user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
73
  photo_id = db.Column(db.Integer, db.ForeignKey('photos.id'), nullable=False)
74
  created_at = db.Column(db.DateTime, default=datetime.utcnow)
75
 
76
- # Kombinasi user_id dan photo_id harus unik (1 user hanya bisa like 1x per foto)
77
- __table_args__ = (db.UniqueConstraint('user_id', 'photo_id', name='unique_user_photo_like'),)
78
 
79
  @login_manager.user_loader
80
  def load_user(user_id):
81
- return db.session.get(User, int(user_id))
82
-
83
- # Buat file lock untuk mencegah multiple workers menjalankan inisialisasi bersamaan
84
- lock_file = '/tmp/app_initialized.lock'
85
 
86
- # INITIALIZE DATABASE AND ADMIN (HANYA SEKALI)
87
  with app.app_context():
88
- # Buat tables
89
  db.create_all()
90
- print("✅ Database tables created!")
91
-
92
- # Cek apakah sudah diinisialisasi sebelumnya
93
- initialized = False
94
- try:
95
- if os.path.exists(lock_file):
96
- with open(lock_file, 'r') as f:
97
- if f.read().strip() == 'initialized':
98
- initialized = True
99
- print("📌 Database already initialized by another worker")
100
- except:
101
- pass
 
 
 
 
 
 
 
 
 
 
 
102
 
103
- if not initialized:
104
- # DAFTAR LENGKAP 11 ADMIN DARI SOAL
105
- admins = [
106
- {'username': 'Hilbraaaam', 'password': 'Ketua Angkatan 24-26'},
107
- {'username': 'Hudzaifahh', 'password': 'Wakil ketua Angkatan 24-26'},
108
- {'username': 'Rafasyahh', 'password': 'Humas Kesayangan'},
109
- {'username': 'Elazzam', 'password': 'Eos 800 D'},
110
- {'username': 'Azzam Diq', 'password': 'Shidiq'},
111
- {'username': 'Dzikrii', 'password': 'Bayar woe'},
112
- {'username': 'Ibrahim', 'password': 'Mboh'},
113
- {'username': 'Yusupp', 'password': 'Bangun'},
114
- {'username': 'Azzam JR', 'password': 'Saturn'},
115
- {'username': 'MK Azzam', 'password': 'Aneka Gold'},
116
- {'username': 'Sami abd', 'password': 'TamTam'}
117
- ]
118
-
119
- # CEK ADMIN YANG SUDAH ADA
120
- existing_admins = {admin.username for admin in User.query.filter_by(is_admin=True).all()}
121
- print(f"📊 Admin yang sudah ada: {len(existing_admins)}")
122
-
123
- # TAMBAHKAN ADMIN YANG BELUM ADA
124
- new_admin_count = 0
125
- for admin_data in admins:
126
- if admin_data['username'] not in existing_admins:
127
- try:
128
- admin = User(
129
- username=admin_data['username'],
130
- is_admin=True
131
- )
132
- admin.set_password(admin_data['password'])
133
- db.session.add(admin)
134
- new_admin_count += 1
135
- print(f" ✓ Admin baru: {admin_data['username']}")
136
- except Exception as e:
137
- print(f" ❌ Gagal menambahkan {admin_data['username']}: {e}")
138
-
139
- if new_admin_count > 0:
140
- try:
141
- db.session.commit()
142
- print(f"✅ Berhasil menambahkan {new_admin_count} admin baru")
143
-
144
- # Buat file lock
145
- try:
146
- with open(lock_file, 'w') as f:
147
- f.write('initialized')
148
- except:
149
- pass
150
- except Exception as e:
151
- db.session.rollback()
152
- print(f"❌ Error saat commit: {e}")
153
- else:
154
- print("⚠️ Tidak ada admin baru yang ditambahkan")
155
-
156
- # TAMPILKAN STATISTIK FINAL
157
- total_admins = User.query.filter_by(is_admin=True).count()
158
- total_users = User.query.count()
159
- print(f"\n📊 Final stats:")
160
- print(f" ✅ Total admin: {total_admins}")
161
- print(f" ✅ Total user: {total_users}")
162
 
163
- # ROUTES
164
  @app.route('/')
165
  def index():
166
- # Halaman publik - tidak perlu login
167
- page = request.args.get('page', 1, type=int)
168
- per_page = 12
169
-
170
- photos = Photo.query.order_by(Photo.upload_date.desc()).paginate(page=page, per_page=per_page, error_out=False)
171
-
172
- # Ambil trending (3 foto dengan like terbanyak)
173
  trending = Photo.query.order_by(Photo.likes_count.desc()).limit(3).all()
174
-
175
  return render_template('index.html', photos=photos, trending=trending)
176
 
177
  @app.route('/login', methods=['GET', 'POST'])
178
  def login():
179
  # Kalau sudah login, redirect ke home
180
  if current_user.is_authenticated:
181
- if current_user.is_admin:
182
- return redirect(url_for('admin_dashboard'))
183
  return redirect(url_for('index'))
184
 
185
  if request.method == 'POST':
186
  username = request.form.get('username')
187
  password = request.form.get('password')
188
- remember = True if request.form.get('remember') else False
189
-
190
- print(f"🔐 Login attempt: {username}") # Debug
191
 
192
  user = User.query.filter_by(username=username).first()
193
 
194
  if user and user.check_password(password):
195
- login_user(user, remember=remember)
196
- flash(f'Login berhasil! Selamat datang {username}', 'success')
197
- print(f" ✅ Login successful for {username}") # Debug
198
-
199
- next_page = request.args.get('next')
200
- if next_page and next_page != url_for('login'):
201
- return redirect(next_page)
202
 
 
203
  if user.is_admin:
204
- return redirect(url_for('admin_dashboard'))
205
  return redirect(url_for('index'))
206
  else:
207
- print(f" ❌ Login failed for {username}") # Debug
208
- flash('❌ Username atau password salah!', 'error')
209
 
210
  return render_template('login.html')
211
 
212
  @app.route('/logout')
213
  def logout():
214
  logout_user()
215
- flash('👋 Anda telah logout', 'info')
216
  return redirect(url_for('index'))
217
 
218
  @app.route('/admin')
219
- def admin_dashboard():
220
- # Cek login manual untuk hindari redirect loop
221
  if not current_user.is_authenticated:
222
- flash('Silakan login terlebih dahulu!', 'warning')
223
- return redirect(url_for('login', next=request.url))
224
 
225
  if not current_user.is_admin:
226
- flash('⛔ Akses ditolak! Halaman ini hanya untuk admin', 'error')
227
  return redirect(url_for('index'))
228
 
229
  photos = Photo.query.order_by(Photo.upload_date.desc()).all()
230
- total_photos = Photo.query.count()
231
- total_likes = db.session.query(db.func.sum(Photo.likes_count)).scalar() or 0
232
-
233
- return render_template('admin.html',
234
- photos=photos,
235
- total_photos=total_photos,
236
- total_likes=total_likes)
237
 
238
  @app.route('/upload', methods=['POST'])
239
- def upload_photo():
240
- # Cek login manual
241
- if not current_user.is_authenticated:
242
- flash('Silakan login terlebih dahulu!', 'warning')
243
- return redirect(url_for('login'))
244
-
245
- if not current_user.is_admin:
246
- flash('⛔ Akses ditolak!', 'error')
247
  return redirect(url_for('index'))
248
 
249
  if 'photo' not in request.files:
250
- flash(' Tidak ada file yang dipilih', 'error')
251
- return redirect(url_for('admin_dashboard'))
252
 
253
  file = request.files['photo']
254
  caption = request.form.get('caption', '')
255
 
256
  if file.filename == '':
257
- flash(' Tidak ada file yang dipilih', 'error')
258
- return redirect(url_for('admin_dashboard'))
259
-
260
- # Cek ekstensi file
261
- allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
262
- if '.' not in file.filename or file.filename.rsplit('.', 1)[1].lower() not in allowed_extensions:
263
- flash('❌ Format file tidak didukung! Gunakan: PNG, JPG, JPEG, GIF, WEBP', 'error')
264
- return redirect(url_for('admin_dashboard'))
265
-
266
- if file:
267
- # Simpan file dengan nama yang aman
268
- filename = secure_filename(file.filename)
269
- # Tambahkan timestamp untuk menghindari nama duplikat
270
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_')
271
- filename = timestamp + filename
272
-
273
- filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
274
- file.save(filepath)
275
-
276
- # Simpan ke database
277
- photo = Photo(
278
- filename=filename,
279
- caption=caption,
280
- uploader_id=current_user.id
281
- )
282
- db.session.add(photo)
283
- db.session.commit()
284
-
285
- flash(f'✅ Foto berhasil diupload!', 'success')
286
- print(f"✅ Photo uploaded by {current_user.username}: {filename}")
287
 
288
- return redirect(url_for('admin_dashboard'))
 
289
 
290
  @app.route('/delete/<int:photo_id>')
291
- def delete_photo(photo_id):
292
- # Cek login manual
293
- if not current_user.is_authenticated:
294
- flash('Silakan login terlebih dahulu!', 'warning')
295
- return redirect(url_for('login'))
296
-
297
- if not current_user.is_admin:
298
- flash('⛔ Akses ditolak!', 'error')
299
  return redirect(url_for('index'))
300
 
301
  photo = Photo.query.get(photo_id)
302
  if photo:
303
- # Hapus file fisik
304
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], photo.filename)
305
  if os.path.exists(filepath):
306
  os.remove(filepath)
307
- print(f"✅ File deleted: {photo.filename}")
308
 
309
- # Hapus dari database
310
  db.session.delete(photo)
311
  db.session.commit()
312
- flash('Foto berhasil dihapus!', 'success')
313
- else:
314
- flash('❌ Foto tidak ditemukan!', 'error')
315
 
316
- return redirect(url_for('admin_dashboard'))
317
 
318
  @app.route('/like/<int:photo_id>', methods=['POST'])
319
- def like_photo(photo_id):
320
- # Cek login manual
321
  if not current_user.is_authenticated:
322
- return jsonify({'error': 'Silakan login terlebih dahulu'}), 401
323
 
324
  photo = Photo.query.get(photo_id)
325
  if not photo:
326
- return jsonify({'error': 'Foto tidak ditemukan'}), 404
327
 
328
- # Cek apakah user sudah like
329
  like = Like.query.filter_by(user_id=current_user.id, photo_id=photo_id).first()
330
 
331
  if like:
332
- # Unlike
333
  db.session.delete(like)
334
  photo.likes_count -= 1
335
  liked = False
336
  else:
337
- # Like
338
  like = Like(user_id=current_user.id, photo_id=photo_id)
339
  db.session.add(like)
340
  photo.likes_count += 1
341
  liked = True
342
 
343
  db.session.commit()
344
-
345
- return jsonify({
346
- 'liked': liked,
347
- 'likes': photo.likes_count
348
- })
349
 
350
  @app.route('/download/<int:photo_id>')
351
- def download_photo(photo_id):
352
  photo = Photo.query.get(photo_id)
353
  if photo:
354
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], photo.filename)
355
  if os.path.exists(filepath):
356
  return send_file(filepath, as_attachment=True, download_name=photo.filename)
357
 
358
- flash('File tidak ditemukan!', 'error')
359
  return redirect(url_for('index'))
360
 
361
- @app.route('/slideshow_photos')
362
- def slideshow_photos():
363
- # Ambil 10 foto terbaru untuk slideshow loading
364
  photos = Photo.query.order_by(Photo.upload_date.desc()).limit(10).all()
365
- photos_data = [{'filename': photo.filename, 'caption': photo.caption} for photo in photos]
366
- return jsonify(photos_data)
367
 
368
- @app.route('/list_admins')
369
- def list_admins():
370
- """Route publik untuk melihat daftar admin (tanpa password)"""
371
  admins = User.query.filter_by(is_admin=True).all()
372
- admin_list = [{'username': a.username, 'is_admin': a.is_admin} for a in admins]
373
  return jsonify({
374
- 'total_admins': len(admin_list),
375
- 'admins': admin_list
376
  })
377
 
378
- @app.route('/health')
379
- def health():
380
- return jsonify({'status': 'ok', 'time': datetime.now().isoformat()})
381
-
382
- # Error handlers
383
- @app.errorhandler(404)
384
- def not_found_error(error):
385
- flash('Halaman tidak ditemukan!', 'error')
386
- return redirect(url_for('index'))
387
-
388
- @app.errorhandler(500)
389
- def internal_error(error):
390
- db.session.rollback()
391
- flash('Terjadi kesalahan internal server!', 'error')
392
- return redirect(url_for('index'))
393
-
394
- @app.errorhandler(413)
395
- def too_large_error(error):
396
- flash('File terlalu besar! Maksimal 50MB', 'error')
397
- return redirect(url_for('admin_dashboard'))
398
-
399
- # Untuk Hugging Face
400
- if __name__ == "__main__":
401
  port = int(os.environ.get('PORT', 7860))
402
  app.run(host='0.0.0.0', port=port, debug=False)
 
6
  from werkzeug.security import generate_password_hash, check_password_hash
7
  from werkzeug.utils import secure_filename
8
  from datetime import datetime
 
9
 
10
  app = Flask(__name__)
11
  app.config['SECRET_KEY'] = 'angkatan-rahasia-2024-2026'
12
  app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///gallery.db'
13
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
14
  app.config['UPLOAD_FOLDER'] = 'static/uploads'
15
+ app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024
 
16
 
17
+ # Buat folder upload
18
  os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
19
 
20
+ # Database
21
  db = SQLAlchemy(app)
22
 
23
  # Login manager
24
  login_manager = LoginManager()
25
  login_manager.init_app(app)
26
+ login_manager.login_view = 'login' # Penting!
 
 
 
 
 
27
 
28
+ # Models
29
  class User(UserMixin, db.Model):
30
  __tablename__ = 'users'
 
31
  id = db.Column(db.Integer, primary_key=True)
32
  username = db.Column(db.String(80), unique=True, nullable=False)
33
  password_hash = db.Column(db.String(200), nullable=False)
34
  is_admin = db.Column(db.Boolean, default=False)
35
  created_at = db.Column(db.DateTime, default=datetime.utcnow)
36
 
 
 
 
 
37
  def set_password(self, password):
38
  self.password_hash = generate_password_hash(password)
39
 
 
42
 
43
  class Photo(db.Model):
44
  __tablename__ = 'photos'
 
45
  id = db.Column(db.Integer, primary_key=True)
46
  filename = db.Column(db.String(200), nullable=False)
47
  caption = db.Column(db.String(500))
 
49
  uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
50
  likes_count = db.Column(db.Integer, default=0)
51
 
52
+ uploader = db.relationship('User', backref='photos')
53
+ likes = db.relationship('Like', backref='photo', cascade='all, delete-orphan')
54
 
55
  class Like(db.Model):
56
  __tablename__ = 'likes'
 
57
  id = db.Column(db.Integer, primary_key=True)
58
  user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
59
  photo_id = db.Column(db.Integer, db.ForeignKey('photos.id'), nullable=False)
60
  created_at = db.Column(db.DateTime, default=datetime.utcnow)
61
 
62
+ __table_args__ = (db.UniqueConstraint('user_id', 'photo_id'),)
 
63
 
64
  @login_manager.user_loader
65
  def load_user(user_id):
66
+ return User.query.get(int(user_id))
 
 
 
67
 
68
+ # CREATE ADMIN USERS (11 admin)
69
  with app.app_context():
 
70
  db.create_all()
71
+ print("✅ Database siap!")
72
+
73
+ admins = [
74
+ {'username': 'Hilbraaaam', 'password': 'Ketua Angkatan 24-26'},
75
+ {'username': 'Hudzaifahh', 'password': 'Wakil ketua Angkatan 24-26'},
76
+ {'username': 'Rafasyahh', 'password': 'Humas Kesayangan'},
77
+ {'username': 'Elazzam', 'password': 'Eos 800 D'},
78
+ {'username': 'Azzam Diq', 'password': 'Shidiq'},
79
+ {'username': 'Dzikrii', 'password': 'Bayar woe'},
80
+ {'username': 'Ibrahim', 'password': 'Mboh'},
81
+ {'username': 'Yusupp', 'password': 'Bangun'},
82
+ {'username': 'Azzam JR', 'password': 'Saturn'},
83
+ {'username': 'MK Azzam', 'password': 'Aneka Gold'},
84
+ {'username': 'Sami abd', 'password': 'TamTam'}
85
+ ]
86
+
87
+ for admin_data in admins:
88
+ existing = User.query.filter_by(username=admin_data['username']).first()
89
+ if not existing:
90
+ admin = User(username=admin_data['username'], is_admin=True)
91
+ admin.set_password(admin_data['password'])
92
+ db.session.add(admin)
93
+ print(f"✓ Admin: {admin_data['username']}")
94
 
95
+ db.session.commit()
96
+ print(f"✅ Total admin: {User.query.filter_by(is_admin=True).count()}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ # ROUTES - SEDERHANA SAJA
99
  @app.route('/')
100
  def index():
101
+ photos = Photo.query.order_by(Photo.upload_date.desc()).all()
 
 
 
 
 
 
102
  trending = Photo.query.order_by(Photo.likes_count.desc()).limit(3).all()
 
103
  return render_template('index.html', photos=photos, trending=trending)
104
 
105
  @app.route('/login', methods=['GET', 'POST'])
106
  def login():
107
  # Kalau sudah login, redirect ke home
108
  if current_user.is_authenticated:
 
 
109
  return redirect(url_for('index'))
110
 
111
  if request.method == 'POST':
112
  username = request.form.get('username')
113
  password = request.form.get('password')
 
 
 
114
 
115
  user = User.query.filter_by(username=username).first()
116
 
117
  if user and user.check_password(password):
118
+ login_user(user)
119
+ flash('Login berhasil!', 'success')
 
 
 
 
 
120
 
121
+ # Redirect berdasarkan role
122
  if user.is_admin:
123
+ return redirect(url_for('admin'))
124
  return redirect(url_for('index'))
125
  else:
126
+ flash('Username atau password salah!', 'error')
 
127
 
128
  return render_template('login.html')
129
 
130
  @app.route('/logout')
131
  def logout():
132
  logout_user()
133
+ flash('Anda telah logout', 'info')
134
  return redirect(url_for('index'))
135
 
136
  @app.route('/admin')
137
+ def admin():
138
+ # Proteksi manual
139
  if not current_user.is_authenticated:
140
+ flash('Silakan login dulu!', 'warning')
141
+ return redirect(url_for('login'))
142
 
143
  if not current_user.is_admin:
144
+ flash('Halaman khusus admin!', 'error')
145
  return redirect(url_for('index'))
146
 
147
  photos = Photo.query.order_by(Photo.upload_date.desc()).all()
148
+ return render_template('admin.html', photos=photos)
 
 
 
 
 
 
149
 
150
  @app.route('/upload', methods=['POST'])
151
+ def upload():
152
+ # Proteksi manual
153
+ if not current_user.is_authenticated or not current_user.is_admin:
154
+ flash('Akses ditolak!', 'error')
 
 
 
 
155
  return redirect(url_for('index'))
156
 
157
  if 'photo' not in request.files:
158
+ flash('Pilih file dulu!', 'error')
159
+ return redirect(url_for('admin'))
160
 
161
  file = request.files['photo']
162
  caption = request.form.get('caption', '')
163
 
164
  if file.filename == '':
165
+ flash('Pilih file dulu!', 'error')
166
+ return redirect(url_for('admin'))
167
+
168
+ # Simpan file
169
+ filename = secure_filename(file.filename)
170
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_')
171
+ filename = timestamp + filename
172
+ file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
173
+
174
+ # Simpan ke DB
175
+ photo = Photo(filename=filename, caption=caption, uploader_id=current_user.id)
176
+ db.session.add(photo)
177
+ db.session.commit()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ flash('Foto berhasil diupload!', 'success')
180
+ return redirect(url_for('admin'))
181
 
182
  @app.route('/delete/<int:photo_id>')
183
+ def delete(photo_id):
184
+ # Proteksi manual
185
+ if not current_user.is_authenticated or not current_user.is_admin:
186
+ flash('Akses ditolak!', 'error')
 
 
 
 
187
  return redirect(url_for('index'))
188
 
189
  photo = Photo.query.get(photo_id)
190
  if photo:
191
+ # Hapus file
192
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], photo.filename)
193
  if os.path.exists(filepath):
194
  os.remove(filepath)
 
195
 
 
196
  db.session.delete(photo)
197
  db.session.commit()
198
+ flash('Foto dihapus!', 'success')
 
 
199
 
200
+ return redirect(url_for('admin'))
201
 
202
  @app.route('/like/<int:photo_id>', methods=['POST'])
203
+ def like(photo_id):
 
204
  if not current_user.is_authenticated:
205
+ return jsonify({'error': 'Login dulu'}), 401
206
 
207
  photo = Photo.query.get(photo_id)
208
  if not photo:
209
+ return jsonify({'error': 'Foto tidak ada'}), 404
210
 
 
211
  like = Like.query.filter_by(user_id=current_user.id, photo_id=photo_id).first()
212
 
213
  if like:
 
214
  db.session.delete(like)
215
  photo.likes_count -= 1
216
  liked = False
217
  else:
 
218
  like = Like(user_id=current_user.id, photo_id=photo_id)
219
  db.session.add(like)
220
  photo.likes_count += 1
221
  liked = True
222
 
223
  db.session.commit()
224
+ return jsonify({'liked': liked, 'likes': photo.likes_count})
 
 
 
 
225
 
226
  @app.route('/download/<int:photo_id>')
227
+ def download(photo_id):
228
  photo = Photo.query.get(photo_id)
229
  if photo:
230
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], photo.filename)
231
  if os.path.exists(filepath):
232
  return send_file(filepath, as_attachment=True, download_name=photo.filename)
233
 
234
+ flash('File tidak ada!', 'error')
235
  return redirect(url_for('index'))
236
 
237
+ @app.route('/slideshow')
238
+ def slideshow():
 
239
  photos = Photo.query.order_by(Photo.upload_date.desc()).limit(10).all()
240
+ return jsonify([{'filename': p.filename} for p in photos])
 
241
 
242
+ @app.route('/cek_admin')
243
+ def cek_admin():
 
244
  admins = User.query.filter_by(is_admin=True).all()
 
245
  return jsonify({
246
+ 'total': len(admins),
247
+ 'usernames': [a.username for a in admins]
248
  })
249
 
250
+ if __name__ == '__main__':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  port = int(os.environ.get('PORT', 7860))
252
  app.run(host='0.0.0.0', port=port, debug=False)