Update.
This commit is contained in:
		
							parent
							
								
									1616e4edb9
								
							
						
					
					
						commit
						44ac1d2bfa
					
				| @ -33,7 +33,8 @@ dependencies = [ | ||||
|     "PyJWT", | ||||
|     "multiavatar", | ||||
|     "gitpython", | ||||
|     "uvloop" | ||||
|     "uvloop", | ||||
|     "humanize" | ||||
| ] | ||||
| 
 | ||||
| [tool.setuptools.packages.find] | ||||
|  | ||||
| @ -182,8 +182,8 @@ class Application(BaseApplication): | ||||
|         self.router.add_view("/drive/{drive}.json", DriveView) | ||||
|         self.router.add_view("/stats.json", StatsView) | ||||
|         self.router.add_view("/user/{user}.html", UserView) | ||||
|         self.router.add_view("/repository/{username}/{repo_name}", RepositoryView) | ||||
|         self.router.add_view("/repository/{username}/{repo_name}/{rel_path:.*}", RepositoryView) | ||||
|         self.router.add_view("/repository/{username}/{repository}", RepositoryView) | ||||
|         self.router.add_view("/repository/{username}/{repository}/{path:.*}", RepositoryView) | ||||
|         self.router.add_view("/settings/repositories/index.html", RepositoriesIndexView) | ||||
|         self.router.add_view("/settings/repositories/create.html", RepositoriesCreateView) | ||||
|         self.router.add_view("/settings/repositories/repository/{name}/update.html", RepositoriesUpdateView) | ||||
|  | ||||
| @ -1,15 +1,265 @@ | ||||
| from snek.system.view import BaseView | ||||
| import os | ||||
| import mimetypes | ||||
| import urllib.parse | ||||
| from pathlib import Path | ||||
| import humanize | ||||
| from aiohttp import web | ||||
| from snek.system.view import BaseView | ||||
| import asyncio  | ||||
| from git import Repo | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class BareRepoNavigator: | ||||
|     def __init__(self, repo_path): | ||||
|         """Initialize the navigator with a bare repository path.""" | ||||
|         try: | ||||
|             self.repo = Repo(repo_path) | ||||
|             if not self.repo.bare: | ||||
|                 print(f"Error: {repo_path} is not a bare repository.") | ||||
|                 sys.exit(1) | ||||
|         except git.exc.InvalidGitRepositoryError: | ||||
|             print(f"Error: {repo_path} is not a valid Git repository.") | ||||
|             sys.exit(1) | ||||
|         except Exception as e: | ||||
|             print(f"Error opening repository: {str(e)}") | ||||
|             sys.exit(1) | ||||
|              | ||||
|         self.repo_path = repo_path | ||||
|         self.branches = list(self.repo.branches) | ||||
|         self.current_branch = None | ||||
|         self.current_commit = None | ||||
|         self.current_path = "" | ||||
|         self.history = [] | ||||
|          | ||||
|     def get_branches(self): | ||||
|         """Return a list of branch names in the repository.""" | ||||
|         return [branch.name for branch in self.branches] | ||||
|      | ||||
|     def set_branch(self, branch_name): | ||||
|         """Set the current branch.""" | ||||
|         try: | ||||
|             self.current_branch = self.repo.branches[branch_name] | ||||
|             self.current_commit = self.current_branch.commit | ||||
|             self.current_path = "" | ||||
|             self.history = [] | ||||
|             return True | ||||
|         except IndexError: | ||||
|             return False | ||||
|              | ||||
|     def get_commits(self, count=10): | ||||
|         """Get the latest commits on the current branch.""" | ||||
|         if not self.current_branch: | ||||
|             return [] | ||||
|              | ||||
|         commits = [] | ||||
|         for commit in self.repo.iter_commits(self.current_branch, max_count=count): | ||||
|             commits.append({ | ||||
|                 'hash': commit.hexsha, | ||||
|                 'short_hash': commit.hexsha[:7], | ||||
|                 'message': commit.message.strip(), | ||||
|                 'author': commit.author.name, | ||||
|                 'date': datetime.fromtimestamp(commit.committed_date).strftime('%Y-%m-%d %H:%M:%S') | ||||
|             }) | ||||
|         return commits | ||||
|          | ||||
|     def set_commit(self, commit_hash): | ||||
|         """Set the current commit by hash.""" | ||||
|         try: | ||||
|             self.current_commit = self.repo.commit(commit_hash) | ||||
|             self.current_path = "" | ||||
|             self.history = [] | ||||
|             return True | ||||
|         except ValueError: | ||||
|             return False | ||||
|              | ||||
|     def list_directory(self, path=""): | ||||
|         """List the contents of a directory in the current commit.""" | ||||
|         if not self.current_commit: | ||||
|             return {'dirs': [], 'files': []} | ||||
|              | ||||
|         dirs = [] | ||||
|         files = [] | ||||
|          | ||||
|         try: | ||||
|             # Get the tree at the current path | ||||
|             if path: | ||||
|                 tree = self.current_commit.tree[path] | ||||
|                 if not hasattr(tree, 'trees'):  # It's a blob, not a tree | ||||
|                     return {'dirs': [], 'files': [path]} | ||||
|             else: | ||||
|                 tree = self.current_commit.tree | ||||
|                  | ||||
|             # List directories and files | ||||
|             for item in tree: | ||||
|                 if item.type == 'tree': | ||||
|                     item_path = os.path.join(path, item.name) if path else item.name | ||||
|                     dirs.append(item_path) | ||||
|                 elif item.type == 'blob': | ||||
|                     item_path = os.path.join(path, item.name) if path else item.name | ||||
|                     files.append(item_path) | ||||
|                      | ||||
|             dirs.sort() | ||||
|             files.sort() | ||||
|             return {'dirs': dirs, 'files': files} | ||||
|              | ||||
|         except KeyError: | ||||
|             return {'dirs': [], 'files': []} | ||||
|              | ||||
|     def get_file_content(self, file_path): | ||||
|         """Get the content of a file in the current commit.""" | ||||
|         if not self.current_commit: | ||||
|             return None | ||||
|              | ||||
|         try: | ||||
|             blob = self.current_commit.tree[file_path] | ||||
|             return blob.data_stream.read().decode('utf-8', errors='replace') | ||||
|         except (KeyError, UnicodeDecodeError): | ||||
|             try: | ||||
|                 # Try to get as binary if text decoding fails | ||||
|                 blob = self.current_commit.tree[file_path] | ||||
|                 return blob.data_stream.read() | ||||
|             except: | ||||
|                 return None | ||||
|                  | ||||
|     def navigate_to(self, path): | ||||
|         """Navigate to a specific path, updating the current path.""" | ||||
|         if not self.current_commit: | ||||
|             return False | ||||
|              | ||||
|         try: | ||||
|             if path: | ||||
|                 self.current_commit.tree[path]  # Check if path exists | ||||
|             self.history.append(self.current_path) | ||||
|             self.current_path = path | ||||
|             return True | ||||
|         except KeyError: | ||||
|             return False | ||||
|              | ||||
|     def navigate_back(self): | ||||
|         """Navigate back to the previous path.""" | ||||
|         if self.history: | ||||
|             self.current_path = self.history.pop() | ||||
|             return True | ||||
|         return False | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class RepositoryView(BaseView): | ||||
| 	async def get(A): | ||||
| 		G='type';H='name';I='.git';J='username';B=A.request.match_info[J];K=A.request.match_info['repo_name'];C=A.request.match_info.get('rel_path','') | ||||
| 		if not B.count('-')==4:E=await A.services.user.get_by_username(B) | ||||
| 		else:E=await A.services.user.get(B) | ||||
| 		if not E:return web.HTTPNotFound() | ||||
| 		B=E[J];M=await A.services.user.get_repository_path(E['uid']) | ||||
| 		if C.endswith(I):C=C[:-4] | ||||
| 		L=M.joinpath(K+I) | ||||
| 		if not L.exists():return web.HTTPNotFound() | ||||
| 		import os;from git import Repo;N=Repo(L.joinpath(C));F=[];O=[];P=N.head.commit | ||||
| 		for D in P.tree.traverse():F.append({H:D.name,'mode':D.mode,G:D.type,'path':D.path,'size':D.size}) | ||||
| 		sorted(F,key=lambda x:x[H]);sorted(F,key=lambda x:x[G],reverse=True);Q=f"{B}/{C}"[:-4];return await A.render_template('repository.html',dict(username=B,repo_name=K,rel_path=C,full_path=Q,files=F,directories=O)) | ||||
| 
 | ||||
