| from flask import Flask, send_from_directory, request, jsonify, Response |
| import requests, time, threading, json, os |
| import cloudinary |
| import cloudinary.uploader |
| import cloudinary.api |
|
|
| app = Flask(__name__, static_folder='static') |
|
|
| |
| cloudinary.config( |
| cloud_name = "db6wrclc7", |
| api_key = "371945239656253", |
| api_secret = "ZgN-B2lyAz6zk2MI2N5b_1dc1r8", |
| secure = True |
| ) |
|
|
| |
| FIREBASE_API_KEY = "AIzaSyB9--Onx3-_YjD-YzblhZjaWSVVqTQJ1lU" |
| FIRESTORE_BASE = "https://firestore.googleapis.com/v1/projects/vservers1/databases/(default)/documents" |
|
|
| |
| |
| |
|
|
| @app.route("/drive/upload", methods=["POST"]) |
| def drive_upload(): |
| try: |
| elev_vpass = request.form.get("elevId", "unknown") |
| materie = request.form.get("materieId", "general") |
| f = request.files.get("file") |
| if not f: |
| return jsonify({"error": "no file"}), 400 |
|
|
| file_bytes = f.read() |
| file_name = f.filename or "fisier" |
|
|
| print(f"[UPLOAD] {elev_vpass}/{materie}/{file_name} ({len(file_bytes)} bytes)") |
|
|
| |
| folder = f"SGE/{elev_vpass}/{materie}" |
|
|
| |
| import io |
| result = cloudinary.uploader.upload( |
| io.BytesIO(file_bytes), |
| folder = folder, |
| public_id = file_name, |
| resource_type = "raw", |
| use_filename = True, |
| unique_filename = False, |
| overwrite = False, |
| context = f"uploaded_at={int(time.time())}|original_name={file_name}", |
| ) |
|
|
| print(f"[UPLOAD] OK β {result['public_id']}") |
| _fs_log("upload", {"elevVpass": elev_vpass, "materie": materie, |
| "fisier": file_name, "fileId": result["public_id"]}) |
|
|
| return jsonify({ |
| "fileId": result["public_id"], |
| "name": file_name, |
| "size": result.get("bytes", 0), |
| "url": result.get("secure_url", ""), |
| }) |
|
|
| except Exception as e: |
| import traceback; traceback.print_exc() |
| return jsonify({"error": str(e)}), 500 |
|
|
|
|
| @app.route("/drive/list", methods=["GET"]) |
| def drive_list(): |
| try: |
| elev_vpass = request.args.get("elevId", "") |
| materie = request.args.get("materieId", "") |
| folder = f"SGE/{elev_vpass}/{materie}" |
|
|
| result = cloudinary.api.resources( |
| type = "upload", |
| resource_type = "raw", |
| prefix = folder + "/", |
| max_results = 100, |
| ) |
|
|
| files = [] |
| for r in result.get("resources", []): |
| pid = r["public_id"] |
| |
| ctx = r.get("context", {}).get("custom", {}) |
| name = ctx.get("original_name", "") |
| if not name: |
| |
| name = pid.split("/")[-1] |
| files.append({ |
| "id": pid, |
| "name": name, |
| "size": r.get("bytes", 0), |
| "mimeType": r.get("resource_type", "raw"), |
| "createdTime": r.get("created_at", ""), |
| "url": r.get("secure_url", ""), |
| }) |
|
|
| return jsonify({"files": files}) |
|
|
| except cloudinary.exceptions.NotFound: |
| return jsonify({"files": []}) |
| except Exception as e: |
| print(f"[LIST ERROR] {e}") |
| return jsonify({"files": [], "error": str(e)}) |
|
|
|
|
| @app.route("/drive/download/<path:file_id>", methods=["GET"]) |
| def drive_download(file_id): |
| try: |
| |
| url = cloudinary.utils.cloudinary_url( |
| file_id, |
| resource_type = "raw", |
| type = "upload", |
| )[0] |
| |
| from flask import redirect |
| return redirect(url) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
|
|
| @app.route("/drive/delete/<path:file_id>", methods=["DELETE"]) |
| def drive_delete(file_id): |
| try: |
| cloudinary.uploader.destroy(file_id, resource_type="raw") |
| return jsonify({"ok": True}) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
|
|
| @app.route("/drive/test", methods=["GET"]) |
| def drive_test(): |
| try: |
| result = cloudinary.api.ping() |
| return jsonify({"ok": True, "cloudinary": result}) |
| except Exception as e: |
| return jsonify({"ok": False, "error": str(e)}), 500 |
|
|
|
|
| |
| |
| |
|
|
| def _to_fields(data): |
| fields = {} |
| for k, v in data.items(): |
| if v is None: fields[k] = {"nullValue": None} |
| elif isinstance(v, bool): fields[k] = {"booleanValue": v} |
| elif isinstance(v, int): fields[k] = {"integerValue": str(v)} |
| else: fields[k] = {"stringValue": str(v)} |
| return fields |
|
|
| def _fs_add(col, data): |
| r = requests.post(f"{FIRESTORE_BASE}/{col}?key={FIREBASE_API_KEY}", |
| json={"fields": _to_fields(data)}, timeout=15) |
| return r.status_code == 200 |
|
|
| def _fs_log(tip, extra={}): |
| try: |
| payload = {"tip": tip, "ts": int(time.time()), **extra} |
| _fs_add("loguri", payload) |
| except: pass |
|
|
|
|
| |
| @app.route("/api/log", methods=["POST"]) |
| def api_log(): |
| try: |
| data = request.get_json(silent=True) or {} |
| _fs_log(data.get("tip","event"), {k:v for k,v in data.items() if k!="tip"}) |
| return jsonify({"ok": True}) |
| except Exception as e: |
| return jsonify({"error": str(e)}), 500 |
|
|
|
|
| |
| @app.route("/api/logs", methods=["GET"]) |
| def api_logs(): |
| try: |
| r = requests.get(f"{FIRESTORE_BASE}/loguri?key={FIREBASE_API_KEY}&pageSize=200", timeout=15) |
| docs = r.json().get("documents", []) |
| logs = [] |
| for d in docs: |
| f = d.get("fields", {}) |
| entry = {} |
| for k, v in f.items(): |
| entry[k] = v.get("stringValue") or v.get("integerValue") or v.get("booleanValue") or "" |
| entry["_id"] = d["name"].split("/")[-1] |
| logs.append(entry) |
| logs.sort(key=lambda x: int(x.get("ts",0)), reverse=True) |
| return jsonify({"logs": logs[:150]}) |
| except Exception as e: |
| return jsonify({"error": str(e), "logs": []}), 500 |
|
|
|
|
| |
| _online = {} |
|
|
| @app.route("/api/ping", methods=["POST"]) |
| def api_ping(): |
| data = request.get_json(silent=True) or {} |
| key = f"{data.get('vpass','?')}_{data.get('role','?')}" |
| _online[key] = {"ts": time.time(), "vpass": data.get("vpass","?"), |
| "role": data.get("role","?"), "name": data.get("name","")} |
| now = time.time() |
| expired = [k for k,v in _online.items() if now - v["ts"] > 120] |
| for k in expired: del _online[k] |
| return jsonify({"ok": True}) |
|
|
| @app.route("/api/online", methods=["GET"]) |
| def api_online(): |
| now = time.time() |
| active = {k:v for k,v in _online.items() if now - v["ts"] <= 120} |
| return jsonify({ |
| "total": len(active), |
| "elevi": [v for v in active.values() if v["role"]=="elev"], |
| "profesori": [v for v in active.values() if v["role"]=="profesor"], |
| }) |
|
|
|
|
| |
| |
| |
| def cleanup_old_files(): |
| while True: |
| try: |
| time.sleep(3600) |
| print("[CLEANUP] Verific fisiere vechi...") |
| limit_ts = int(time.time()) - 7 * 24 * 3600 |
| result = cloudinary.api.resources( |
| type="upload", resource_type="raw", |
| prefix="SGE/", max_results=500, |
| ) |
| deleted = 0 |
| for r in result.get("resources", []): |
| created = r.get("created_at","") |
| if created: |
| try: |
| from datetime import datetime, timezone |
| dt = datetime.fromisoformat(created.replace("Z","+00:00")) |
| if int(dt.timestamp()) < limit_ts: |
| cloudinary.uploader.destroy(r["public_id"], resource_type="raw") |
| deleted += 1 |
| print(f"[CLEANUP] Sters: {r['public_id']}") |
| _fs_log("cleanup_delete", {"fisier": r["public_id"]}) |
| except: pass |
| print(f"[CLEANUP] Gata: {deleted} sterse.") |
| except Exception as ex: |
| print(f"[CLEANUP] Eroare: {ex}") |
| time.sleep(300) |
|
|
|
|
| |
| |
| |
| ELEVI = [ |
| {"pozitie":1,"nume":"Andronic Aniela","vpassId":"AA-0001"}, |
| {"pozitie":2,"nume":"Badan Alexandra","vpassId":"BA-0002"}, |
| {"pozitie":3,"nume":"Bunescu Lavinia","vpassId":"BL-0003"}, |
| {"pozitie":4,"nume":"Bunescu Lorena","vpassId":"BL-0004"}, |
| {"pozitie":5,"nume":"Burdujan Damian","vpassId":"BD-0005"}, |
| {"pozitie":6,"nume":"Ceban Madalina","vpassId":"CM-0006"}, |
| {"pozitie":7,"nume":"Ceban Valeria","vpassId":"CV-0007"}, |
| {"pozitie":8,"nume":"Cebanu Nichita","vpassId":"CN-0008"}, |
| {"pozitie":9,"nume":"Chele Ilinca","vpassId":"CI-0009"}, |
| {"pozitie":10,"nume":"Chihai Mirela","vpassId":"CM-0010"}, |
| {"pozitie":11,"nume":"Cojocari Ciprian","vpassId":"CC-0011"}, |
| {"pozitie":12,"nume":"Creciun Leonard","vpassId":"CL-0012"}, |
| {"pozitie":13,"nume":"Lozovan Sanda","vpassId":"LS-0013"}, |
| {"pozitie":14,"nume":"Lungu Sergiu","vpassId":"LS-0014"}, |
| {"pozitie":15,"nume":"Mahnea Adelina","vpassId":"MA-0015"}, |
| {"pozitie":16,"nume":"Morcan Ina","vpassId":"MI-0016"}, |
| {"pozitie":17,"nume":"Morcan Ion","vpassId":"MI-0017"}, |
| {"pozitie":18,"nume":"Popa Cristina","vpassId":"PC-0018"}, |
| {"pozitie":19,"nume":"Pricob Adelina","vpassId":"PA-0019"}, |
| {"pozitie":20,"nume":"Prinos Catalina","vpassId":"PC-0020"}, |
| {"pozitie":21,"nume":"Radu Daniela","vpassId":"RD-0021"}, |
| {"pozitie":22,"nume":"Rosca Victor","vpassId":"RV-0022"}, |
| {"pozitie":23,"nume":"Rotari Cristian","vpassId":"RC-0023"}, |
| {"pozitie":24,"nume":"Rotaru Camelia","vpassId":"RC-0024"}, |
| {"pozitie":25,"nume":"Sambris Stanislav","vpassId":"SS-0025"}, |
| {"pozitie":26,"nume":"Sirbu Delia","vpassId":"SD-0026"}, |
| {"pozitie":27,"nume":"Soltoian Elena","vpassId":"SE-0027"}, |
| {"pozitie":28,"nume":"Tabuncic Chiril","vpassId":"TC-0028"}, |
| {"pozitie":29,"nume":"Turcan Gabriel","vpassId":"TG-0029"}, |
| {"pozitie":30,"nume":"Turcan Mihai","vpassId":"TM-0030"}, |
| {"pozitie":31,"nume":"Turcanu Ilie","vpassId":"TI-0031"}, |
| {"pozitie":32,"nume":"Virlan Daniela","vpassId":"VD-0032"}, |
| ] |
|
|
| def auto_seed(): |
| time.sleep(4) |
| try: |
| r = requests.get(f"{FIRESTORE_BASE}/elevi?key={FIREBASE_API_KEY}&pageSize=1", timeout=15) |
| if r.json().get("documents"): |
| print("[SGE] DB populat β seed omis."); return |
| print("[SGE] Seed automat...") |
| ok = 0 |
| for e in ELEVI: |
| if _fs_add("elevi", {"nume":e["nume"],"vpassId":e["vpassId"], |
| "pozitie":e["pozitie"],"pin":None,"confirmed":False}): |
| ok += 1 |
| time.sleep(0.12) |
| print(f"[SGE] Seed gata: {ok}/{len(ELEVI)}") |
| except Exception as ex: |
| print(f"[SGE] Seed eroare: {ex}") |
|
|
| threading.Thread(target=auto_seed, daemon=True).start() |
| threading.Thread(target=cleanup_old_files, daemon=True).start() |
|
|
| |
| @app.route("/") |
| def index(): return send_from_directory("static", "index.html") |
|
|
| @app.errorhandler(404) |
| def not_found(e): return send_from_directory("static", "503.html"), 404 |
|
|
| @app.route("/<path:filename>") |
| def serve_files(filename): |
| try: return send_from_directory("static", filename) |
| except: return send_from_directory("static", "503.html"), 404 |
|
|
| if __name__ == "__main__": |
| app.run(host="0.0.0.0", port=7860, debug=False) |
|
|