├── .gitignore
├── Config
└── config.py
├── Database
└── db.py
├── Functions
└── functions.py
├── LICENSE
├── README.md
├── Telegram
└── client.py
├── Webserver
├── __main__.py
└── auth.py
├── example.env
├── index.html
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.session
6 | # C extensions
7 | *.so
8 | .vscode
9 | .env
10 | # Distribution / packaging
11 | .Python
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | pip-wheel-metadata/
25 | share/python-wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 | MANIFEST
30 |
31 | # PyInstaller
32 | # Usually these files are written by a python script from a template
33 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 | *.manifest
35 | *.spec
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .nox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | *.py,cover
52 | .hypothesis/
53 | .pytest_cache/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | .python-version
87 |
88 | # pipenv
89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
92 | # install all needed dependencies.
93 | #Pipfile.lock
94 |
95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96 | __pypackages__/
97 |
98 | # Celery stuff
99 | celerybeat-schedule
100 | celerybeat.pid
101 |
102 | # SageMath parsed files
103 | *.sage.py
104 |
105 | # Environments
106 | .env
107 | .venv
108 | env/
109 | venv/
110 | ENV/
111 | env.bak/
112 | venv.bak/
113 |
114 | # Spyder project settings
115 | .spyderproject
116 | .spyproject
117 |
118 | # Rope project settings
119 | .ropeproject
120 |
121 | # mkdocs documentation
122 | /site
123 |
124 | # mypy
125 | .mypy_cache/
126 | .dmypy.json
127 | dmypy.json
128 |
129 | # Pyre type checker
130 | .pyre/
131 |
--------------------------------------------------------------------------------
/Config/config.py:
--------------------------------------------------------------------------------
1 | from os import getenv as ge
2 |
3 | from dotenv import load_dotenv
4 |
5 | load_dotenv()
6 |
7 |
8 | class config:
9 | chat_id = int(ge("CHAT_ID"))
10 | api_id = int(ge("API_ID"))
11 | api_hash = ge("API_HASH")
12 | bot_token1 = ge("BOT_TOKEN1")
13 | bot_token2 = ge("BOT_TOKEN2")
14 | bot_token3 = ge("BOT_TOKEN3")
15 | bot_token4 = ge("BOT_TOKEN4")
16 | web_port = int(ge("WEB_PORT"))
17 | db_name = ge("DB_NAME")
18 | db_host = ge("DB_HOST")
19 | db_port = ge("DB_PORT")
20 | db_user = ge("DB_USER")
21 | db_password = ge("DB_PASSWORD")
22 | secret_key = ge("SECRET_KEY")
23 | domain_name = ge("DOMAIN_NAME")
24 |
--------------------------------------------------------------------------------
/Database/db.py:
--------------------------------------------------------------------------------
1 | import random
2 | import string
3 |
4 | import psycopg2
5 |
6 | from Config.config import config
7 | from Functions.functions import data_key
8 |
9 |
10 | class database:
11 | conn = psycopg2.connect(
12 | database=config.db_name,
13 | host=config.db_host,
14 | user=config.db_user,
15 | password=config.db_password,
16 | port=config.db_port,
17 | )
18 | cursor = conn.cursor()
19 |
20 | async def create_file_table(self, table_name):
21 | self.cursor.execute(
22 | f"""CREATE TABLE IF NOT EXISTS {table_name}(
23 | Index Serial PRIMARY KEY,
24 | filename VARCHAR(200) NOT NULL,
25 | FileSize VARCHAR(300) NOT NULL,
26 | MessageID INT UNIQUE NOT NULL,
27 | FileKey VARCHAR(20) UNIQUE NOT NULL,
28 | UserID VARCHAR(50) NOT NULL,
29 | Content VARCHAR(50),
30 | TIME TIMESTAMP NOT NULL);
31 | """
32 | )
33 | self.conn.commit()
34 |
35 | async def create_user_table(self, table_name):
36 | self.cursor.execute(
37 | f"""CREATE TABLE IF NOT EXISTS {table_name}(
38 | Index Serial PRIMARY KEY,
39 | UserName VARCHAR(300) NOT NULL,
40 | UserID VARCHAR(50) UNIQUE NOT NULL);"""
41 | )
42 | self.conn.commit()
43 |
44 | async def insert_file_data(
45 | self,
46 | filename,
47 | fileSize,
48 | MessageID,
49 | FileKey,
50 | UserID,
51 | Content,
52 | Time,
53 | ):
54 | insert = (
55 | filename,
56 | fileSize,
57 | MessageID,
58 | FileKey,
59 | UserID,
60 | Content,
61 | Time,
62 | )
63 | self.cursor.execute(
64 | """INSERT INTO FileData(filename, FileSize, MessageID, FileKey, UserID, CONTENT,TIME)
65 | Values (%s, %s, %s, %s, %s, %s, %s);""",
66 | insert,
67 | )
68 | self.conn.commit()
69 |
70 | async def display_table_data(self, table_name):
71 | self.cursor.execute(f"SELECT * FROM {table_name}")
72 |
73 | async def add_user(self, name):
74 | key = data_key("DRIVO-", 10)
75 | data = (name, key)
76 | self.cursor.execute(
77 | """INSERT INTO UserData(UserName, UserID)
78 | Values (%s, %s)""",
79 | data,
80 | )
81 | self.conn.commit()
82 | return key
83 |
84 | async def login_check(self, key):
85 | self.cursor.execute(
86 | """SELECT Username from Userdata WHERE USERID = %s;""",
87 | (key,),
88 | )
89 | row = self.cursor.fetchone()
90 | return row[0] if row else None
91 |
92 | async def get_uploads(self, key):
93 | self.cursor.execute(
94 | """SELECT filename, content, Filesize, FileKey FROM filedata WHERE USERID = %s;""",
95 | (key,),
96 | )
97 | rows = self.cursor.fetchall()
98 | final_data = []
99 | if rows:
100 | for row in rows:
101 | data = {
102 | "file_name": row[0],
103 | "content": row[1],
104 | "file_size": row[2],
105 | "file_key": row[3],
106 | }
107 | final_data.append(data)
108 | return final_data
109 | else:
110 | return []
111 |
112 | async def deleteFile(self, file_key, User_id):
113 | self.cursor.execute(
114 | """SELECT Filename FROM FileData WHERE Filekey = %s and UserID = %s""",
115 | (
116 | file_key,
117 | User_id,
118 | ),
119 | )
120 | row = self.cursor.fetchone()
121 | if row:
122 | self.cursor.execute(
123 | """DELETE FROM FileData WHERE Filekey = %s and UserID = %s""",
124 | (
125 | file_key,
126 | User_id,
127 | ),
128 | )
129 | self.conn.commit()
130 | return row[0]
131 |
132 | async def getFile(self, file_key, User_id):
133 | self.cursor.execute(
134 | """SELECT MessageID FROM FileData WHERE USERID = %s and Filekey = %s;""",
135 | (User_id, file_key),
136 | )
137 | row = self.cursor.fetchone()
138 | return row[0] if row else None
139 |
140 | async def create_share_table(self):
141 | self.cursor.execute(
142 | """CREATE TABLE IF NOT EXISTS sharedata(
143 | Index Serial PRIMARY KEY,
144 | Token VARCHAR(50000) UNIQUE NOT NULL,
145 | Shorten VARCHAR(500) UNIQUE NOT NULL,
146 | userid VARCHAR(500) NOT NULL,
147 | time INT NOT NULL);
148 | """
149 | )
150 |
151 | async def share_data_add(self, short, token, userid, time):
152 | insert = (token, short, userid, time)
153 | self.cursor.execute(
154 | """INSERT INTO sharedata(token,shorten,userid,time)
155 | Values(%s,%s,%s,%s)""",
156 | insert,
157 | )
158 | self.conn.commit()
159 |
160 | async def share_data_search(self, shorten):
161 | self.cursor.execute(
162 | """SELECT token,time FROM sharedata WHERE Shorten = %s;""",
163 | (shorten,),
164 | )
165 | row = self.cursor.fetchone()
166 | return row[0], row[1] if row else None
167 |
--------------------------------------------------------------------------------
/Functions/functions.py:
--------------------------------------------------------------------------------
1 | """functions used in the project, database commands simplified made more easier"""
2 | import json
3 | import random
4 | import string
5 |
6 | import pyrogram
7 | from cryptography.fernet import Fernet
8 | from pydantic import BaseModel
9 |
10 | from Config.config import config
11 |
12 | # from Webserver.webserver import Share
13 |
14 |
15 | def data_key(type, len):
16 | return type + "".join(
17 | random.choices(string.ascii_uppercase + string.digits, k=len)
18 | )
19 |
20 |
21 | ## Not using conversion anymore
22 | # def convert_bytes(size):
23 | # for x in ["bytes", "KB", "MB", "GB", "TB"]:
24 | # if size < 1024.0:
25 | # return "%3.1f %s" % (size, x)
26 | # size /= 1024.0
27 |
28 | # return size
29 |
30 |
31 | class Share(BaseModel):
32 | userkey: str
33 | filekey: str
34 | exp: float
35 |
36 |
37 | def chunk_stream(client: pyrogram.Client, fileID: str):
38 | for chunk in client.stream_media(str(fileID)):
39 | yield chunk
40 |
41 |
42 | async def file_info(client: pyrogram.client, message_id):
43 | info = await client.get_messages(config.chat_id, message_id)
44 | return (
45 | info.document.file_id,
46 | info.document.file_name,
47 | info.document.file_size,
48 | info.document.mime_type,
49 | )
50 |
51 |
52 | async def encrypt_and_return(share: Share):
53 | encrypt_client = Fernet(config.secret_key)
54 | encrypted = encrypt_client.encrypt(
55 | str(share.__dict__).encode("utf-8")
56 | ).decode("utf-8")
57 | return encrypted
58 |
59 |
60 | async def decrypt_and_return(enc_token):
61 | decrypt_client = Fernet(config.secret_key)
62 | decrypted = (
63 | decrypt_client.decrypt(token=enc_token.encode("utf-8"))
64 | .decode("utf-8")
65 | .replace("'", '"')
66 | )
67 | final_data = json.loads(decrypted)
68 | return final_data
69 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Aarav Arora
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Drivogram Backend
2 | Backend of Drivogram
3 |
4 |
5 | A Telegram based drive's backend written in Python with help of Pyrogram,FastAPI,Postgres
6 |
7 | ## Requirements
8 | - Python3
9 | - Postgres Database
10 | - Telegram Bot Tokens
11 | - Mind :P
12 |
13 | ## Getting Started
14 | - Checkout example.env and fill it according to your values and rename it to .env
15 |
16 | ## Installation
17 | ```console
18 | git clone https://github.com/Axrav/DrivogramBackend
19 | cd DrivogramBackend
20 | pip3 install -U -r requirements.txt
21 | python3 -m Webserver
22 | ```
23 |
24 | ## Features
25 | - Store Unlimited Files (No limit on storage)
26 | - Your files are stored with yourself, you dont have to worry about security issues.
27 | - User based Drive with proper security key authentication
28 | - File stays forever until you delete it.
29 | - Store your data and share whenever you want to share it(without downloading the data)
30 | - Open Sourced ( Make changes as per your requirements, if you know how to do XD)
31 |
32 |
33 |
34 | ## Try?
35 | Want to try? checkout [The CLI tool](https://github.com/Axrav/DrivogramCLI),It is integrated with a hosted server.File storing/sharing/downloading made super easy!
36 |
37 | ## Documentation
38 | As of now,there is no proper documentation for Drivogram
39 |
40 | ## Contributions
41 | - [Fork](https://github.com/Axrav/DrivogramBackend/fork) the Repository
42 | - Commit the changes you like and open a PR
43 |
44 |
45 |
46 | If you feel like adding more to the list, PR's are always welcome!
47 |
48 | ## Bugs,Questions and Support/Discussions
49 | For Questions,Bugs feel free to open an [issue](https://github.com/Axrav/DrivogramBackend/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) or share your opinions!
50 | For any sort of support you can visit [@DrivogramSupport](https://t.me/DrivogramSupport)
51 |
52 |
53 |
54 | ## LICENSE
55 | ```MIT License
56 | Drivogram, Open Source Cloud Drive Storage
57 | Copyright (c) 2022 Aarav Arora
58 |
59 | Permission is hereby granted, free of charge, to any person obtaining a copy
60 | of this software and associated documentation files (the "Software"), to deal
61 | in the Software without restriction, including without limitation the rights
62 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
63 | copies of the Software, and to permit persons to whom the Software is
64 | furnished to do so, subject to the following conditions:
65 |
66 | The above copyright notice and this permission notice shall be included in all
67 | copies or substantial portions of the Software.
68 |
69 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
73 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
74 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
75 | SOFTWARE.```
76 |
77 |
--------------------------------------------------------------------------------
/Telegram/client.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client
2 |
3 | from Config.config import config
4 |
5 | app1 = Client(
6 | "app1",
7 | config.api_id,
8 | config.api_hash,
9 | bot_token=config.bot_token1,
10 | )
11 | app2 = Client(
12 | "app2",
13 | config.api_id,
14 | config.api_hash,
15 | bot_token=config.bot_token2,
16 | )
17 | app3 = Client(
18 | "app3",
19 | config.api_id,
20 | config.api_hash,
21 | bot_token=config.bot_token3,
22 | )
23 | app4 = Client(
24 | "app4",
25 | config.api_id,
26 | config.api_hash,
27 | bot_token=config.bot_token4,
28 | )
29 |
--------------------------------------------------------------------------------
/Webserver/__main__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 |
5 | current = os.path.dirname(os.path.realpath(__file__))
6 | parent = os.path.dirname(current)
7 | sys.path.append(parent)
8 | import asyncio
9 | import json
10 | import random
11 | from io import BytesIO
12 |
13 | import uvicorn
14 | from Crypto.Cipher import AES
15 | from fastapi import Depends, FastAPI, Header, HTTPException, UploadFile
16 | from fastapi.responses import JSONResponse, StreamingResponse, FileResponse
17 | from fastapi.security.api_key import APIKey
18 | from pydantic import BaseModel
19 |
20 | from Database.db import database
21 | from Functions.functions import (
22 | Share,
23 | chunk_stream,
24 | data_key,
25 | decrypt_and_return,
26 | encrypt_and_return,
27 | file_info,
28 | )
29 | from Webserver import auth
30 |
31 | data_object = database()
32 | import nest_asyncio
33 | from pyrogram import idle
34 |
35 | from Config.config import config
36 | from Telegram.client import app1, app2, app3, app4
37 |
38 | chat_id = config.chat_id
39 | choose = [app1, app2, app3, app4]
40 | nest_asyncio.apply()
41 | web = FastAPI()
42 |
43 | from fastapi.middleware.cors import CORSMiddleware
44 |
45 | web.add_middleware(
46 | CORSMiddleware,
47 | allow_origins=["*"],
48 | allow_credentials=True,
49 | allow_methods=["*"],
50 | allow_headers=["*"],
51 | expose_headers=["*"],
52 | )
53 |
54 |
55 | @web.on_event("startup")
56 | async def startup():
57 | async def client_start():
58 | try:
59 | await app1.start()
60 | await app2.start()
61 | await app3.start()
62 | await app4.start()
63 | await idle()
64 | except Exception as e:
65 | print(e)
66 |
67 | asyncio.create_task(client_start())
68 |
69 |
70 | @web.get("/")
71 | async def get_home():
72 | return FileResponse("index.html")
73 |
74 |
75 | @web.post("/api/upload")
76 | async def home(IN_FILE: UploadFile, X_API_KEY: APIKey = Depends(auth.apikey)):
77 | try:
78 | content = await IN_FILE.read()
79 | b = BytesIO(content)
80 | b.name = IN_FILE.filename
81 | random_client = random.choice(choose)
82 | await data_object.create_file_table(table_name="FileData")
83 | key_file = data_key(type="FILE-", len=7)
84 | doc = await random_client.send_document(
85 | chat_id, b, force_document=True, caption=f"{key_file}"
86 | )
87 | await data_object.insert_file_data(
88 | filename=IN_FILE.filename,
89 | fileSize=str(doc.document.file_size),
90 | MessageID=doc.id,
91 | FileKey=key_file,
92 | UserID=X_API_KEY,
93 | Content=IN_FILE.content_type,
94 | Time=doc.date,
95 | )
96 | return JSONResponse(
97 | status_code=200,
98 | content={
99 | "msg": "file uploaded successfully",
100 | "file_key": key_file,
101 | "user": X_API_KEY,
102 | },
103 | )
104 | except Exception as e:
105 | print(e)
106 | return JSONResponse(
107 | status_code=500,
108 | content={"error": "some server error, please try later!"},
109 | )
110 |
111 |
112 | @web.get("/api/signup")
113 | async def data(NAME: str | None = Header(default=None)):
114 | try:
115 | await data_object.create_user_table("UserData")
116 | if NAME == None or NAME == "":
117 | raise HTTPException(
118 | status_code=422,
119 | detail="missing parameter 'NAME',provide a name",
120 | )
121 | return JSONResponse(
122 | {"X-API-KEY": (await data_object.add_user(NAME))},
123 | status_code=200,
124 | )
125 | except Exception as e:
126 | print(e)
127 | return JSONResponse(
128 | status_code=500,
129 | content={"error": "some server error, please try later!"},
130 | )
131 |
132 |
133 | @web.get("/api/logincheck")
134 | async def login(X_API_KEY: str | None = Header(default=None)):
135 | try:
136 | if X_API_KEY == None:
137 | raise HTTPException(
138 | status_code=422,
139 | detail="NO X-API KEY PROVIDED UNABLE TO PROCEED",
140 | )
141 | x = await data_object.login_check(X_API_KEY)
142 | if x == None:
143 | raise HTTPException(
144 | status_code=401,
145 | detail="Unauthorized Login, Please signup",
146 | )
147 | return JSONResponse(
148 | status_code=200,
149 | content={
150 | "login": True,
151 | "user": x,
152 | },
153 | )
154 | except Exception as e:
155 | print(e)
156 | return JSONResponse(
157 | status_code=500,
158 | content={"error": "some server error, please try later!"},
159 | )
160 |
161 |
162 | @web.get("/api/uploads")
163 | async def uploads(X_API_KEY: APIKey = Depends(auth.apikey)):
164 | try:
165 | return JSONResponse(
166 | status_code=200,
167 | content={
168 | "User": X_API_KEY,
169 | "Uploads": (await data_object.get_uploads(X_API_KEY)),
170 | },
171 | )
172 | except Exception as e:
173 | print(e)
174 | return JSONResponse(
175 | status_code=500,
176 | content={"error": "some server error, please try later!"},
177 | )
178 |
179 |
180 | @web.delete("/api/delete")
181 | async def delete(
182 | FILE_KEY: str | None = Header(default=None),
183 | X_API_KEY: APIKey = Depends(auth.apikey),
184 | ):
185 | try:
186 | name = await data_object.deleteFile(FILE_KEY, X_API_KEY)
187 | if name == None:
188 | raise HTTPException(status_code=404, detail="File Not Found")
189 | return JSONResponse(
190 | status_code=200,
191 | content={
192 | "user": X_API_KEY,
193 | "file": name,
194 | "message": "Deleted the file successfully",
195 | },
196 | )
197 | except Exception as e:
198 | print(e)
199 | return JSONResponse(
200 | status_code=500,
201 | content={"error": "some server error, please try later!"},
202 | )
203 |
204 |
205 | @web.get("/api/download")
206 | async def download(
207 | FILE_KEY: str | None = Header(default=None),
208 | X_API_KEY: APIKey = Depends(auth.apikey),
209 | ):
210 | if FILE_KEY == None or FILE_KEY == "":
211 | raise HTTPException(status_code=404, detail="Invalid file Key")
212 | message_id = await data_object.getFile(file_key=FILE_KEY, User_id=X_API_KEY)
213 | if message_id == None:
214 | raise HTTPException(status_code=404, detail="File Not Found")
215 | else:
216 | random_client = random.choice(choose)
217 | file_id, file_name, file_size, file_content = await file_info(
218 | random_client, message_id
219 | )
220 | stream_data = chunk_stream(client=random_client, fileID=file_id)
221 | return StreamingResponse(
222 | stream_data,
223 | status_code=200,
224 | media_type=file_content,
225 | headers={
226 | "content-length": str(file_size),
227 | "X-FILE-NAME": file_name,
228 | },
229 | )
230 |
231 |
232 | @web.post("/api/share")
233 | async def sharable(share: Share, X_API_KEY: APIKey = Depends(auth.apikey)):
234 | try:
235 | new_time = int(time.time()) + share.exp * 3600
236 | await data_object.create_share_table()
237 | encrypted = await encrypt_and_return(share)
238 | random = data_key("", len=8)
239 | await data_object.share_data_add(
240 | short=random,
241 | token=encrypted,
242 | userid=X_API_KEY,
243 | time=new_time,
244 | )
245 | return JSONResponse(
246 | status_code=200,
247 | content={
248 | "link": f"{config.domain_name}/share/?token={random}",
249 | "time": share.exp,
250 | },
251 | )
252 | except Exception as e:
253 | print(e)
254 | return JSONResponse(
255 | status_code=500,
256 | content={"error": "some server error, please try later!"},
257 | )
258 |
259 |
260 | @web.get("/share/")
261 | async def share(token: str):
262 | # try:
263 | current_time = int(time.time())
264 | enc_token, time_token = await data_object.share_data_search(shorten=token)
265 | if current_time > int(time_token):
266 | raise HTTPException(
267 | status_code=503,
268 | detail="The sharing session has expired",
269 | )
270 | final_data = await decrypt_and_return(enc_token)
271 | message_id = await data_object.getFile(
272 | file_key=final_data["filekey"],
273 | User_id=final_data["userkey"],
274 | )
275 | if message_id == None:
276 | raise HTTPException(status_code=404, detail="File Not Found")
277 | else:
278 | random_client = random.choice(choose)
279 | file_id, file_name, file_size, file_content = await file_info(
280 | random_client, message_id
281 | )
282 | stream_data = chunk_stream(client=random_client, fileID=file_id)
283 | return StreamingResponse(
284 | stream_data,
285 | status_code=200,
286 | media_type=file_content,
287 | headers={
288 | "content-length": str(file_size),
289 | "X-FILE-NAME": file_name,
290 | },
291 | )
292 |
293 |
294 | # except Exception as e:
295 | # print(e)
296 | # return JSONResponse(
297 | # status_code=500,
298 | # content={"error": "some server error, please try later!"},
299 | # )
300 |
301 |
302 | uvicorn.run(web, port=config.web_port)
303 |
--------------------------------------------------------------------------------
/Webserver/auth.py:
--------------------------------------------------------------------------------
1 | from fastapi import HTTPException, Security
2 | from fastapi.security.api_key import APIKeyHeader
3 |
4 | from Database.db import database
5 |
6 | da = database()
7 |
8 | X_API_KEY = APIKeyHeader(name="X-API-KEY", auto_error=False)
9 |
10 |
11 | async def apikey(X_API_KEY: str = Security(X_API_KEY)):
12 | if await da.login_check(X_API_KEY):
13 | return X_API_KEY
14 | else:
15 | raise HTTPException(
16 | status_code=403,
17 | detail="Unable to Validate your API KEY, Try signing up",
18 | )
19 |
--------------------------------------------------------------------------------
/example.env:
--------------------------------------------------------------------------------
1 | CHAT_ID=""
2 | API_ID=""
3 | API_HASH=""
4 | BOT_TOKEN1=""
5 | BOT_TOKEN2=""
6 | BOT_TOKEN3=""
7 | BOT_TOKEN4=""
8 | WEB_PORT=""
9 | DB_NAME=""
10 | DB_PORT=""
11 | JWT_SECRET=""
12 | DOMAIN_NAME=""
13 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi
2 | uvicorn
3 | pyrogram
4 | tgcrypto
5 | python-dotenv
6 | psycopg2-binary
7 | python-multipart
8 | pycrypto
9 | nest-asyncio
--------------------------------------------------------------------------------