|     login_required = True | ||||
| 
 | ||||
|     def checkout_bare_repo(self, bare_repo_path: Path, target_path: Path, ref: str = 'HEAD'): | ||||
|         repo = Repo(bare_repo_path) | ||||
|         assert repo.bare, "Repository is not bare." | ||||
| 
 | ||||
|         commit = repo.commit(ref) | ||||
|         tree = commit.tree | ||||
| 
 | ||||
|         for blob in tree.traverse(): | ||||
|             target_file = target_path / blob.path | ||||
|                  | ||||
|             target_file.parent.mkdir(parents=True, exist_ok=True) | ||||
|             print(blob.path) | ||||
| 
 | ||||
|             with open(target_file, 'wb') as f: | ||||
|                 f.write(blob.data_stream.read()) | ||||
| 
 | ||||
| 
 | ||||
|     async def get(self): | ||||
| 
 | ||||
|         base_repo_path = Path("drive/repositories")   | ||||
| 
 | ||||
|         authenticated_user_id = self.session.get("uid") | ||||
| 
 | ||||
|         username = self.request.match_info.get('username') | ||||
|         repo_name = self.request.match_info.get('repository') | ||||
|         rel_path = self.request.match_info.get('path', '') | ||||
|         user = None | ||||
|         if not username.count("-") == 4: | ||||
|             user = await self.app.services.user.get(username=username) | ||||
|             if not user: | ||||
|                 return web.Response(text="404 Not Found", status=404) | ||||
|             username = user["username"] | ||||
|         else: | ||||
|             user = await self.app.services.user.get(uid=username) | ||||
| 
 | ||||
