Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files- Dockerfile +1 -18
- app.py +29 -130
Dockerfile
CHANGED
|
@@ -1,33 +1,16 @@
|
|
| 1 |
FROM python:3.9-slim
|
| 2 |
|
| 3 |
-
|
| 4 |
-
RUN apt-get update && apt-get install -y rclone procps && rm -rf /var/lib/apt/lists/*
|
| 5 |
|
| 6 |
WORKDIR /home/user/app
|
| 7 |
|
| 8 |
-
COPY requirements.txt .
|
| 9 |
-
RUN pip install --no-cache-dir -r requirements.txt
|
| 10 |
-
|
| 11 |
COPY . .
|
| 12 |
|
| 13 |
-
# এন্ট্রি পয়েন্ট স্ক্রিপ্ট তৈরি
|
| 14 |
RUN echo '#!/bin/bash\n\
|
| 15 |
mkdir -p /config/rclone\n\
|
| 16 |
-
# সিক্রেট ডাটা চেক\n\
|
| 17 |
-
if [ -z "$RCLONE_CONF_DATA" ]; then\n\
|
| 18 |
-
echo "ERROR: RCLONE_CONF_DATA is empty! Set it in Secrets."\n\
|
| 19 |
-
exit 1\n\
|
| 20 |
-
fi\n\
|
| 21 |
-
\n\
|
| 22 |
echo "$RCLONE_CONF_DATA" > /config/rclone/rclone.conf\n\
|
| 23 |
-
\n\
|
| 24 |
-
# ব্যাকগ্রাউন্ডে Rclone চালু (Port 8080)\n\
|
| 25 |
-
rclone serve webdav gdrive: --addr :8080 --user "$FTP_USER" --pass "$FTP_PASS" --config /config/rclone/rclone.conf --vfs-cache-mode full &\n\
|
| 26 |
-
\n\
|
| 27 |
-
# মেইন পাইথন গেটওয়ে চালু\n\
|
| 28 |
exec python app.py\n\
|
| 29 |
' > /entrypoint.sh && chmod +x /entrypoint.sh
|
| 30 |
|
| 31 |
EXPOSE 7860
|
| 32 |
-
|
| 33 |
CMD ["/entrypoint.sh"]
|
|
|
|
| 1 |
FROM python:3.9-slim
|
| 2 |
|
| 3 |
+
RUN apt-get update && apt-get install -y rclone && rm -rf /var/lib/apt/lists/*
|
|
|
|
| 4 |
|
| 5 |
WORKDIR /home/user/app
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
COPY . .
|
| 8 |
|
|
|
|
| 9 |
RUN echo '#!/bin/bash\n\
|
| 10 |
mkdir -p /config/rclone\n\
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
echo "$RCLONE_CONF_DATA" > /config/rclone/rclone.conf\n\
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
exec python app.py\n\
|
| 13 |
' > /entrypoint.sh && chmod +x /entrypoint.sh
|
| 14 |
|
| 15 |
EXPOSE 7860
|
|
|
|
| 16 |
CMD ["/entrypoint.sh"]
|
app.py
CHANGED
|
@@ -1,136 +1,35 @@
|
|
| 1 |
import os
|
| 2 |
-
import
|
| 3 |
-
import
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
app = Flask(__name__)
|
| 7 |
-
|
| 8 |
-
# CONFIGURATION
|
| 9 |
-
WEBDAV_URL = "http://127.0.0.1:8080"
|
| 10 |
-
USER = os.getenv("FTP_USER", "admin")
|
| 11 |
-
PASS = os.getenv("FTP_PASS", "123456")
|
| 12 |
-
|
| 13 |
-
# MODERN UI DESIGN (Premium NoteLM Theme)
|
| 14 |
-
HTML_TEMPLATE = """
|
| 15 |
-
<!DOCTYPE html>
|
| 16 |
-
<html lang="en">
|
| 17 |
-
<head>
|
| 18 |
-
<meta charset="UTF-8">
|
| 19 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 20 |
-
<title>NoteLM Premium Drive</title>
|
| 21 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 22 |
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
| 23 |
-
<style>
|
| 24 |
-
body { background: #0b0f1a; color: #e2e8f0; font-family: 'Segoe UI', sans-serif; }
|
| 25 |
-
.glass-card { background: rgba(23, 32, 51, 0.7); backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5); }
|
| 26 |
-
.accent-gradient { background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%); }
|
| 27 |
-
.status-dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; animation: pulse 2s infinite; }
|
| 28 |
-
@keyframes pulse { 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); } 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); } }
|
| 29 |
-
</style>
|
| 30 |
-
</head>
|
| 31 |
-
<body class="min-h-screen p-4 md:p-10 flex flex-col items-center">
|
| 32 |
-
<div class="w-full max-w-5xl">
|
| 33 |
-
<div class="glass-card rounded-2xl p-6 mb-8 flex justify-between items-center">
|
| 34 |
-
<div class="flex items-center gap-4">
|
| 35 |
-
<div class="accent-gradient p-3 rounded-xl shadow-lg">
|
| 36 |
-
<i class="fa-solid fa-server text-white text-xl"></i>
|
| 37 |
-
</div>
|
| 38 |
-
<div>
|
| 39 |
-
<h1 class="text-2xl font-bold tracking-tight">NoteLM <span class="text-indigo-400">Cloud</span></h1>
|
| 40 |
-
<p class="text-xs text-gray-500 uppercase tracking-widest font-semibold">Secure Storage Gateway</p>
|
| 41 |
-
</div>
|
| 42 |
-
</div>
|
| 43 |
-
<div class="flex items-center gap-3 bg-black/20 px-4 py-2 rounded-full border border-white/5">
|
| 44 |
-
<span class="status-dot bg-green-500"></span>
|
| 45 |
-
<span class="text-xs font-medium text-green-400">System Online</span>
|
| 46 |
-
</div>
|
| 47 |
-
</div>
|
| 48 |
-
|
| 49 |
-
<div class="glass-card rounded-2xl overflow-hidden shadow-2xl">
|
| 50 |
-
<div class="p-6 border-b border-white/5 flex justify-between items-center">
|
| 51 |
-
<h3 class="font-bold text-lg">Connected Devices</h3>
|
| 52 |
-
<button class="text-xs bg-indigo-500/10 hover:bg-indigo-500/20 text-indigo-400 px-4 py-2 rounded-lg transition border border-indigo-500/20">
|
| 53 |
-
<i class="fa-solid fa-sync-alt mr-2"></i>Refresh
|
| 54 |
-
</button>
|
| 55 |
-
</div>
|
| 56 |
-
<div class="overflow-x-auto">
|
| 57 |
-
<table class="w-full text-left">
|
| 58 |
-
<thead class="bg-white/5 text-gray-400 text-[11px] uppercase tracking-wider">
|
| 59 |
-
<tr>
|
| 60 |
-
<th class="px-6 py-4">Resource</th>
|
| 61 |
-
<th class="px-6 py-4">Protocol</th>
|
| 62 |
-
<th class="px-6 py-4">Security</th>
|
| 63 |
-
<th class="px-6 py-4 text-center">Status</th>
|
| 64 |
-
</tr>
|
| 65 |
-
</thead>
|
| 66 |
-
<tbody class="divide-y divide-white/5">
|
| 67 |
-
<tr class="hover:bg-white/[0.02] transition-colors">
|
| 68 |
-
<td class="px-6 py-5 flex items-center gap-3">
|
| 69 |
-
<i class="fa-brands fa-google-drive text-blue-400 text-lg"></i>
|
| 70 |
-
<span class="text-sm font-medium">Google Drive Root</span>
|
| 71 |
-
</td>
|
| 72 |
-
<td class="px-6 py-5">
|
| 73 |
-
<span class="bg-blue-500/10 text-blue-400 text-[10px] px-2 py-1 rounded-md font-bold border border-blue-500/20">WEBDAV / FTP</span>
|
| 74 |
-
</td>
|
| 75 |
-
<td class="px-6 py-5 text-sm text-gray-400">AES-256 Encrypted</td>
|
| 76 |
-
<td class="px-6 py-5 text-center">
|
| 77 |
-
<i class="fa-solid fa-circle-check text-green-500"></i>
|
| 78 |
-
</td>
|
| 79 |
-
</tr>
|
| 80 |
-
</tbody>
|
| 81 |
-
</table>
|
| 82 |
-
</div>
|
| 83 |
-
</div>
|
| 84 |
-
|
| 85 |
-
<footer class="mt-10 text-center">
|
| 86 |
-
<p class="text-gray-600 text-[10px] uppercase tracking-[0.3em]">Encrypted Bridge by Mahadi • 2026</p>
|
| 87 |
-
</footer>
|
| 88 |
-
</div>
|
| 89 |
-
</body>
|
| 90 |
-
</html>
|
| 91 |
-
"""
|
| 92 |
-
|
| 93 |
-
@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PROPFIND', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'])
|
| 94 |
-
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PROPFIND', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'])
|
| 95 |
-
def proxy_gateway(path):
|
| 96 |
try:
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
print(f"[TRACK] Browser access detected. Serving UI.")
|
| 100 |
-
return render_template_string(HTML_TEMPLATE)
|
| 101 |
-
|
| 102 |
-
# WebDAV রিকোয়েস্ট হ্যান্ডেল করা (App থেকে আসা)
|
| 103 |
-
target_url = f"{WEBDAV_URL}/{path}"
|
| 104 |
-
print(f"[TRACK] Proxying {request.method} request to: {target_url}")
|
| 105 |
-
|
| 106 |
-
# হেডার থেকে 'host' বাদ দিয়ে কপি করা
|
| 107 |
-
headers = {key: value for (key, value) in request.headers if key.lower() != 'host'}
|
| 108 |
-
|
| 109 |
-
# Rclone এ রিকোয়েস্ট পাঠানো
|
| 110 |
-
response = requests.request(
|
| 111 |
-
method=request.method,
|
| 112 |
-
url=target_url,
|
| 113 |
-
headers=headers,
|
| 114 |
-
data=request.get_data(),
|
| 115 |
-
cookies=request.cookies,
|
| 116 |
-
allow_redirects=False,
|
| 117 |
-
auth=(USER, PASS),
|
| 118 |
-
stream=True # বড় ফাইলের জন্য স্ট্রীমিং জরুরি
|
| 119 |
-
)
|
| 120 |
-
|
| 121 |
-
# রেসপন্স হেডার ফিল্টার করা
|
| 122 |
-
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
|
| 123 |
-
resp_headers = [(name, value) for (name, value) in response.raw.headers.items()
|
| 124 |
-
if name.lower() not in excluded_headers]
|
| 125 |
-
|
| 126 |
-
print(f"[TRACK] Success: {response.status_code}")
|
| 127 |
-
return Response(response.iter_content(chunk_size=1024), response.status_code, resp_headers)
|
| 128 |
-
|
| 129 |
except Exception as e:
|
| 130 |
-
|
| 131 |
-
error_msg = traceback.format_exc()
|
| 132 |
-
print(f"--- ERROR CAPTURED --- \n{error_msg}\n-----------------------")
|
| 133 |
-
return f"NoteLM Gateway Error: {str(e)}", 500
|
| 134 |
|
| 135 |
if __name__ == "__main__":
|
| 136 |
-
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
+
import subprocess
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
def start_services():
|
| 6 |
+
# সিক্রেট থেকে তথ্য নেওয়া
|
| 7 |
+
USER = os.getenv("FTP_USER", "admin")
|
| 8 |
+
PASS = os.getenv("FTP_PASS", "123456")
|
| 9 |
+
RCLONE_CONF = "/config/rclone/rclone.conf"
|
| 10 |
+
|
| 11 |
+
print("[SYSTEM] Starting Rclone WebDAV & Modern UI...")
|
| 12 |
+
|
| 13 |
+
# Rclone কমান্ড যা একই সাথে WebDAV এবং Web UI চালাবে
|
| 14 |
+
# --baseurl ব্যবহার করা হয়েছে যাতে Hugging Face এ পাথ ঠিক থাকে
|
| 15 |
+
cmd = [
|
| 16 |
+
"rclone", "serve", "webdav", "gdrive:",
|
| 17 |
+
"--addr", ":7860",
|
| 18 |
+
"--user", USER,
|
| 19 |
+
"--pass", PASS,
|
| 20 |
+
"--config", RCLONE_CONF,
|
| 21 |
+
"--vfs-cache-mode", "full",
|
| 22 |
+
"--ui", # এটি আধুনিক GUI এনাবেল করবে
|
| 23 |
+
"--ui-no-auth", # মূল লিঙ্কে ঢোকার জন্য UI আলাদা পাসওয়ার্ড চাইবে না (সিকিউরিটি মেইন পাসওয়ার্ডেই থাকবে)
|
| 24 |
+
]
|
| 25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
try:
|
| 27 |
+
process = subprocess.Popen(cmd)
|
| 28 |
+
process.wait()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
except Exception as e:
|
| 30 |
+
print(f"[ERROR] Service crashed: {str(e)}")
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
if __name__ == "__main__":
|
| 33 |
+
# rclone.conf তৈরি হওয়ার জন্য ১ সেকেন্ড অপেক্ষা
|
| 34 |
+
time.sleep(1)
|
| 35 |
+
start_services()
|