|
2 | 2 |
|
3 | 3 | import base64 |
4 | 4 | import quart |
| 5 | +import re |
| 6 | +import httpx |
5 | 7 |
|
6 | 8 | from .....core import taskmgr |
7 | 9 | from .. import group |
@@ -48,7 +50,9 @@ async def _(author: str, plugin_name: str) -> str: |
48 | 50 | delete_data = quart.request.args.get('delete_data', 'false').lower() == 'true' |
49 | 51 | ctx = taskmgr.TaskContext.new() |
50 | 52 | wrapper = self.ap.task_mgr.create_user_task( |
51 | | - self.ap.plugin_connector.delete_plugin(author, plugin_name, delete_data=delete_data, task_context=ctx), |
| 53 | + self.ap.plugin_connector.delete_plugin( |
| 54 | + author, plugin_name, delete_data=delete_data, task_context=ctx |
| 55 | + ), |
52 | 56 | kind='plugin-operation', |
53 | 57 | name=f'plugin-remove-{plugin_name}', |
54 | 58 | label=f'Removing plugin {plugin_name}', |
@@ -90,23 +94,145 @@ async def _(author: str, plugin_name: str) -> quart.Response: |
90 | 94 |
|
91 | 95 | return quart.Response(icon_data, mimetype=mime_type) |
92 | 96 |
|
| 97 | + @self.route('/github/releases', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) |
| 98 | + async def _() -> str: |
| 99 | + """Get releases from a GitHub repository URL""" |
| 100 | + data = await quart.request.json |
| 101 | + repo_url = data.get('repo_url', '') |
| 102 | + |
| 103 | + # Parse GitHub repository URL to extract owner and repo |
| 104 | + # Supports: https://github.com/owner/repo or github.com/owner/repo |
| 105 | + pattern = r'github\.com/([^/]+)/([^/]+?)(?:\.git)?(?:/.*)?$' |
| 106 | + match = re.search(pattern, repo_url) |
| 107 | + |
| 108 | + if not match: |
| 109 | + return self.http_status(400, -1, 'Invalid GitHub repository URL') |
| 110 | + |
| 111 | + owner, repo = match.groups() |
| 112 | + |
| 113 | + try: |
| 114 | + # Fetch releases from GitHub API |
| 115 | + url = f'https://api.github.com/repos/{owner}/{repo}/releases' |
| 116 | + async with httpx.AsyncClient( |
| 117 | + trust_env=True, |
| 118 | + follow_redirects=True, |
| 119 | + timeout=10, |
| 120 | + ) as client: |
| 121 | + response = await client.get(url) |
| 122 | + response.raise_for_status() |
| 123 | + releases = response.json() |
| 124 | + |
| 125 | + # Format releases data for frontend |
| 126 | + formatted_releases = [] |
| 127 | + for release in releases: |
| 128 | + formatted_releases.append( |
| 129 | + { |
| 130 | + 'id': release['id'], |
| 131 | + 'tag_name': release['tag_name'], |
| 132 | + 'name': release['name'], |
| 133 | + 'published_at': release['published_at'], |
| 134 | + 'prerelease': release['prerelease'], |
| 135 | + 'draft': release['draft'], |
| 136 | + } |
| 137 | + ) |
| 138 | + |
| 139 | + return self.success(data={'releases': formatted_releases, 'owner': owner, 'repo': repo}) |
| 140 | + except httpx.RequestError as e: |
| 141 | + return self.http_status(500, -1, f'Failed to fetch releases: {str(e)}') |
| 142 | + |
| 143 | + @self.route( |
| 144 | + '/github/release-assets', |
| 145 | + methods=['POST'], |
| 146 | + auth_type=group.AuthType.USER_TOKEN, |
| 147 | + ) |
| 148 | + async def _() -> str: |
| 149 | + """Get assets from a specific GitHub release""" |
| 150 | + data = await quart.request.json |
| 151 | + owner = data.get('owner', '') |
| 152 | + repo = data.get('repo', '') |
| 153 | + release_id = data.get('release_id', '') |
| 154 | + |
| 155 | + if not all([owner, repo, release_id]): |
| 156 | + return self.http_status(400, -1, 'Missing required parameters') |
| 157 | + |
| 158 | + try: |
| 159 | + # Fetch release assets from GitHub API |
| 160 | + url = f'https://api.github.com/repos/{owner}/{repo}/releases/{release_id}' |
| 161 | + async with httpx.AsyncClient( |
| 162 | + trust_env=True, |
| 163 | + follow_redirects=True, |
| 164 | + timeout=10, |
| 165 | + ) as client: |
| 166 | + response = await client.get( |
| 167 | + url, |
| 168 | + ) |
| 169 | + response.raise_for_status() |
| 170 | + release = response.json() |
| 171 | + |
| 172 | + # Format assets data for frontend |
| 173 | + formatted_assets = [] |
| 174 | + for asset in release.get('assets', []): |
| 175 | + formatted_assets.append( |
| 176 | + { |
| 177 | + 'id': asset['id'], |
| 178 | + 'name': asset['name'], |
| 179 | + 'size': asset['size'], |
| 180 | + 'download_url': asset['browser_download_url'], |
| 181 | + 'content_type': asset['content_type'], |
| 182 | + } |
| 183 | + ) |
| 184 | + |
| 185 | + # add zipball as a downloadable asset |
| 186 | + # formatted_assets.append( |
| 187 | + # { |
| 188 | + # "id": 0, |
| 189 | + # "name": "Source code (zip)", |
| 190 | + # "size": -1, |
| 191 | + # "download_url": release["zipball_url"], |
| 192 | + # "content_type": "application/zip", |
| 193 | + # } |
| 194 | + # ) |
| 195 | + |
| 196 | + return self.success(data={'assets': formatted_assets}) |
| 197 | + except httpx.RequestError as e: |
| 198 | + return self.http_status(500, -1, f'Failed to fetch release assets: {str(e)}') |
| 199 | + |
93 | 200 | @self.route('/install/github', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) |
94 | 201 | async def _() -> str: |
| 202 | + """Install plugin from GitHub release asset""" |
95 | 203 | data = await quart.request.json |
| 204 | + asset_url = data.get('asset_url', '') |
| 205 | + owner = data.get('owner', '') |
| 206 | + repo = data.get('repo', '') |
| 207 | + release_tag = data.get('release_tag', '') |
| 208 | + |
| 209 | + if not asset_url: |
| 210 | + return self.http_status(400, -1, 'Missing asset_url parameter') |
96 | 211 |
|
97 | 212 | ctx = taskmgr.TaskContext.new() |
98 | | - short_source_str = data['source'][-8:] |
| 213 | + install_info = { |
| 214 | + 'asset_url': asset_url, |
| 215 | + 'owner': owner, |
| 216 | + 'repo': repo, |
| 217 | + 'release_tag': release_tag, |
| 218 | + 'github_url': f'https://github.com/{owner}/{repo}', |
| 219 | + } |
| 220 | + |
99 | 221 | wrapper = self.ap.task_mgr.create_user_task( |
100 | | - self.ap.plugin_mgr.install_plugin(data['source'], task_context=ctx), |
| 222 | + self.ap.plugin_connector.install_plugin(PluginInstallSource.GITHUB, install_info, task_context=ctx), |
101 | 223 | kind='plugin-operation', |
102 | 224 | name='plugin-install-github', |
103 | | - label=f'Installing plugin from github ...{short_source_str}', |
| 225 | + label=f'Installing plugin from GitHub {owner}/{repo}@{release_tag}', |
104 | 226 | context=ctx, |
105 | 227 | ) |
106 | 228 |
|
107 | 229 | return self.success(data={'task_id': wrapper.id}) |
108 | 230 |
|
109 | | - @self.route('/install/marketplace', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) |
| 231 | + @self.route( |
| 232 | + '/install/marketplace', |
| 233 | + methods=['POST'], |
| 234 | + auth_type=group.AuthType.USER_TOKEN, |
| 235 | + ) |
110 | 236 | async def _() -> str: |
111 | 237 | data = await quart.request.json |
112 | 238 |
|
|
0 commit comments