Skip to content

Commit 52e724e

Browse files
authored
Merge pull request #93 from zecrypt-io/preview
Preview
2 parents 7209bf4 + 2dc28b9 commit 52e724e

46 files changed

Lines changed: 12771 additions & 1011 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/backend-server/app/api/v1/web/api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from app.api.v1.web.user import api as user_router
77
from app.api.v1.web.secrets import api as secrets_router
88
from app.api.v1.web.dashboard import api as dashboard_router
9-
9+
from app.api.v1.web.drive import api as drive_router
1010
api_router = APIRouter()
1111

1212
api_router.prefix = "/web"
@@ -18,3 +18,5 @@
1818
api_router.include_router(user_router.router, tags=["User"])
1919
api_router.include_router(workspace_router.router, tags=["Workspace"])
2020
api_router.include_router(dashboard_router.router, tags=["Dashboard"])
21+
api_router.include_router(drive_router.router)
22+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from fastapi import APIRouter
2+
from app.api.v1.web.drive.folders import api as folders_router
3+
from app.api.v1.web.drive.files import api as files_router
4+
5+
6+
router = APIRouter()
7+
8+
router.prefix = "/drive"
9+
10+
router.include_router(folders_router.router, tags=["Drive: Folders"])
11+
router.include_router(files_router.router, tags=["Drive: Files"])

