├── .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 --------------------------------------------------------------------------------