FossilRepo

Center OAuth buttons, Git mirror sync polish

ragelink 2026-04-07 03:57 trunk
Commit e33a0d1a93e3209e17cc78c7127dff0c694559609602b20bac5c5e715d591c6d
--- config/urls.py
+++ config/urls.py
@@ -2,12 +2,43 @@
22
from datetime import UTC, datetime
33
44
from django.conf import settings
55
from django.contrib import admin
66
from django.http import HttpResponse, JsonResponse
7
+from django.shortcuts import redirect as _redirect
78
from django.urls import include, path
89
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
+
940
1041
admin.site.site_header = settings.ADMIN_SITE_HEADER
1142
admin.site.site_title = settings.ADMIN_SITE_TITLE
1243
admin.site.index_title = "Welcome to Fossilrepo"
1344
@@ -193,8 +224,10 @@
193224
path("settings/", include("organization.urls")),
194225
path("projects/", include("projects.urls")),
195226
path("projects/<slug:slug>/fossil/", include("fossil.urls")),
196227
path("kb/", include("pages.urls")),
197228
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"),
198231
path("admin/", admin.site.urls),
199232
path("health/", health_check, name="health"),
200233
]
201234
--- 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 @@
2424
2525
client_id = config.GITHUB_OAUTH_CLIENT_ID
2626
if not client_id:
2727
return None
2828
29
- callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/github/")
29
+ callback = request.build_absolute_uri("/oauth/callback/github/")
3030
state = f"{slug}:{mirror_id or 'new'}"
3131
3232
return f"{GITHUB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&scope=repo&state={state}"
3333
3434
@@ -71,11 +71,11 @@
7171
7272
client_id = config.GITLAB_OAUTH_CLIENT_ID
7373
if not client_id:
7474
return None
7575
76
- callback = request.build_absolute_uri(f"/projects/{slug}/fossil/sync/git/callback/gitlab/")
76
+ callback = request.build_absolute_uri("/oauth/callback/gitlab/")
7777
state = f"{slug}:{mirror_id or 'new'}"
7878
7979
return f"{GITLAB_AUTHORIZE_URL}?client_id={client_id}&redirect_uri={callback}&response_type=code&scope=api&state={state}"
8080
8181
@@ -87,11 +87,11 @@
8787
if not code:
8888
return {"token": "", "error": "No code received"}
8989
9090
client_id = config.GITLAB_OAUTH_CLIENT_ID
9191
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/")
9393
9494
try:
9595
resp = requests.post(
9696
GITLAB_TOKEN_URL,
9797
data={
9898
--- 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
--- templates/fossil/git_mirror.html
+++ templates/fossil/git_mirror.html
@@ -58,12 +58,12 @@
5858
</div>
5959
{% endif %}
6060
6161
<!-- OAuth connect buttons -->
6262
<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">
6565
<a href="{% url 'fossil:oauth_github' slug=project.slug %}"
6666
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">
6767
<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>
6868
Connect GitHub
6969
</a>
7070
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button