packages/backend-server/app/api/v1/web/drive/files/__init__.py

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from fastapi import APIRouter,Request,Depends,UploadFile
2+
from app.api.v1.web.drive.files.schema import RenameFile, MoveFile, DeleteFiles, GetPresignedUrl
3+
from app.api.v1.web.auth.schema import UserDetails
4+
from app.framework.permission_services.service import get_current_user
5+
from app.api.v1.web.drive.files.services import rename_file, get_presigned_url, delete_files, move_files
6+
7+
router = APIRouter()
8+
9+
FILE_URL ="/file"
10+
11+
@router.post(FILE_URL+"/get-presigned-url")
12+
async def get_presigned_url_api(payload: GetPresignedUrl, user: UserDetails = Depends(get_current_user)):
13+
return await get_presigned_url(user, payload.model_dump())
14+
15+
@router.post(FILE_URL+"/rename")
16+
async def rename_file_api(payload: RenameFile, user: UserDetails = Depends(get_current_user)):
17+
return await rename_file(user, payload.model_dump())
18+
19+
20+
@router.delete(FILE_URL+"/delete")
21+
async def delete_files_api(payload: DeleteFiles, user: UserDetails = Depends(get_current_user)):
22+
return await delete_files(user, payload.model_dump())
23+
24+
25+
@router.post(FILE_URL+"/move")
26+
async def move_files_api(payload: MoveFile, user: UserDetails = Depends(get_current_user)):
27+
return await move_files(user, payload.model_dump())
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from pydantic import BaseModel, Field
2+
from typing import Optional, List
3+
from datetime import datetime
4+
5+
6+
class RenameFile(BaseModel):
7+
name: str = Field(..., description="File name")
8+
file_id: str = Field(..., description="File ID")
9+
parent_id: Optional[str] = Field(None, description="Parent folder ID, None for root")
10+
11+
class MoveFile(BaseModel):
12+
file_ids: List[str] = Field(..., description="File IDs")
13+
parent_id: str = Field(..., description="Parent folder ID")
14+
15+
class DeleteFiles(BaseModel):
16+
file_ids: List[str] = Field(..., description="File IDs")
17+
18+
class GetPresignedUrl(BaseModel):
19+
file_type: str = Field(..., description="File type")
20+
name: str = Field(..., description="File name")
21+
size: int = Field(..., description="File size")
22+
file_path: str = Field(..., description="File path")
23+
parent_id: Optional[str] = Field(None, description="Parent folder ID")
24+
iv: str = Field(..., description="IV")
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from app.framework.mongo_db import base_manager as db_manager
2+
from app.managers.collection_names import FILES, FOLDERS
3+
from app.utils.utils import response_helper, create_uuid, get_file_extension, get_folders_from_path, create_timestamp
4+
from app.utils.i8ns import translate
5+
from app.utils.s3_utils import generate_upload_url, generate_download_url
6+
7+
8+
9+
def get_files_list(user, parent_id=None):
10+
db = user.get("db")
11+
query = {"created_by": user.get("user_id")}
12+
if parent_id:
13+
query["parent_id"] = parent_id
14+
files = db_manager.find(db, FILES, query)
15+
for file in files:
16+
file["file_url"]= generate_download_url(file.get("key"), 360000)
17+
return files
18+
19+
async def get_presigned_url(user, payload):
20+
db = user.get("db")
21+
user_id = user.get("user_id")
22+
file_name = payload.get("name")
23+
24+
# Check if file already exists for this user
25+
query = {
26+
"lower_name": file_name.strip().lower(),
27+
"created_by": user_id
28+
}
29+
if payload.get("parent_id"):
30+
query["parent_id"] = payload.get("parent_id")
31+
32+
file = db_manager.find_one(db, FILES, query)
33+
if file:
34+
return response_helper(400, translate("drive.files.already_exists"))
35+
36+
# Generate file paths
37+
file_id = create_uuid()
38+
file_extension = get_file_extension(file_name)
39+
final_file_path = f"{user_id}/files/{file_id}.{file_extension}"
40+
41+
# Get folders from path
42+
folders = get_folders_from_path(payload.get("file_path"))
43+
parent_id = payload.get("parent_id")
44+
45+
# Create folder hierarchy if folders exist
46+
for folder_name in folders:
47+
query = {
48+
"created_by": user_id,
49+
"lower_name": folder_name.strip().lower()
50+
}
51+
if parent_id:
52+
query["parent_id"] = parent_id
53+
54+
folder_details = db_manager.find_one(db, FOLDERS, query)
55+
if not folder_details:
56+
# Create new folder
57+
folder_data = {
58+
"doc_id": create_uuid(),
59+
"name": folder_name.strip(),
60+
"parent_id": parent_id,
61+
"created_by": user_id,
62+
"lower_name": folder_name.strip().lower(),
63+
}
64+
db_manager.insert_one(db, FOLDERS, folder_data)
65+
parent_id = folder_data.get("doc_id")
66+
else:
67+
# Use existing folder
68+
parent_id = folder_details.get("doc_id")
69+
70+
# Insert file record (always, regardless of whether folders exist)
71+
file_data = {
72+
"doc_id": file_id,
73+
"type": payload.get("file_type"),
74+
"name": file_name,
75+
"lower_name": file_name.strip().lower(),
76+
"size": payload.get("size"),
77+
"path": payload.get("file_path"),
78+
"key": final_file_path,
79+
"parent_id": parent_id,
80+
"created_by": user_id,
81+
"iv": payload.get("iv"),
82+
}
83+
db_manager.insert_one(db, FILES, file_data)
84+
85+
# Generate presigned upload URL
86+
upload_url = generate_upload_url(final_file_path)
87+
88+
return response_helper(200, translate("file.get_presigned_url"), data=upload_url)
89+
90+
91+
async def rename_file(user, payload):
92+
db = user.get("db")
93+
name = payload.get("name").strip()
94+
file_id = payload.get("file_id")
95+
lower_name = name.lower()
96+
query = {"created_by": user.get("user_id"), "lower_name": lower_name, "doc_id": {"$ne": file_id}}
97+
98+
if payload.get("parent_id"):
99+
query["parent_id"] = payload.get("parent_id")
100+
101+
file = db_manager.find_one(db, FILES, query)
102+
if file:
103+
return response_helper(400, translate("drive.files.already_exists"))
104+
105+
db_manager.update_one(db, FILES, {"doc_id": file_id, "created_by": user.get("user_id")}, {"$set": {"name": name, "lower_name": lower_name}})
106+
return response_helper(200, translate("drive.files.renamed"))
107+
108+
109+
async def delete_files(user, payload):
110+
db = user.get("db")
111+
file_ids = payload.get("file_ids")
112+
user_id = user.get("user_id")
113+
db_manager.update_many(db, FILES, {"doc_id": {"$in": file_ids}, "created_by": user_id}, {"$set": {"access": False, "delete_by": user_id, "deleted_at": create_timestamp()}})
114+
return response_helper(200, translate("drive.files.deleted"))
115+
116+
117+
async def move_files(user, payload):
118+
db = user.get("db")
119+
file_ids = payload.get("file_ids")
120+
parent_id = payload.get("parent_id")
121+
query = {"created_by": user.get("user_id"), "doc_id": {"$in": file_ids}}
122+
files = db_manager.find(db, FILES, query)
123+
files_moved = []
124+
for item in files:
125+
individual_query = {"created_by": user.get("user_id"), "parent_id": parent_id, "lower_name": item.get("lower_name")}
126+
if not db_manager.find_one(db, FILES, individual_query):
127+
db_manager.update_one(db, FILES, {"doc_id": item.get("doc_id")}, {"$set": {"parent_id": parent_id}})
128+
files_moved.append(item.get("name"))
129+
130+
return response_helper(200, translate("drive.files.moved"), files_moved=files_moved)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from fastapi import APIRouter,Depends,Query
2+
from app.api.v1.web.drive.folders.schema import CreateFolder, RenameFolder, MoveFolder, DeleteFolders
3+
from app.api.v1.web.auth.schema import UserDetails
4+
from app.framework.permission_services.service import get_current_user
5+
from app.api.v1.web.drive.folders.services import create_folder,delete_folders,rename_folder, move_folders, get_folders_list
6+
7+
FOLDER_URL ="/folder"
8+
router = APIRouter()
9+
10+
@router.post(FOLDER_URL+"/create")
11+
async def create_folder_api(payload: CreateFolder, user: UserDetails = Depends(get_current_user)):
12+
return await create_folder(user, payload.model_dump())
13+
14+
15+
@router.delete(FOLDER_URL+"/delete")
16+
async def delete_folder_api(payload: DeleteFolders, user: UserDetails = Depends(get_current_user)):
17+
return await delete_folders(user, payload.model_dump())
18+
19+
20+
@router.post(FOLDER_URL+"/rename")
21+
async def rename_folder_api(payload: RenameFolder, user: UserDetails = Depends(get_current_user)):
22+
return await rename_folder(user, payload.model_dump())
23+
24+
25+
@router.post(FOLDER_URL+"/move")
26+
async def move_folder_api(payload: MoveFolder, user: UserDetails = Depends(get_current_user)):
27+
return await move_folders(user, payload.model_dump())
28+
29+
30+
@router.get(FOLDER_URL+"/list")
31+
async def get_all_folders_api(parent_id: str = Query(None, description="Parent folder ID", ge=1), user: UserDetails = Depends(get_current_user)):
32+
return await get_folders_list(user, parent_id)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from pydantic import BaseModel, Field
2+
from typing import Optional, List
3+
from datetime import datetime
4+
5+
class CreateFolder(BaseModel):
6+
name: str
7+
parent_id: Optional[str]= None
8+
9+
class RenameFolder(BaseModel):
10+
name: str
11+
folder_id: str
12+
parent_id: Optional[str] = None
13+
14+
class MoveFolder(BaseModel):
15+
folder_ids: List[str]
16+
parent_id: str
17+
18+
class DeleteFolders(BaseModel):
19+
folder_ids: List[str]
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
2+
from app.framework.mongo_db import base_manager as db_manager
3+
from app.managers.collection_names import FILES, FOLDERS
4+
from app.utils.utils import response_helper, create_uuid, create_timestamp
5+
from app.utils.i8ns import translate
6+
from app.api.v1.web.drive.files.services import get_files_list
7+
8+
async def create_folder(user, payload):
9+
db = user.get("db")
10+
folder_id = create_uuid()
11+
query={
12+
"created_by": user.get("user_id"),
13+
"lower_name": payload.get("name").strip().lower(),
14+
}
15+
if payload.get("parent_id"):
16+
query["parent_id"] = payload.get("parent_id")
17+
folder = db_manager.find_one(db, FOLDERS, query)
18+
if folder:
19+
return response_helper(400, translate("drive.folders.already_exists"))
20+
21+
folder = {
22+
"doc_id": folder_id,
23+
"name": payload.get("name"),
24+
"parent_id": payload.get("parent_id"),
25+
"created_by": user.get("user_id"),
26+
"lower_name": payload.get("name").strip().lower(),
27+
"created_at": create_timestamp(),
28+
29+
}
30+
db_manager.insert_one(db, FOLDERS, folder)
31+
return response_helper(200, translate("drive.folders.created"))
32+
33+
34+
async def delete_folders(user, payload):
35+
db = user.get("db")
36+
folder_ids = payload.get("folder_ids")
37+
user_id = user.get("user_id")
38+
db_manager.update_many(db, FOLDERS, {"doc_id": {"$in":folder_ids}, "created_by": user_id}, {"$set": {"access": False,"delete_by":user_id, "deleted_at": create_timestamp()}})
39+
return response_helper(200, translate("drive.folders.deleted"))
40+
41+
42+
async def rename_folder(user, payload):
43+
db = user.get("db")
44+
name = payload.get("name").strip()
45+
folder_id = payload.get("folder_id")
46+
lower_name= name.lower()
47+
query={"created_by": user.get("user_id"), "lower_name": lower_name}
48+
49+
if payload.get("parent_id"):
50+
query["parent_id"] = payload.get("parent_id")
51+
folder = db_manager.find_one(db, FOLDERS, query)
52+
53+
if folder:
54+
return response_helper(400, translate("drive.folders.already_exists"))
55+
56+
db_manager.update_one(db, FOLDERS, {"doc_id": folder_id, "created_by": user.get("user_id")}, {"$set": {"name": name, "lower_name": lower_name}})
57+
return response_helper(200, translate("drive.folders.renamed"))
58+
59+
60+
async def move_folders(user, payload):
61+
db = user.get("db")
62+
folder_ids = payload.get("folder_ids")
63+
parent_id = payload.get("parent_id")
64+
query={"created_by": user.get("user_id"), "doc_id": {"$in":folder_ids}}
65+
folders = db_manager.find(db, FOLDERS, query)
66+
folders_moved=[]
67+
for item in folders:
68+
individual_query={"created_by": user.get("user_id"), "parent_id": parent_id, "lower_name": item.get("lower_name")}
69+
if not db_manager.find_one(db, FOLDERS, individual_query):
70+
db_manager.update_one(db, FOLDERS, {"doc_id": item.get("doc_id")}, {"$set": {"parent_id": parent_id}})
71+
folders_moved.append(item.get("name"))
72+
73+
74+
db_manager.update_many(db, FOLDERS, {"doc_id": {"$in":folder_ids}, "created_by": user.get("user_id")}, {"$set": {"parent_id": parent_id}})
75+
return response_helper(200, translate("drive.folders.moved"), folders_moved=folders_moved)
76+
77+
async def get_folders_list(user, parent_id):
78+
query = {"created_by": user.get("user_id")}
79+
if parent_id:
80+
query["parent_id"] = parent_id
81+
82+
folders = get_folders(user, parent_id)
83+
84+
for folder in folders:
85+
folder["files"] = get_files_list(user, folder.get("doc_id"))
86+
folder["sub_folders"] = get_folders(user, folder.get("doc_id"))
87+
88+
data= {
89+
"folders": folders,
90+
"files": get_files_list(user, parent_id)
91+
}
92+
return response_helper(200, translate("drive.folders.list"), data=data)
93+
94+
95+
96+
def get_folders(user, parent_id=None):
97+
db = user.get("db")
98+
query = {"created_by": user.get("user_id")}
99+
if parent_id:
100+
query["parent_id"] = parent_id
101+
folders = db_manager.find(db, FOLDERS, query)
102+
return folders

packages/backend-server/app/core/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ class Settings(BaseSettings):
1515

1616
VALKEY_URL: str
1717

18+
DO_SPACES_KEY:str
19+
DO_SPACES_SECRET:str
20+
DO_SPACES_REGION:str
21+
DO_SPACES_BUCKET:str
22+
DO_SPACES_ENDPOINT:str
1823
class Config:
1924
case_sensitive = True
2025
env_file = ".env"

0 commit comments

Comments
 (0)