|         repo = await self.app.services.repository.get(name=repo_name, user_uid=user["uid"]) | ||||
|         if not repo: | ||||
|             return web.Response(text="404 Not Found", status=404) | ||||
|         if repo['is_private'] and authenticated_user_id != repo['uid']:   | ||||
|             return web.Response(text="404 Not Found", status=404)  | ||||
| 
 | ||||
|         repo_root_base = (base_repo_path / user['uid'] / (repo_name + ".git")).resolve() | ||||
|         repo_root = (base_repo_path / user['uid'] / repo_name).resolve() | ||||
|         try: | ||||
|             loop = asyncio.get_event_loop() | ||||
|             await loop.run_in_executor(None, | ||||
|                          self.checkout_bare_repo, repo_root_base, repo_root | ||||
|             ) | ||||
|         except: | ||||
|             pass | ||||
|          | ||||
|         if not repo_root.exists() or not repo_root.is_dir(): | ||||
|             return web.Response(text="404 Not Found", status=404) | ||||
| 
 | ||||
|         safe_rel_path = os.path.normpath(rel_path).lstrip(os.sep) | ||||
|         abs_path = (repo_root / safe_rel_path).resolve() | ||||
| 
 | ||||
|         if not abs_path.exists() or not abs_path.is_relative_to(repo_root): | ||||
|             return web.Response(text="404 Not Found", status=404) | ||||
| 
 | ||||
|         if abs_path.is_dir(): | ||||
|             return web.Response(text=self.render_directory(abs_path, username, repo_name, safe_rel_path), content_type='text/html') | ||||
|         else: | ||||
|             return web.Response(text=self.render_file(abs_path), content_type='text/html') | ||||
| 
 | ||||
|     def render_directory(self, abs_path, username, repo_name, safe_rel_path): | ||||
|         entries = sorted(abs_path.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())) | ||||
|         items = [] | ||||
| 
 | ||||
|         if safe_rel_path: | ||||
|             parent_path = Path(safe_rel_path).parent | ||||
|             parent_link = f"/repository/{username}/{repo_name}/{parent_path}".rstrip('/') | ||||
|             items.append(f'<li><a href="{parent_link}">β¬
οΈ ..</a></li>') | ||||
| 
 | ||||
|         for entry in entries: | ||||
|             link_path = urllib.parse.quote(str(Path(safe_rel_path) / entry.name)) | ||||
|             link = f"/repository/{username}/{repo_name}/{link_path}".rstrip('/') | ||||
|             display = entry.name + ('/' if entry.is_dir() else '') | ||||
|             size = '' if entry.is_dir() else humanize.naturalsize(entry.stat().st_size) | ||||
|             icon = self.get_icon(entry) | ||||
|             items.append(f'<li>{icon} <a href="{link}">{display}</a> {size}</li>') | ||||
| 
 | ||||
|         html = f""" | ||||
|         <html> | ||||
|         <head><title>π {repo_name}/{safe_rel_path}</title></head> | ||||
|         <body> | ||||
|         <h2>π {username}/{repo_name}/{safe_rel_path}</h2> | ||||
|         <ul> | ||||
|             {''.join(items)} | ||||
|         </ul> | ||||
|         </body> | ||||
|         </html> | ||||
|         """ | ||||
|         return html | ||||
| 
 | ||||
|     def render_file(self, abs_path): | ||||
|         try: | ||||
|             with open(abs_path, 'r', encoding='utf-8', errors='ignore') as f: | ||||
|                 content = f.read() | ||||
|             return f"<pre>{content}</pre>" | ||||
|         except Exception as e: | ||||
|             return f"<h1>Error</h1><pre>{e}</pre>" | ||||
| 
 | ||||
|     def get_icon(self, file): | ||||
|         if file.is_dir(): return "π" | ||||
|         mime = mimetypes.guess_type(file.name)[0] or '' | ||||
|         if mime.startswith("image"): return "πΌοΈ" | ||||
|         if mime.startswith("text"): return "π" | ||||
|         if mime.startswith("audio"): return "π΅" | ||||
|         if mime.startswith("video"): return "π¬" | ||||
|         if file.name.endswith(".py"): return "π" | ||||
|         return "π¦" | ||||
| 
 | ||||
|  | ||||
		Loadingβ¦
	
		Reference in New Issue
	
	Block a user