tangled
alpha
login
or
join now
baileytownsend.dev
/
pds-gatekeeper
89
fork
atom
Microservice to bring 2FA to self hosted PDSes
89
fork
atom
overview
issues
1
pulls
3
pipelines
wip of template partials
baileytownsend.dev
1 week ago
31213121
1644d657
+291
-1593
12 changed files
expand all
collapse all
unified
split
html_templates
admin
account_detail.hbs
accounts.hbs
create_account.hbs
dashboard.hbs
error.hbs
invite_codes.hbs
login.hbs
partials
base_css.hbs
flash.hbs
sidebar.hbs
request_crawl.hbs
src
main.rs
+3
-226
html_templates/admin/account_detail.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>{{account.handle}} - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
154
10
155
11
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
12
margin-bottom: 4px;
159
13
}
160
14
···
164
18
font-family: 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
165
19
margin-bottom: 24px;
166
20
word-break: break-all;
167
167
-
}
168
168
-
169
169
-
.flash-success {
170
170
-
background: rgba(22, 163, 74, 0.1);
171
171
-
color: var(--success-color);
172
172
-
border: 1px solid rgba(22, 163, 74, 0.2);
173
173
-
border-radius: 8px;
174
174
-
padding: 10px 14px;
175
175
-
font-size: 0.875rem;
176
176
-
margin-bottom: 20px;
177
177
-
}
178
178
-
179
179
-
.flash-error {
180
180
-
background: rgba(220, 38, 38, 0.1);
181
181
-
color: var(--danger-color);
182
182
-
border: 1px solid rgba(220, 38, 38, 0.2);
183
183
-
border-radius: 8px;
184
184
-
padding: 10px 14px;
185
185
-
font-size: 0.875rem;
186
186
-
margin-bottom: 20px;
187
21
}
188
22
189
23
.detail-section {
···
260
94
}
261
95
262
96
.btn {
263
263
-
display: inline-flex;
264
264
-
align-items: center;
265
265
-
justify-content: center;
266
97
padding: 8px 16px;
267
98
font-size: 0.8125rem;
268
268
-
font-weight: 500;
269
99
border: 1px solid var(--border-color);
270
270
-
border-radius: 8px;
271
271
-
cursor: pointer;
272
272
-
transition: opacity 0.15s;
273
273
-
text-decoration: none;
274
100
background: var(--bg-primary-color);
275
101
color: var(--primary-color);
276
102
}
277
103
278
278
-
.btn:hover {
279
279
-
opacity: 0.85;
280
280
-
}
281
281
-
282
104
.btn-primary {
283
283
-
background: var(--brand-color);
284
284
-
color: #fff;
285
105
border-color: var(--brand-color);
286
106
}
287
107
288
108
.btn-danger {
289
289
-
background: var(--danger-color);
290
290
-
color: #fff;
291
109
border-color: var(--danger-color);
292
110
}
293
111
294
112
.btn-warning {
295
295
-
background: var(--warning-color);
296
296
-
color: #000;
297
113
border-color: var(--warning-color);
298
114
}
299
115
···
355
171
padding: 4px 0;
356
172
color: var(--secondary-color);
357
173
}
358
358
-
359
359
-
@media (max-width: 768px) {
360
360
-
.sidebar {
361
361
-
display: none;
362
362
-
}
363
363
-
364
364
-
.main {
365
365
-
margin-left: 0;
366
366
-
}
367
367
-
}
368
174
</style>
369
175
</head>
370
176
<body>
371
177
<div class="layout">
372
372
-
<aside class="sidebar">
373
373
-
<div class="sidebar-title">{{pds_hostname}}</div>
374
374
-
<div class="sidebar-subtitle">Admin Portal</div>
375
375
-
<nav>
376
376
-
<a href="/admin/dashboard">Dashboard</a>
377
377
-
{{#if can_view_accounts}}
378
378
-
<a href="/admin/accounts" class="active">Accounts</a>
379
379
-
{{/if}}
380
380
-
{{#if can_manage_invites}}
381
381
-
<a href="/admin/invite-codes">Invite Codes</a>
382
382
-
{{/if}}
383
383
-
{{#if can_create_account}}
384
384
-
<a href="/admin/create-account">Create Account</a>
385
385
-
{{/if}}
386
386
-
{{#if can_request_crawl}}
387
387
-
<a href="/admin/request-crawl">Request Crawl</a>
388
388
-
{{/if}}
389
389
-
</nav>
390
390
-
<div class="sidebar-footer">
391
391
-
<div class="session-info">Signed in as {{handle}}</div>
392
392
-
<form method="POST" action="/admin/logout">
393
393
-
<button type="submit">Sign out</button>
394
394
-
</form>
395
395
-
</div>
396
396
-
</aside>
178
178
+
{{> admin/partials/sidebar.hbs}}
397
179
398
180
<main class="main">
399
399
-
{{#if flash_success}}
400
400
-
<div class="flash-success">{{flash_success}}</div>
401
401
-
{{/if}}
402
402
-
{{#if flash_error}}
403
403
-
<div class="flash-error">{{flash_error}}</div>
404
404
-
{{/if}}
181
181
+
{{> admin/partials/flash.hbs}}
405
182
406
183
<a href="/admin/accounts" class="back-link">← Back to Accounts</a>
407
184
+3
-235
html_templates/admin/accounts.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Accounts - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
154
154
-
155
155
-
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
158
-
margin-bottom: 24px;
159
159
-
}
160
160
-
161
161
-
.flash-success {
162
162
-
background: rgba(22, 163, 74, 0.1);
163
163
-
color: var(--success-color);
164
164
-
border: 1px solid rgba(22, 163, 74, 0.2);
165
165
-
border-radius: 8px;
166
166
-
padding: 10px 14px;
167
167
-
font-size: 0.875rem;
168
168
-
margin-bottom: 20px;
169
169
-
}
170
170
-
171
171
-
.flash-error {
172
172
-
background: rgba(220, 38, 38, 0.1);
173
173
-
color: var(--danger-color);
174
174
-
border: 1px solid rgba(220, 38, 38, 0.2);
175
175
-
border-radius: 8px;
176
176
-
padding: 10px 14px;
177
177
-
font-size: 0.875rem;
178
178
-
margin-bottom: 20px;
179
179
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
180
10
181
11
.search-form {
182
12
display: flex;
···
199
29
border-color: var(--brand-color);
200
30
}
201
31
202
202
-
.btn {
203
203
-
display: inline-flex;
204
204
-
align-items: center;
205
205
-
justify-content: center;
206
206
-
padding: 10px 20px;
207
207
-
font-size: 0.875rem;
208
208
-
font-weight: 500;
209
209
-
border: none;
210
210
-
border-radius: 8px;
211
211
-
cursor: pointer;
212
212
-
transition: opacity 0.15s;
213
213
-
text-decoration: none;
214
214
-
}
215
215
-
216
216
-
.btn:hover {
217
217
-
opacity: 0.85;
218
218
-
}
219
219
-
220
220
-
.btn-primary {
221
221
-
background: var(--brand-color);
222
222
-
color: #fff;
223
223
-
}
224
224
-
225
32
.table-container {
226
33
background: var(--bg-primary-color);
227
34
border: 1px solid var(--border-color);
···
283
90
font-size: 0.75rem;
284
91
color: var(--secondary-color);
285
92
}
286
286
-
287
287
-
@media (max-width: 768px) {
288
288
-
.sidebar {
289
289
-
display: none;
290
290
-
}
291
291
-
292
292
-
.main {
293
293
-
margin-left: 0;
294
294
-
}
295
295
-
}
296
93
</style>
297
94
</head>
298
95
<body>
299
96
<div class="layout">
300
300
-
<aside class="sidebar">
301
301
-
<div class="sidebar-title">{{pds_hostname}}</div>
302
302
-
<div class="sidebar-subtitle">Admin Portal</div>
303
303
-
<nav>
304
304
-
<a href="/admin/dashboard">Dashboard</a>
305
305
-
{{#if can_view_accounts}}
306
306
-
<a href="/admin/accounts" class="active">Accounts</a>
307
307
-
{{/if}}
308
308
-
{{#if can_manage_invites}}
309
309
-
<a href="/admin/invite-codes">Invite Codes</a>
310
310
-
{{/if}}
311
311
-
{{#if can_create_account}}
312
312
-
<a href="/admin/create-account">Create Account</a>
313
313
-
{{/if}}
314
314
-
{{#if can_request_crawl}}
315
315
-
<a href="/admin/request-crawl">Request Crawl</a>
316
316
-
{{/if}}
317
317
-
</nav>
318
318
-
<div class="sidebar-footer">
319
319
-
<div class="session-info">Signed in as {{handle}}</div>
320
320
-
<form method="POST" action="/admin/logout">
321
321
-
<button type="submit">Sign out</button>
322
322
-
</form>
323
323
-
</div>
324
324
-
</aside>
97
97
+
{{> admin/partials/sidebar.hbs}}
325
98
326
99
<main class="main">
327
327
-
{{#if flash_success}}
328
328
-
<div class="flash-success">{{flash_success}}</div>
329
329
-
{{/if}}
330
330
-
{{#if flash_error}}
331
331
-
<div class="flash-error">{{flash_error}}</div>
332
332
-
{{/if}}
100
100
+
{{> admin/partials/flash.hbs}}
333
101
334
102
<h1 class="page-title">Accounts</h1>
335
103
+3
-235
html_templates/admin/create_account.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Create Account - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
154
154
-
155
155
-
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
158
-
margin-bottom: 24px;
159
159
-
}
160
160
-
161
161
-
.flash-success {
162
162
-
background: rgba(22, 163, 74, 0.1);
163
163
-
color: var(--success-color);
164
164
-
border: 1px solid rgba(22, 163, 74, 0.2);
165
165
-
border-radius: 8px;
166
166
-
padding: 10px 14px;
167
167
-
font-size: 0.875rem;
168
168
-
margin-bottom: 20px;
169
169
-
}
170
170
-
171
171
-
.flash-error {
172
172
-
background: rgba(220, 38, 38, 0.1);
173
173
-
color: var(--danger-color);
174
174
-
border: 1px solid rgba(220, 38, 38, 0.2);
175
175
-
border-radius: 8px;
176
176
-
padding: 10px 14px;
177
177
-
font-size: 0.875rem;
178
178
-
margin-bottom: 20px;
179
179
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
180
10
181
11
.form-card {
182
12
background: var(--bg-primary-color);
···
220
50
margin-top: 4px;
221
51
}
222
52
223
223
-
.btn {
224
224
-
display: inline-flex;
225
225
-
align-items: center;
226
226
-
justify-content: center;
227
227
-
padding: 10px 20px;
228
228
-
font-size: 0.875rem;
229
229
-
font-weight: 500;
230
230
-
border: none;
231
231
-
border-radius: 8px;
232
232
-
cursor: pointer;
233
233
-
transition: opacity 0.15s;
234
234
-
text-decoration: none;
235
235
-
}
236
236
-
237
237
-
.btn:hover {
238
238
-
opacity: 0.85;
239
239
-
}
240
240
-
241
241
-
.btn-primary {
242
242
-
background: var(--brand-color);
243
243
-
color: #fff;
244
244
-
}
245
245
-
246
53
.success-card {
247
54
background: var(--bg-primary-color);
248
55
border: 1px solid var(--border-color);
···
309
116
color: var(--primary-color);
310
117
border-color: var(--primary-color);
311
118
}
312
312
-
313
313
-
@media (max-width: 768px) {
314
314
-
.sidebar {
315
315
-
display: none;
316
316
-
}
317
317
-
318
318
-
.main {
319
319
-
margin-left: 0;
320
320
-
}
321
321
-
}
322
119
</style>
323
120
</head>
324
121
<body>
325
122
<div class="layout">
326
326
-
<aside class="sidebar">
327
327
-
<div class="sidebar-title">{{pds_hostname}}</div>
328
328
-
<div class="sidebar-subtitle">Admin Portal</div>
329
329
-
<nav>
330
330
-
<a href="/admin/dashboard">Dashboard</a>
331
331
-
{{#if can_view_accounts}}
332
332
-
<a href="/admin/accounts">Accounts</a>
333
333
-
{{/if}}
334
334
-
{{#if can_manage_invites}}
335
335
-
<a href="/admin/invite-codes">Invite Codes</a>
336
336
-
{{/if}}
337
337
-
{{#if can_create_account}}
338
338
-
<a href="/admin/create-account" class="active">Create Account</a>
339
339
-
{{/if}}
340
340
-
{{#if can_request_crawl}}
341
341
-
<a href="/admin/request-crawl">Request Crawl</a>
342
342
-
{{/if}}
343
343
-
</nav>
344
344
-
<div class="sidebar-footer">
345
345
-
<div class="session-info">Signed in as {{handle}}</div>
346
346
-
<form method="POST" action="/admin/logout">
347
347
-
<button type="submit">Sign out</button>
348
348
-
</form>
349
349
-
</div>
350
350
-
</aside>
123
123
+
{{> admin/partials/sidebar.hbs}}
351
124
352
125
<main class="main">
353
353
-
{{#if flash_success}}
354
354
-
<div class="flash-success">{{flash_success}}</div>
355
355
-
{{/if}}
356
356
-
{{#if flash_error}}
357
357
-
<div class="flash-error">{{flash_error}}</div>
358
358
-
{{/if}}
126
126
+
{{> admin/partials/flash.hbs}}
359
127
360
128
<h1 class="page-title">Create Account</h1>
361
129
+3
-212
html_templates/admin/dashboard.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Dashboard - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
154
154
-
155
155
-
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
158
-
margin-bottom: 24px;
159
159
-
}
160
160
-
161
161
-
.flash-success {
162
162
-
background: rgba(22, 163, 74, 0.1);
163
163
-
color: var(--success-color);
164
164
-
border: 1px solid rgba(22, 163, 74, 0.2);
165
165
-
border-radius: 8px;
166
166
-
padding: 10px 14px;
167
167
-
font-size: 0.875rem;
168
168
-
margin-bottom: 20px;
169
169
-
}
170
170
-
171
171
-
.flash-error {
172
172
-
background: rgba(220, 38, 38, 0.1);
173
173
-
color: var(--danger-color);
174
174
-
border: 1px solid rgba(220, 38, 38, 0.2);
175
175
-
border-radius: 8px;
176
176
-
padding: 10px 14px;
177
177
-
font-size: 0.875rem;
178
178
-
margin-bottom: 20px;
179
179
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
180
10
181
11
.cards {
182
12
display: grid;
···
259
89
.detail-row .value a:hover {
260
90
text-decoration: underline;
261
91
}
262
262
-
263
263
-
@media (max-width: 768px) {
264
264
-
.sidebar {
265
265
-
display: none;
266
266
-
}
267
267
-
268
268
-
.main {
269
269
-
margin-left: 0;
270
270
-
}
271
271
-
}
272
92
</style>
273
93
</head>
274
94
<body>
275
95
<div class="layout">
276
276
-
<aside class="sidebar">
277
277
-
<div class="sidebar-title">{{pds_hostname}}</div>
278
278
-
<div class="sidebar-subtitle">Admin Portal</div>
279
279
-
<nav>
280
280
-
<a href="/admin/dashboard" class="active">Dashboard</a>
281
281
-
{{#if can_view_accounts}}
282
282
-
<a href="/admin/accounts">Accounts</a>
283
283
-
{{/if}}
284
284
-
{{#if can_manage_invites}}
285
285
-
<a href="/admin/invite-codes">Invite Codes</a>
286
286
-
{{/if}}
287
287
-
{{#if can_create_account}}
288
288
-
<a href="/admin/create-account">Create Account</a>
289
289
-
{{/if}}
290
290
-
{{#if can_request_crawl}}
291
291
-
<a href="/admin/request-crawl">Request Crawl</a>
292
292
-
{{/if}}
293
293
-
</nav>
294
294
-
<div class="sidebar-footer">
295
295
-
<div class="session-info">Signed in as {{handle}}</div>
296
296
-
<form method="POST" action="/admin/logout">
297
297
-
<button type="submit">Sign out</button>
298
298
-
</form>
299
299
-
</div>
300
300
-
</aside>
96
96
+
{{> admin/partials/sidebar.hbs}}
301
97
302
98
<main class="main">
303
303
-
{{#if flash_success}}
304
304
-
<div class="flash-success">{{flash_success}}</div>
305
305
-
{{/if}}
306
306
-
{{#if flash_error}}
307
307
-
<div class="flash-error">{{flash_error}}</div>
308
308
-
{{/if}}
99
99
+
{{> admin/partials/flash.hbs}}
309
100
310
101
<h1 class="page-title">Dashboard</h1>
311
102
+4
-144
html_templates/admin/error.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Error - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
}
20
20
-
21
21
-
@media (prefers-color-scheme: dark) {
22
22
-
:root {
23
23
-
--brand-color: rgb(16, 131, 254);
24
24
-
--primary-color: rgb(255, 255, 255);
25
25
-
--secondary-color: rgb(133, 152, 173);
26
26
-
--bg-primary-color: rgb(7, 10, 13);
27
27
-
--bg-secondary-color: rgb(13, 18, 23);
28
28
-
--border-color: rgb(40, 45, 55);
29
29
-
}
30
30
-
}
31
31
-
32
32
-
:root.dark-mode {
33
33
-
--brand-color: rgb(16, 131, 254);
34
34
-
--primary-color: rgb(255, 255, 255);
35
35
-
--secondary-color: rgb(133, 152, 173);
36
36
-
--bg-primary-color: rgb(7, 10, 13);
37
37
-
--bg-secondary-color: rgb(13, 18, 23);
38
38
-
--border-color: rgb(40, 45, 55);
39
39
-
}
40
40
-
41
41
-
* { margin: 0; padding: 0; box-sizing: border-box; }
9
9
+
{{> admin/partials/base_css.hbs}}
42
10
43
11
body {
44
44
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
45
45
-
background: var(--bg-secondary-color);
46
46
-
color: var(--primary-color);
47
47
-
text-rendering: optimizeLegibility;
48
48
-
-webkit-font-smoothing: antialiased;
49
12
min-height: 100vh;
50
13
}
51
14
52
52
-
/* When logged in, use sidebar layout */
53
53
-
.layout { display: flex; min-height: 100vh; }
54
54
-
55
55
-
.sidebar {
56
56
-
width: 220px;
57
57
-
background: var(--bg-primary-color);
58
58
-
border-right: 1px solid var(--border-color);
59
59
-
padding: 20px 0;
60
60
-
position: fixed;
61
61
-
top: 0; left: 0; bottom: 0;
62
62
-
overflow-y: auto;
63
63
-
display: flex;
64
64
-
flex-direction: column;
65
65
-
}
66
66
-
67
67
-
.sidebar-title {
68
68
-
font-size: 0.8125rem;
69
69
-
font-weight: 700;
70
70
-
padding: 0 20px;
71
71
-
margin-bottom: 4px;
72
72
-
white-space: nowrap;
73
73
-
overflow: hidden;
74
74
-
text-overflow: ellipsis;
75
75
-
}
76
76
-
77
77
-
.sidebar-subtitle {
78
78
-
font-size: 0.6875rem;
79
79
-
color: var(--secondary-color);
80
80
-
padding: 0 20px;
81
81
-
margin-bottom: 20px;
82
82
-
}
83
83
-
84
84
-
.sidebar nav { flex: 1; }
85
85
-
86
86
-
.sidebar nav a {
87
87
-
display: block;
88
88
-
padding: 8px 20px;
89
89
-
font-size: 0.8125rem;
90
90
-
color: var(--secondary-color);
91
91
-
text-decoration: none;
92
92
-
transition: background 0.1s, color 0.1s;
93
93
-
}
94
94
-
95
95
-
.sidebar nav a:hover {
96
96
-
background: var(--bg-secondary-color);
97
97
-
color: var(--primary-color);
98
98
-
}
99
99
-
100
100
-
.sidebar-footer {
101
101
-
padding: 16px 20px 0;
102
102
-
border-top: 1px solid var(--border-color);
103
103
-
margin-top: 16px;
104
104
-
}
105
105
-
106
106
-
.sidebar-footer .session-info {
107
107
-
font-size: 0.75rem;
108
108
-
color: var(--secondary-color);
109
109
-
margin-bottom: 8px;
110
110
-
}
111
111
-
112
112
-
.sidebar-footer form { display: inline; }
113
113
-
114
114
-
.sidebar-footer button {
115
115
-
background: none;
116
116
-
border: none;
117
117
-
font-size: 0.75rem;
118
118
-
color: var(--secondary-color);
119
119
-
cursor: pointer;
120
120
-
padding: 0;
121
121
-
text-decoration: underline;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer button:hover { color: var(--primary-color); }
125
125
-
126
126
-
.main {
127
127
-
margin-left: 220px;
128
128
-
flex: 1;
129
129
-
padding: 32px;
130
130
-
max-width: 960px;
131
131
-
}
132
132
-
133
15
/* Standalone centered layout (when not logged in) */
134
16
.centered {
135
17
display: flex;
···
184
66
transition: opacity 0.15s;
185
67
}
186
68
187
187
-
.error-link:hover { opacity: 0.85; }
188
188
-
189
189
-
@media (max-width: 768px) {
190
190
-
.sidebar { display: none; }
191
191
-
.main { margin-left: 0; }
69
69
+
.error-link:hover {
70
70
+
opacity: 0.85;
192
71
}
193
72
</style>
194
73
</head>
···
196
75
{{#if handle}}
197
76
{{!-- Logged-in user: show sidebar layout --}}
198
77
<div class="layout">
199
199
-
<aside class="sidebar">
200
200
-
<div class="sidebar-title">{{pds_hostname}}</div>
201
201
-
<div class="sidebar-subtitle">Admin Portal</div>
202
202
-
<nav>
203
203
-
<a href="/admin/">Dashboard</a>
204
204
-
<a href="/admin/accounts">Accounts</a>
205
205
-
{{#if can_manage_invites}}
206
206
-
<a href="/admin/invite-codes">Invite Codes</a>
207
207
-
{{/if}}
208
208
-
{{#if can_create_account}}
209
209
-
<a href="/admin/create-account">Create Account</a>
210
210
-
{{/if}}
211
211
-
</nav>
212
212
-
<div class="sidebar-footer">
213
213
-
<div class="session-info">Signed in as {{handle}}</div>
214
214
-
<form method="POST" action="/admin/logout">
215
215
-
<button type="submit">Sign out</button>
216
216
-
</form>
217
217
-
</div>
218
218
-
</aside>
78
78
+
{{> admin/partials/sidebar.hbs}}
219
79
220
80
<main class="main">
221
81
<div class="error-card" style="text-align:center; margin: 60px auto;">
+3
-235
html_templates/admin/invite_codes.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Invite Codes - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
154
154
-
155
155
-
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
158
-
margin-bottom: 24px;
159
159
-
}
160
160
-
161
161
-
.flash-success {
162
162
-
background: rgba(22, 163, 74, 0.1);
163
163
-
color: var(--success-color);
164
164
-
border: 1px solid rgba(22, 163, 74, 0.2);
165
165
-
border-radius: 8px;
166
166
-
padding: 10px 14px;
167
167
-
font-size: 0.875rem;
168
168
-
margin-bottom: 20px;
169
169
-
}
170
170
-
171
171
-
.flash-error {
172
172
-
background: rgba(220, 38, 38, 0.1);
173
173
-
color: var(--danger-color);
174
174
-
border: 1px solid rgba(220, 38, 38, 0.2);
175
175
-
border-radius: 8px;
176
176
-
padding: 10px 14px;
177
177
-
font-size: 0.875rem;
178
178
-
margin-bottom: 20px;
179
179
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
180
10
181
11
.create-form {
182
12
display: flex;
···
212
42
border-color: var(--brand-color);
213
43
}
214
44
215
215
-
.btn {
216
216
-
display: inline-flex;
217
217
-
align-items: center;
218
218
-
justify-content: center;
219
219
-
padding: 10px 20px;
220
220
-
font-size: 0.875rem;
221
221
-
font-weight: 500;
222
222
-
border: none;
223
223
-
border-radius: 8px;
224
224
-
cursor: pointer;
225
225
-
transition: opacity 0.15s;
226
226
-
text-decoration: none;
227
227
-
}
228
228
-
229
229
-
.btn:hover {
230
230
-
opacity: 0.85;
231
231
-
}
232
232
-
233
233
-
.btn-primary {
234
234
-
background: var(--brand-color);
235
235
-
color: #fff;
236
236
-
}
237
237
-
238
45
.btn-small {
239
46
padding: 6px 12px;
240
47
font-size: 0.75rem;
···
353
160
.load-more a:hover {
354
161
text-decoration: underline;
355
162
}
356
356
-
357
357
-
@media (max-width: 768px) {
358
358
-
.sidebar {
359
359
-
display: none;
360
360
-
}
361
361
-
362
362
-
.main {
363
363
-
margin-left: 0;
364
364
-
}
365
365
-
}
366
163
</style>
367
164
</head>
368
165
<body>
369
166
<div class="layout">
370
370
-
<aside class="sidebar">
371
371
-
<div class="sidebar-title">{{pds_hostname}}</div>
372
372
-
<div class="sidebar-subtitle">Admin Portal</div>
373
373
-
<nav>
374
374
-
<a href="/admin/dashboard">Dashboard</a>
375
375
-
{{#if can_view_accounts}}
376
376
-
<a href="/admin/accounts">Accounts</a>
377
377
-
{{/if}}
378
378
-
{{#if can_manage_invites}}
379
379
-
<a href="/admin/invite-codes" class="active">Invite Codes</a>
380
380
-
{{/if}}
381
381
-
{{#if can_create_account}}
382
382
-
<a href="/admin/create-account">Create Account</a>
383
383
-
{{/if}}
384
384
-
{{#if can_request_crawl}}
385
385
-
<a href="/admin/request-crawl">Request Crawl</a>
386
386
-
{{/if}}
387
387
-
</nav>
388
388
-
<div class="sidebar-footer">
389
389
-
<div class="session-info">Signed in as {{handle}}</div>
390
390
-
<form method="POST" action="/admin/logout">
391
391
-
<button type="submit">Sign out</button>
392
392
-
</form>
393
393
-
</div>
394
394
-
</aside>
167
167
+
{{> admin/partials/sidebar.hbs}}
395
168
396
169
<main class="main">
397
397
-
{{#if flash_success}}
398
398
-
<div class="flash-success">{{flash_success}}</div>
399
399
-
{{/if}}
400
400
-
{{#if flash_error}}
401
401
-
<div class="flash-error">{{flash_error}}</div>
402
402
-
{{/if}}
170
170
+
{{> admin/partials/flash.hbs}}
403
171
404
172
<h1 class="page-title">Invite Codes</h1>
405
173
+1
-56
html_templates/admin/login.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Admin Login - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
}
20
20
-
21
21
-
@media (prefers-color-scheme: dark) {
22
22
-
:root {
23
23
-
--brand-color: rgb(16, 131, 254);
24
24
-
--primary-color: rgb(255, 255, 255);
25
25
-
--secondary-color: rgb(133, 152, 173);
26
26
-
--bg-primary-color: rgb(7, 10, 13);
27
27
-
--bg-secondary-color: rgb(13, 18, 23);
28
28
-
--border-color: rgb(40, 45, 55);
29
29
-
}
30
30
-
}
31
31
-
32
32
-
:root.dark-mode {
33
33
-
--brand-color: rgb(16, 131, 254);
34
34
-
--primary-color: rgb(255, 255, 255);
35
35
-
--secondary-color: rgb(133, 152, 173);
36
36
-
--bg-primary-color: rgb(7, 10, 13);
37
37
-
--bg-secondary-color: rgb(13, 18, 23);
38
38
-
--border-color: rgb(40, 45, 55);
39
39
-
}
40
40
-
41
41
-
* { margin: 0; padding: 0; box-sizing: border-box; }
9
9
+
{{> admin/partials/base_css.hbs}}
42
10
43
11
body {
44
44
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
45
45
-
background: var(--bg-secondary-color);
46
46
-
color: var(--primary-color);
47
47
-
text-rendering: optimizeLegibility;
48
48
-
-webkit-font-smoothing: antialiased;
49
12
min-height: 100vh;
50
13
display: flex;
51
14
align-items: center;
···
104
67
border-color: var(--brand-color);
105
68
}
106
69
107
107
-
.btn {
108
108
-
display: inline-flex;
109
109
-
align-items: center;
110
110
-
justify-content: center;
111
111
-
padding: 10px 20px;
112
112
-
font-size: 0.875rem;
113
113
-
font-weight: 500;
114
114
-
border: none;
115
115
-
border-radius: 8px;
116
116
-
cursor: pointer;
117
117
-
transition: opacity 0.15s;
118
118
-
text-decoration: none;
119
119
-
}
120
120
-
121
121
-
.btn:hover { opacity: 0.85; }
122
122
-
123
70
.btn-primary {
124
124
-
background: var(--brand-color);
125
125
-
color: #fff;
126
71
width: 100%;
127
72
}
128
73
+216
html_templates/admin/partials/base_css.hbs
···
1
1
+
:root,
2
2
+
:root.light-mode {
3
3
+
--brand-color: rgb(16, 131, 254);
4
4
+
--primary-color: rgb(7, 10, 13);
5
5
+
--secondary-color: rgb(66, 86, 108);
6
6
+
--bg-primary-color: rgb(255, 255, 255);
7
7
+
--bg-secondary-color: rgb(240, 242, 245);
8
8
+
--border-color: rgb(220, 225, 230);
9
9
+
--danger-color: rgb(220, 38, 38);
10
10
+
--success-color: rgb(22, 163, 74);
11
11
+
--warning-color: rgb(234, 179, 8);
12
12
+
--table-stripe: rgba(0, 0, 0, 0.02);
13
13
+
}
14
14
+
15
15
+
@media (prefers-color-scheme: dark) {
16
16
+
:root {
17
17
+
--brand-color: rgb(16, 131, 254);
18
18
+
--primary-color: rgb(255, 255, 255);
19
19
+
--secondary-color: rgb(133, 152, 173);
20
20
+
--bg-primary-color: rgb(7, 10, 13);
21
21
+
--bg-secondary-color: rgb(13, 18, 23);
22
22
+
--border-color: rgb(40, 45, 55);
23
23
+
--table-stripe: rgba(255, 255, 255, 0.02);
24
24
+
}
25
25
+
}
26
26
+
27
27
+
:root.dark-mode {
28
28
+
--brand-color: rgb(16, 131, 254);
29
29
+
--primary-color: rgb(255, 255, 255);
30
30
+
--secondary-color: rgb(133, 152, 173);
31
31
+
--bg-primary-color: rgb(7, 10, 13);
32
32
+
--bg-secondary-color: rgb(13, 18, 23);
33
33
+
--border-color: rgb(40, 45, 55);
34
34
+
--table-stripe: rgba(255, 255, 255, 0.02);
35
35
+
}
36
36
+
37
37
+
* {
38
38
+
margin: 0;
39
39
+
padding: 0;
40
40
+
box-sizing: border-box;
41
41
+
}
42
42
+
43
43
+
body {
44
44
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
45
45
+
background: var(--bg-secondary-color);
46
46
+
color: var(--primary-color);
47
47
+
text-rendering: optimizeLegibility;
48
48
+
-webkit-font-smoothing: antialiased;
49
49
+
}
50
50
+
51
51
+
.layout {
52
52
+
display: flex;
53
53
+
min-height: 100vh;
54
54
+
}
55
55
+
56
56
+
.sidebar {
57
57
+
width: 220px;
58
58
+
background: var(--bg-primary-color);
59
59
+
border-right: 1px solid var(--border-color);
60
60
+
padding: 20px 0;
61
61
+
position: fixed;
62
62
+
top: 0;
63
63
+
left: 0;
64
64
+
bottom: 0;
65
65
+
overflow-y: auto;
66
66
+
display: flex;
67
67
+
flex-direction: column;
68
68
+
}
69
69
+
70
70
+
.sidebar-title {
71
71
+
font-size: 0.8125rem;
72
72
+
font-weight: 700;
73
73
+
padding: 0 20px;
74
74
+
margin-bottom: 4px;
75
75
+
white-space: nowrap;
76
76
+
overflow: hidden;
77
77
+
text-overflow: ellipsis;
78
78
+
}
79
79
+
80
80
+
.sidebar-subtitle {
81
81
+
font-size: 0.6875rem;
82
82
+
color: var(--secondary-color);
83
83
+
padding: 0 20px;
84
84
+
margin-bottom: 20px;
85
85
+
}
86
86
+
87
87
+
.sidebar nav {
88
88
+
flex: 1;
89
89
+
}
90
90
+
91
91
+
.sidebar nav a {
92
92
+
display: block;
93
93
+
padding: 8px 20px;
94
94
+
font-size: 0.8125rem;
95
95
+
color: var(--secondary-color);
96
96
+
text-decoration: none;
97
97
+
transition: background 0.1s, color 0.1s;
98
98
+
}
99
99
+
100
100
+
.sidebar nav a:hover {
101
101
+
background: var(--bg-secondary-color);
102
102
+
color: var(--primary-color);
103
103
+
}
104
104
+
105
105
+
.sidebar nav a.active {
106
106
+
color: var(--brand-color);
107
107
+
font-weight: 500;
108
108
+
}
109
109
+
110
110
+
.sidebar-footer {
111
111
+
padding: 16px 20px 0;
112
112
+
border-top: 1px solid var(--border-color);
113
113
+
margin-top: 16px;
114
114
+
}
115
115
+
116
116
+
.sidebar-footer .session-info {
117
117
+
font-size: 0.75rem;
118
118
+
color: var(--secondary-color);
119
119
+
margin-bottom: 8px;
120
120
+
}
121
121
+
122
122
+
.sidebar-footer form {
123
123
+
display: inline;
124
124
+
}
125
125
+
126
126
+
.sidebar-footer button {
127
127
+
background: none;
128
128
+
border: none;
129
129
+
font-size: 0.75rem;
130
130
+
color: var(--secondary-color);
131
131
+
cursor: pointer;
132
132
+
padding: 0;
133
133
+
text-decoration: underline;
134
134
+
}
135
135
+
136
136
+
.sidebar-footer button:hover {
137
137
+
color: var(--primary-color);
138
138
+
}
139
139
+
140
140
+
.main {
141
141
+
margin-left: 220px;
142
142
+
flex: 1;
143
143
+
padding: 32px;
144
144
+
max-width: 960px;
145
145
+
}
146
146
+
147
147
+
.page-title {
148
148
+
font-size: 1.5rem;
149
149
+
font-weight: 700;
150
150
+
margin-bottom: 24px;
151
151
+
}
152
152
+
153
153
+
.flash-success {
154
154
+
background: rgba(22, 163, 74, 0.1);
155
155
+
color: var(--success-color);
156
156
+
border: 1px solid rgba(22, 163, 74, 0.2);
157
157
+
border-radius: 8px;
158
158
+
padding: 10px 14px;
159
159
+
font-size: 0.875rem;
160
160
+
margin-bottom: 20px;
161
161
+
}
162
162
+
163
163
+
.flash-error {
164
164
+
background: rgba(220, 38, 38, 0.1);
165
165
+
color: var(--danger-color);
166
166
+
border: 1px solid rgba(220, 38, 38, 0.2);
167
167
+
border-radius: 8px;
168
168
+
padding: 10px 14px;
169
169
+
font-size: 0.875rem;
170
170
+
margin-bottom: 20px;
171
171
+
}
172
172
+
173
173
+
.btn {
174
174
+
display: inline-flex;
175
175
+
align-items: center;
176
176
+
justify-content: center;
177
177
+
padding: 10px 20px;
178
178
+
font-size: 0.875rem;
179
179
+
font-weight: 500;
180
180
+
border: none;
181
181
+
border-radius: 8px;
182
182
+
cursor: pointer;
183
183
+
transition: opacity 0.15s;
184
184
+
text-decoration: none;
185
185
+
}
186
186
+
187
187
+
.btn:hover {
188
188
+
opacity: 0.85;
189
189
+
}
190
190
+
191
191
+
.btn-primary {
192
192
+
background: var(--brand-color);
193
193
+
color: #fff;
194
194
+
}
195
195
+
196
196
+
.btn-danger {
197
197
+
background: var(--danger-color);
198
198
+
color: #fff;
199
199
+
border-color: var(--danger-color);
200
200
+
}
201
201
+
202
202
+
.btn-warning {
203
203
+
background: var(--warning-color);
204
204
+
color: #000;
205
205
+
border-color: var(--warning-color);
206
206
+
}
207
207
+
208
208
+
@media (max-width: 768px) {
209
209
+
.sidebar {
210
210
+
display: none;
211
211
+
}
212
212
+
213
213
+
.main {
214
214
+
margin-left: 0;
215
215
+
}
216
216
+
}
+6
html_templates/admin/partials/flash.hbs
···
1
1
+
{{#if flash_success}}
2
2
+
<div class="flash-success">{{flash_success}}</div>
3
3
+
{{/if}}
4
4
+
{{#if flash_error}}
5
5
+
<div class="flash-error">{{flash_error}}</div>
6
6
+
{{/if}}
+25
html_templates/admin/partials/sidebar.hbs
···
1
1
+
<aside class="sidebar">
2
2
+
<div class="sidebar-title">{{pds_hostname}}</div>
3
3
+
<div class="sidebar-subtitle">Admin Portal</div>
4
4
+
<nav>
5
5
+
<a href="/admin/dashboard" {{#if (eq active_page "dashboard")}}class="active"{{/if}}>Dashboard</a>
6
6
+
{{#if can_view_accounts}}
7
7
+
<a href="/admin/accounts" {{#if (eq active_page "accounts")}}class="active"{{/if}}>Accounts</a>
8
8
+
{{/if}}
9
9
+
{{#if can_manage_invites}}
10
10
+
<a href="/admin/invite-codes" {{#if (eq active_page "invite_codes")}}class="active"{{/if}}>Invite Codes</a>
11
11
+
{{/if}}
12
12
+
{{#if can_create_account}}
13
13
+
<a href="/admin/create-account" {{#if (eq active_page "create_account")}}class="active"{{/if}}>Create Account</a>
14
14
+
{{/if}}
15
15
+
{{#if can_request_crawl}}
16
16
+
<a href="/admin/request-crawl" {{#if (eq active_page "request_crawl")}}class="active"{{/if}}>Request Crawl</a>
17
17
+
{{/if}}
18
18
+
</nav>
19
19
+
<div class="sidebar-footer">
20
20
+
<div class="session-info">Signed in as {{handle}}</div>
21
21
+
<form method="POST" action="/admin/logout">
22
22
+
<button type="submit">Sign out</button>
23
23
+
</form>
24
24
+
</div>
25
25
+
</aside>
+3
-231
html_templates/admin/request_crawl.hbs
···
6
6
<meta name="referrer" content="origin-when-cross-origin"/>
7
7
<title>Request Crawl - {{pds_hostname}}</title>
8
8
<style>
9
9
-
:root,
10
10
-
:root.light-mode {
11
11
-
--brand-color: rgb(16, 131, 254);
12
12
-
--primary-color: rgb(7, 10, 13);
13
13
-
--secondary-color: rgb(66, 86, 108);
14
14
-
--bg-primary-color: rgb(255, 255, 255);
15
15
-
--bg-secondary-color: rgb(240, 242, 245);
16
16
-
--border-color: rgb(220, 225, 230);
17
17
-
--danger-color: rgb(220, 38, 38);
18
18
-
--success-color: rgb(22, 163, 74);
19
19
-
--warning-color: rgb(234, 179, 8);
20
20
-
--table-stripe: rgba(0, 0, 0, 0.02);
21
21
-
}
22
22
-
23
23
-
@media (prefers-color-scheme: dark) {
24
24
-
:root {
25
25
-
--brand-color: rgb(16, 131, 254);
26
26
-
--primary-color: rgb(255, 255, 255);
27
27
-
--secondary-color: rgb(133, 152, 173);
28
28
-
--bg-primary-color: rgb(7, 10, 13);
29
29
-
--bg-secondary-color: rgb(13, 18, 23);
30
30
-
--border-color: rgb(40, 45, 55);
31
31
-
--table-stripe: rgba(255, 255, 255, 0.02);
32
32
-
}
33
33
-
}
34
34
-
35
35
-
:root.dark-mode {
36
36
-
--brand-color: rgb(16, 131, 254);
37
37
-
--primary-color: rgb(255, 255, 255);
38
38
-
--secondary-color: rgb(133, 152, 173);
39
39
-
--bg-primary-color: rgb(7, 10, 13);
40
40
-
--bg-secondary-color: rgb(13, 18, 23);
41
41
-
--border-color: rgb(40, 45, 55);
42
42
-
--table-stripe: rgba(255, 255, 255, 0.02);
43
43
-
}
44
44
-
45
45
-
* {
46
46
-
margin: 0;
47
47
-
padding: 0;
48
48
-
box-sizing: border-box;
49
49
-
}
50
50
-
51
51
-
body {
52
52
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
53
-
background: var(--bg-secondary-color);
54
54
-
color: var(--primary-color);
55
55
-
text-rendering: optimizeLegibility;
56
56
-
-webkit-font-smoothing: antialiased;
57
57
-
}
58
58
-
59
59
-
.layout {
60
60
-
display: flex;
61
61
-
min-height: 100vh;
62
62
-
}
63
63
-
64
64
-
.sidebar {
65
65
-
width: 220px;
66
66
-
background: var(--bg-primary-color);
67
67
-
border-right: 1px solid var(--border-color);
68
68
-
padding: 20px 0;
69
69
-
position: fixed;
70
70
-
top: 0;
71
71
-
left: 0;
72
72
-
bottom: 0;
73
73
-
overflow-y: auto;
74
74
-
display: flex;
75
75
-
flex-direction: column;
76
76
-
}
77
77
-
78
78
-
.sidebar-title {
79
79
-
font-size: 0.8125rem;
80
80
-
font-weight: 700;
81
81
-
padding: 0 20px;
82
82
-
margin-bottom: 4px;
83
83
-
white-space: nowrap;
84
84
-
overflow: hidden;
85
85
-
text-overflow: ellipsis;
86
86
-
}
87
87
-
88
88
-
.sidebar-subtitle {
89
89
-
font-size: 0.6875rem;
90
90
-
color: var(--secondary-color);
91
91
-
padding: 0 20px;
92
92
-
margin-bottom: 20px;
93
93
-
}
94
94
-
95
95
-
.sidebar nav {
96
96
-
flex: 1;
97
97
-
}
98
98
-
99
99
-
.sidebar nav a {
100
100
-
display: block;
101
101
-
padding: 8px 20px;
102
102
-
font-size: 0.8125rem;
103
103
-
color: var(--secondary-color);
104
104
-
text-decoration: none;
105
105
-
transition: background 0.1s, color 0.1s;
106
106
-
}
107
107
-
108
108
-
.sidebar nav a:hover {
109
109
-
background: var(--bg-secondary-color);
110
110
-
color: var(--primary-color);
111
111
-
}
112
112
-
113
113
-
.sidebar nav a.active {
114
114
-
color: var(--brand-color);
115
115
-
font-weight: 500;
116
116
-
}
117
117
-
118
118
-
.sidebar-footer {
119
119
-
padding: 16px 20px 0;
120
120
-
border-top: 1px solid var(--border-color);
121
121
-
margin-top: 16px;
122
122
-
}
123
123
-
124
124
-
.sidebar-footer .session-info {
125
125
-
font-size: 0.75rem;
126
126
-
color: var(--secondary-color);
127
127
-
margin-bottom: 8px;
128
128
-
}
129
129
-
130
130
-
.sidebar-footer form {
131
131
-
display: inline;
132
132
-
}
133
133
-
134
134
-
.sidebar-footer button {
135
135
-
background: none;
136
136
-
border: none;
137
137
-
font-size: 0.75rem;
138
138
-
color: var(--secondary-color);
139
139
-
cursor: pointer;
140
140
-
padding: 0;
141
141
-
text-decoration: underline;
142
142
-
}
143
143
-
144
144
-
.sidebar-footer button:hover {
145
145
-
color: var(--primary-color);
146
146
-
}
147
147
-
148
148
-
.main {
149
149
-
margin-left: 220px;
150
150
-
flex: 1;
151
151
-
padding: 32px;
152
152
-
max-width: 960px;
153
153
-
}
9
9
+
{{> admin/partials/base_css.hbs}}
154
10
155
11
.page-title {
156
156
-
font-size: 1.5rem;
157
157
-
font-weight: 700;
158
12
margin-bottom: 8px;
159
13
}
160
14
···
162
16
font-size: 0.875rem;
163
17
color: var(--secondary-color);
164
18
margin-bottom: 24px;
165
165
-
}
166
166
-
167
167
-
.flash-success {
168
168
-
background: rgba(22, 163, 74, 0.1);
169
169
-
color: var(--success-color);
170
170
-
border: 1px solid rgba(22, 163, 74, 0.2);
171
171
-
border-radius: 8px;
172
172
-
padding: 10px 14px;
173
173
-
font-size: 0.875rem;
174
174
-
margin-bottom: 20px;
175
175
-
}
176
176
-
177
177
-
.flash-error {
178
178
-
background: rgba(220, 38, 38, 0.1);
179
179
-
color: var(--danger-color);
180
180
-
border: 1px solid rgba(220, 38, 38, 0.2);
181
181
-
border-radius: 8px;
182
182
-
padding: 10px 14px;
183
183
-
font-size: 0.875rem;
184
184
-
margin-bottom: 20px;
185
19
}
186
20
187
21
.form-card {
···
225
59
color: var(--secondary-color);
226
60
margin-top: 4px;
227
61
}
228
228
-
229
229
-
.btn {
230
230
-
display: inline-flex;
231
231
-
align-items: center;
232
232
-
justify-content: center;
233
233
-
padding: 10px 20px;
234
234
-
font-size: 0.875rem;
235
235
-
font-weight: 500;
236
236
-
border: none;
237
237
-
border-radius: 8px;
238
238
-
cursor: pointer;
239
239
-
transition: opacity 0.15s;
240
240
-
text-decoration: none;
241
241
-
}
242
242
-
243
243
-
.btn:hover {
244
244
-
opacity: 0.85;
245
245
-
}
246
246
-
247
247
-
.btn-primary {
248
248
-
background: var(--brand-color);
249
249
-
color: #fff;
250
250
-
}
251
251
-
252
252
-
@media (max-width: 768px) {
253
253
-
.sidebar {
254
254
-
display: none;
255
255
-
}
256
256
-
257
257
-
.main {
258
258
-
margin-left: 0;
259
259
-
}
260
260
-
}
261
62
</style>
262
63
</head>
263
64
<body>
264
65
<div class="layout">
265
265
-
<aside class="sidebar">
266
266
-
<div class="sidebar-title">{{pds_hostname}}</div>
267
267
-
<div class="sidebar-subtitle">Admin Portal</div>
268
268
-
<nav>
269
269
-
<a href="/admin/dashboard">Dashboard</a>
270
270
-
{{#if can_view_accounts}}
271
271
-
<a href="/admin/accounts">Accounts</a>
272
272
-
{{/if}}
273
273
-
{{#if can_manage_invites}}
274
274
-
<a href="/admin/invite-codes">Invite Codes</a>
275
275
-
{{/if}}
276
276
-
{{#if can_create_account}}
277
277
-
<a href="/admin/create-account">Create Account</a>
278
278
-
{{/if}}
279
279
-
{{#if can_request_crawl}}
280
280
-
<a href="/admin/request-crawl" class="active">Request Crawl</a>
281
281
-
{{/if}}
282
282
-
</nav>
283
283
-
<div class="sidebar-footer">
284
284
-
<div class="session-info">Signed in as {{handle}}</div>
285
285
-
<form method="POST" action="/admin/logout">
286
286
-
<button type="submit">Sign out</button>
287
287
-
</form>
288
288
-
</div>
289
289
-
</aside>
66
66
+
{{> admin/partials/sidebar.hbs}}
290
67
291
68
<main class="main">
292
292
-
{{#if flash_success}}
293
293
-
<div class="flash-success">{{flash_success}}</div>
294
294
-
{{/if}}
295
295
-
{{#if flash_error}}
296
296
-
<div class="flash-error">{{flash_error}}</div>
297
297
-
{{/if}}
69
69
+
{{> admin/partials/flash.hbs}}
298
70
299
71
<h1 class="page-title">Request Crawl</h1>
300
72
<p class="page-description">Request a relay to crawl this PDS. This sends your PDS hostname to the relay so it
+21
-19
src/main.rs
···
477
477
}
478
478
479
479
// Background cleanup for admin sessions
480
480
-
let cleanup_pool = state.pds_gatekeeper_pool.clone();
481
480
let admin_enabled = state.admin_rbac_config.is_some();
482
482
-
let admin_session_ttl_in_mins = state.app_config.admin_session_ttl_hours * 60;
483
483
-
tokio::spawn(async move {
484
484
-
let mut interval = tokio::time::interval(Duration::from_secs(300));
485
485
-
loop {
486
486
-
interval.tick().await;
487
487
-
if admin_enabled {
488
488
-
if let Err(e) = admin::session::cleanup_expired_sessions(&cleanup_pool).await {
489
489
-
tracing::error!("Failed to cleanup expired admin sessions: {}", e);
490
490
-
}
491
491
-
if let Err(e) = admin::store::cleanup_stale_auth_requests(
492
492
-
&cleanup_pool,
493
493
-
admin_session_ttl_in_mins as i64,
494
494
-
)
495
495
-
.await
496
496
-
{
497
497
-
tracing::error!("Failed to cleanup stale OAuth auth requests: {}", e);
481
481
+
if admin_enabled {
482
482
+
let admin_session_ttl_in_mins = state.app_config.admin_session_ttl_hours * 60;
483
483
+
let cleanup_pool = state.pds_gatekeeper_pool.clone();
484
484
+
tokio::spawn(async move {
485
485
+
let mut interval = tokio::time::interval(Duration::from_secs(300));
486
486
+
loop {
487
487
+
interval.tick().await;
488
488
+
if admin_enabled {
489
489
+
if let Err(e) = admin::session::cleanup_expired_sessions(&cleanup_pool).await {
490
490
+
tracing::error!("Failed to cleanup expired admin sessions: {}", e);
491
491
+
}
492
492
+
if let Err(e) = admin::store::cleanup_stale_auth_requests(
493
493
+
&cleanup_pool,
494
494
+
admin_session_ttl_in_mins as i64,
495
495
+
)
496
496
+
.await
497
497
+
{
498
498
+
tracing::error!("Failed to cleanup stale OAuth auth requests: {}", e);
499
499
+
}
498
500
}
499
501
}
500
500
-
}
501
501
-
});
502
502
+
});
503
503
+
}
502
504
503
505
let request_logging = env::var("GATEKEEPER_REQUEST_LOGGING")
504
506
.map(|v| v.eq_ignore_ascii_case("true") || v == "1")