FossilRepo
Center OAuth buttons, Git mirror sync polish
Commit
e33a0d1a93e3209e17cc78c7127dff0c694559609602b20bac5c5e715d591c6d
Parent
70fa9579745a8a2…
3 files changed
+33
+3
-3
+2
-2
+33
| --- config/urls.py | ||
| +++ config/urls.py | ||
| @@ -2,12 +2,43 @@ | ||
| 2 | 2 | from datetime import UTC, datetime |
| 3 | 3 | |
| 4 | 4 | from django.conf import settings |
| 5 | 5 | from django.contrib import admin |
| 6 | 6 | from django.http import HttpResponse, JsonResponse |
| 7 | +from django.shortcuts import redirect as _redirect | |
| 7 | 8 | from django.urls import include, path |
| 8 | 9 | from django.views.generic import RedirectView |
| 10 | + | |
| 11 | + | |
| 12 | +def _oauth_github_callback(request): | |
| 13 | + """Global GitHub OAuth callback. Extracts slug from state param and delegates.""" | |
| 14 | + state = request.GET.get("state", "") | |
| 15 | + slug = state.split(":")[0] if ":" in state else "" | |
| 16 | + if not slug: | |
| 17 | + return _redirect("/dashboard/") | |
| 18 | + from fossil.oauth import github_exchange_token | |
| 19 | + | |
| 20 | + result = github_exchange_token(request, slug) | |
| 21 | + if result.get("token"): | |
| 22 | + request.session["github_oauth_token"] = result["token"] | |
| 23 | + request.session["github_oauth_user"] = result.get("username", "") | |
| 24 | + return _redirect(f"/projects/{slug}/fossil/sync/git/") | |
| 25 | + | |
| 26 | + | |
| 27 | +def _oauth_gitlab_callback(request): | |
| 28 | + """Global GitLab OAuth callback. Extracts slug from state param and delegates.""" | |
| 29 | + state = request.GET.get("state", "") | |
| 30 | + slug = state.split(":")[0] if ":" in state else "" | |
| 31 | + if not slug: | |
| 32 | + return _redirect("/dashboard/") | |
| 33 | + from fossil.oauth import gitlab_exchange_token | |
| 34 | + | |
| 35 | + result = gitlab_exchange_token(request, slug) | |
| 36 | + if result.get("token"): | |
| 37 | + request.session["gitlab_oauth_token"] = result["token"] | |
| 38 | + return _redirect(f"/projects/{slug}/fossil/sync/git/") | |
| 39 | + | |
| 9 | 40 | |
| 10 | 41 | admin.site.site_header = settings.ADMIN_SITE_HEADER |
| 11 | 42 | admin.site.site_title = settings.ADMIN_SITE_TITLE |
| 12 | 43 | admin.site.index_title = "Welcome to Fossilrepo" |
| 13 | 44 | |
| @@ -193,8 +224,10 @@ | ||
| 193 | 224 | path("settings/", include("organization.urls")), |
| 194 | 225 | path("projects/", include("projects.urls")), |
| 195 | 226 | path("projects/<slug:slug>/fossil/", include("fossil.urls")), |
| 196 | 227 | path("kb/", include("pages.urls")), |
| 197 | 228 | path("items/", include("items.urls")), |
| 229 | + path("oauth/callback/github/", _oauth_github_callback, name="oauth_github_callback_global"), | |
| 230 | + path("oauth/callback/gitlab/", _oauth_gitlab_callback, name="oauth_gitlab_callback_global"), | |
| 198 | 231 | path("admin/", admin.site.urls), |
| 199 | 232 | path("health/", health_check, name="health"), |
| 200 | 233 | ] |
| 201 | 234 |
| --- config/urls.py | |
| +++ config/urls.py | |
| @@ -2,12 +2,43 @@ | |
| 2 | from datetime import UTC, datetime |
| 3 | |
| 4 | from django.conf import settings |
| 5 | from django.contrib import admin |
| 6 | from django.http import HttpResponse, JsonResponse |
| 7 | from django.urls import include, path |
| 8 | from django.views.generic import RedirectView |
| 9 | |
| 10 | admin.site.site_header = settings.ADMIN_SITE_HEADER |
| 11 | admin.site.site_title = settings.ADMIN_SITE_TITLE |
| 12 | admin.site.index_title = "Welcome to Fossilrepo" |
| 13 | |
| @@ -193,8 +224,10 @@ | |
| 193 | path("settings/", include("organization.urls")), |
| 194 | path("projects/", include("projects.urls")), |
| 195 | path("projects/<slug:slug>/fossil/", include("fossil.urls")), |
| 196 | path("kb/", include("pages.urls")), |
| 197 | path("items/", include("items.urls")), |
| 198 | path("admin/", admin.site.urls), |
| 199 | path("health/", health_check, name="health"), |
| 200 | ] |
| 201 |
| --- config/urls.py | |
| +++ config/urls.py | |
| @@ -2,12 +2,43 @@ | |
| 2 | from datetime import UTC, datetime |
| 3 | |
| 4 | from django.conf import settings |
| 5 | from django.contrib import admin |
| 6 | from django.http import HttpResponse, JsonResponse |
| 7 | from django.shortcuts import redirect as _redirect |
| 8 | from django.urls import include, path |
| 9 | from django.views.generic import RedirectView |
| 10 | |
| 11 | |
| 12 | def _oauth_github_callback(request): |
| 13 | """Global GitHub OAuth callback. Extracts slug from state param and delegates.""" |
| 14 | state = request.GET.get("state", "") |
| 15 | slug = state.split(":")[0] if ":" in state else "" |
| 16 | if not slug: |
| 17 | return _redirect("/dashboard/") |
| 18 | from fossil.oauth import github_exchange_token |
| 19 | |
| 20 | result = github_exchange_token(request, slug) |
| 21 | if result.get("token"): |
| 22 | request.session["github_oauth_token"] = result["token"] |
| 23 | request.session["github_oauth_user"] = result.get("username", "") |
| 24 | return _redirect(f"/projects/{slug}/fossil/sync/git/") |
| 25 | |
| 26 | |
| 27 | def _oauth_gitlab_callback(request): |
| 28 | """Global GitLab OAuth callback. Extracts slug from state param and delegates.""" |
| 29 | state = request.GET.get("state", "") |
| 30 | slug = state.split(":")[0] if ":" in state else "" |
| 31 | if not slug: |
| 32 | return _redirect("/dashboard/") |
| 33 | from fossil.oauth import gitlab_exchange_token |
| 34 | |
| 35 | result = gitlab_exchange_token(request, slug) |
| 36 | if result.get("token"): |
| 37 | request.session["gitlab_oauth_token"] = result["token"] |
| 38 | return _redirect(f"/projects/{slug}/fossil/sync/git/") |
| 39 | |
| 40 | |
| 41 | admin.site.site_header = settings.ADMIN_SITE_HEADER |
| 42 | admin.site.site_title = settings.ADMIN_SITE_TITLE |
| 43 | admin.site.index_title = "Welcome to Fossilrepo" |
| 44 | |
| @@ -193,8 +224,10 @@ | |
| 224 | path("settings/", include("organization.urls")), |
| 225 | path("projects/", include("projects.urls")), |
| 226 | path("projects/<slug:slug>/fossil/", include("fossil.urls")), |
| 227 | path("kb/", include("pages.urls")), |
| 228 | path("items/", include("items.urls")), |
| 229 | path("oauth/callback/github/", _oauth_github_callback, name="oauth_github_callback_global"), |
| 230 | path("oauth/callback/gitlab/", _oauth_gitlab_callback, name="oauth_gitlab_callback_global"), |
| 231 | path("admin/", admin.site.urls), |
| 232 | path("health/", health_check, name="health"), |
| 233 | ] |
| 234 |
+3
-3
| --- fossil/oauth.py | ||
| +++ fossil/oauth.py | ||
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | |
| 25 | 25 | client_id = config.GITHUB_OAUTH_CLIENT_ID |
| 26 | 26 | if not client_id: |
| 27 | 27 | return None |
| 28 | 28 | |
| 29 | - callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/github/") | |
| 29 | + callback = request.build_absolute_uri("/oauth/callback/github/") | |
| 30 | 30 | state = f"{slug}:{mirror_id or 'new'}" |
| 31 | 31 | |
| 32 | 32 | return f"{GITHUB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&scope=repo&state={state}" |
| 33 | 33 | |
| 34 | 34 | |
| @@ -71,11 +71,11 @@ | ||
| 71 | 71 | |
| 72 | 72 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 73 | 73 | if not client_id: |
| 74 | 74 | return None |
| 75 | 75 | |
| 76 | - callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/gitlab/") | |
| 76 | + callback = request.build_absolute_uri("/oauth/callback/gitlab/") | |
| 77 | 77 | state = f"{slug}:{mirror_id or 'new'}" |
| 78 | 78 | |
| 79 | 79 | return f"{GITLAB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&response_type=code&scope=api&state={state}" |
| 80 | 80 | |
| 81 | 81 | |
| @@ -87,11 +87,11 @@ | ||
| 87 | 87 | if not code: |
| 88 | 88 | return {"token": "", "error": "No code received"} |
| 89 | 89 | |
| 90 | 90 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 91 | 91 | client_secret = config.GITLAB_OAUTH_CLIENT_SECRET |
| 92 | - callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/gitlab/") | |
| 92 | + callback = request.build_absolute_uri("/oauth/callback/gitlab/") | |
| 93 | 93 | |
| 94 | 94 | try: |
| 95 | 95 | resp = requests.post( |
| 96 | 96 | GITLAB_TOKEN_URL, |
| 97 | 97 | data={ |
| 98 | 98 |
| --- fossil/oauth.py | |
| +++ fossil/oauth.py | |
| @@ -24,11 +24,11 @@ | |
| 24 | |
| 25 | client_id = config.GITHUB_OAUTH_CLIENT_ID |
| 26 | if not client_id: |
| 27 | return None |
| 28 | |
| 29 | callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/github/") |
| 30 | state = f"{slug}:{mirror_id or 'new'}" |
| 31 | |
| 32 | return f"{GITHUB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&scope=repo&state={state}" |
| 33 | |
| 34 | |
| @@ -71,11 +71,11 @@ | |
| 71 | |
| 72 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 73 | if not client_id: |
| 74 | return None |
| 75 | |
| 76 | callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/gitlab/") |
| 77 | state = f"{slug}:{mirror_id or 'new'}" |
| 78 | |
| 79 | return f"{GITLAB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&response_type=code&scope=api&state={state}" |
| 80 | |
| 81 | |
| @@ -87,11 +87,11 @@ | |
| 87 | if not code: |
| 88 | return {"token": "", "error": "No code received"} |
| 89 | |
| 90 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 91 | client_secret = config.GITLAB_OAUTH_CLIENT_SECRET |
| 92 | callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/gitlab/") |
| 93 | |
| 94 | try: |
| 95 | resp = requests.post( |
| 96 | GITLAB_TOKEN_URL, |
| 97 | data={ |
| 98 |
| --- fossil/oauth.py | |
| +++ fossil/oauth.py | |
| @@ -24,11 +24,11 @@ | |
| 24 | |
| 25 | client_id = config.GITHUB_OAUTH_CLIENT_ID |
| 26 | if not client_id: |
| 27 | return None |
| 28 | |
| 29 | callback = request.build_absolute_uri("/oauth/callback/github/") |
| 30 | state = f"{slug}:{mirror_id or 'new'}" |
| 31 | |
| 32 | return f"{GITHUB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&scope=repo&state={state}" |
| 33 | |
| 34 | |
| @@ -71,11 +71,11 @@ | |
| 71 | |
| 72 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 73 | if not client_id: |
| 74 | return None |
| 75 | |
| 76 | callback = request.build_absolute_uri("/oauth/callback/gitlab/") |
| 77 | state = f"{slug}:{mirror_id or 'new'}" |
| 78 | |
| 79 | return f"{GITLAB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&response_type=code&scope=api&state={state}" |
| 80 | |
| 81 | |
| @@ -87,11 +87,11 @@ | |
| 87 | if not code: |
| 88 | return {"token": "", "error": "No code received"} |
| 89 | |
| 90 | client_id = config.GITLAB_OAUTH_CLIENT_ID |
| 91 | client_secret = config.GITLAB_OAUTH_CLIENT_SECRET |
| 92 | callback = request.build_absolute_uri("/oauth/callback/gitlab/") |
| 93 | |
| 94 | try: |
| 95 | resp = requests.post( |
| 96 | GITLAB_TOKEN_URL, |
| 97 | data={ |
| 98 |
+2
-2
| --- templates/fossil/git_mirror.html | ||
| +++ templates/fossil/git_mirror.html | ||
| @@ -58,12 +58,12 @@ | ||
| 58 | 58 | </div> |
| 59 | 59 | {% endif %} |
| 60 | 60 | |
| 61 | 61 | <!-- OAuth connect buttons --> |
| 62 | 62 | <div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6"> |
| 63 | - <h3 class="text-sm font-semibold text-gray-200 mb-3">Quick Connect</h3> | |
| 64 | - <div class="flex items-center gap-3"> | |
| 63 | + <h3 class="text-sm font-semibold text-gray-200 mb-3 text-center">Quick Connect</h3> | |
| 64 | + <div class="flex items-center justify-center gap-3"> | |
| 65 | 65 | <a href="{% url 'fossil:oauth_github' slug=project.slug %}" |
| 66 | 66 | class="inline-flex items-center gap-2 rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-gray-200 ring-1 ring-inset ring-gray-600 hover:bg-gray-700"> |
| 67 | 67 | <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg> |
| 68 | 68 | Connect GitHub |
| 69 | 69 | </a> |
| 70 | 70 |
| --- templates/fossil/git_mirror.html | |
| +++ templates/fossil/git_mirror.html | |
| @@ -58,12 +58,12 @@ | |
| 58 | </div> |
| 59 | {% endif %} |
| 60 | |
| 61 | <!-- OAuth connect buttons --> |
| 62 | <div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6"> |
| 63 | <h3 class="text-sm font-semibold text-gray-200 mb-3">Quick Connect</h3> |
| 64 | <div class="flex items-center gap-3"> |
| 65 | <a href="{% url 'fossil:oauth_github' slug=project.slug %}" |
| 66 | class="inline-flex items-center gap-2 rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-gray-200 ring-1 ring-inset ring-gray-600 hover:bg-gray-700"> |
| 67 | <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg> |
| 68 | Connect GitHub |
| 69 | </a> |
| 70 |
| --- templates/fossil/git_mirror.html | |
| +++ templates/fossil/git_mirror.html | |
| @@ -58,12 +58,12 @@ | |
| 58 | </div> |
| 59 | {% endif %} |
| 60 | |
| 61 | <!-- OAuth connect buttons --> |
| 62 | <div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6"> |
| 63 | <h3 class="text-sm font-semibold text-gray-200 mb-3 text-center">Quick Connect</h3> |
| 64 | <div class="flex items-center justify-center gap-3"> |
| 65 | <a href="{% url 'fossil:oauth_github' slug=project.slug %}" |
| 66 | class="inline-flex items-center gap-2 rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-gray-200 ring-1 ring-inset ring-gray-600 hover:bg-gray-700"> |
| 67 | <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg> |
| 68 | Connect GitHub |
| 69 | </a> |
| 70 |