@dsh-plus/access-gate 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/style.css CHANGED
@@ -1,4 +1,4 @@
1
- .obLtqa_block {
1
+ .nVxsMW_block {
2
2
  --dsl-diff-radius: 12px;
3
3
  --dsl-diff-line-height: 22px;
4
4
  color: var(--dsw-alias-label-primary);
@@ -8,7 +8,7 @@
8
8
  position: relative;
9
9
  }
10
10
 
11
- .obLtqa_copyButton {
11
+ .nVxsMW_copyButton {
12
12
  z-index: 1;
13
13
  color: var(--dsw-alias-label-secondary);
14
14
  cursor: pointer;
@@ -22,46 +22,46 @@
22
22
  right: 12px;
23
23
  }
24
24
 
25
- .obLtqa_body {
25
+ .nVxsMW_body {
26
26
  font: var(--dsw-font-markdown-code-block);
27
27
  padding: 12px 14px;
28
28
  overflow: auto hidden;
29
29
  }
30
30
 
31
- .obLtqa_line {
31
+ .nVxsMW_line {
32
32
  min-height: var(--dsl-diff-line-height);
33
33
  white-space: pre;
34
34
  }
35
35
 
36
- .obLtqa_path {
36
+ .nVxsMW_path {
37
37
  color: var(--dsw-alias-label-primary);
38
38
  padding-right: 56px;
39
39
  font-weight: 600;
40
40
  }
41
41
 
42
- .obLtqa_gap {
42
+ .nVxsMW_gap {
43
43
  color: var(--dsw-alias-label-tertiary);
44
44
  }
45
45
 
46
- .obLtqa_del:before {
46
+ .nVxsMW_del:before {
47
47
  content: "- ";
48
48
  color: var(--dsw-alias-state-error-primary);
49
49
  }
50
50
 
51
- .obLtqa_del {
51
+ .nVxsMW_del {
52
52
  color: var(--dsw-alias-state-error-primary);
53
53
  }
54
54
 
55
- .obLtqa_add:before {
55
+ .nVxsMW_add:before {
56
56
  content: "+ ";
57
57
  color: var(--dsw-alias-state-success-primary);
58
58
  }
59
59
 
60
- .obLtqa_add {
60
+ .nVxsMW_add {
61
61
  color: var(--dsw-alias-state-success-primary);
62
62
  }
63
63
 
64
- .obLtqa_expand {
64
+ .nVxsMW_expand {
65
65
  width: 100%;
66
66
  color: var(--dsw-alias-label-tertiary);
67
67
  cursor: pointer;
@@ -73,16 +73,16 @@
73
73
  display: block;
74
74
  }
75
75
 
76
- .obLtqa_expand:hover {
76
+ .nVxsMW_expand:hover {
77
77
  color: var(--dsw-alias-label-secondary);
78
78
  }
79
79
 
80
- .obLtqa_footer {
80
+ .nVxsMW_footer {
81
81
  font: var(--dsw-font-markdown-code-block);
82
82
  color: var(--dsw-alias-label-tertiary);
83
83
  padding: 0 14px 12px;
84
84
  }
85
- .folzqq_block {
85
+ .Hbqh2W_block {
86
86
  --dsl-code-block-banner-background-color: var(--dsw-alias-markdown-code-block-banner);
87
87
  --dsl-code-block-border-radius: 12px;
88
88
  --dsl-code-block-banner-font: 11px/18px var(--dsw-font-family);
@@ -94,11 +94,11 @@
94
94
  position: relative;
95
95
  }
96
96
 
97
- .folzqq_block:not(:last-child) {
97
+ .Hbqh2W_block:not(:last-child) {
98
98
  margin-bottom: 11px;
99
99
  }
100
100
 
101
- .folzqq_bannerWrap {
101
+ .Hbqh2W_bannerWrap {
102
102
  z-index: 6;
103
103
  background-color: var(--dsw-alias-bg-base);
104
104
  border-top-left-radius: var(--dsl-code-block-border-radius);
@@ -107,7 +107,7 @@
107
107
  top: 0;
108
108
  }
109
109
 
110
- .folzqq_banner {
110
+ .Hbqh2W_banner {
111
111
  background: var(--dsl-code-block-banner-background-color);
112
112
  font: var(--dsl-code-block-banner-font);
113
113
  border-top-left-radius: var(--dsl-code-block-border-radius);
@@ -119,7 +119,7 @@
119
119
  display: flex;
120
120
  }
121
121
 
122
- .folzqq_infostring {
122
+ .Hbqh2W_infostring {
123
123
  color: var(--dsw-alias-label-primary);
124
124
  font-family: var(--ds-font-family-code);
125
125
  text-overflow: ellipsis;
@@ -130,13 +130,13 @@
130
130
  overflow: hidden;
131
131
  }
132
132
 
133
- .folzqq_action {
133
+ .Hbqh2W_action {
134
134
  flex-shrink: 0;
135
135
  align-items: center;
136
136
  display: flex;
137
137
  }
138
138
 
139
- .folzqq_copyButton {
139
+ .Hbqh2W_copyButton {
140
140
  color: inherit;
141
141
  cursor: pointer;
142
142
  font: inherit;
@@ -146,7 +146,7 @@
146
146
  padding: 0;
147
147
  }
148
148
 
149
- .folzqq_block :where(pre) {
149
+ .Hbqh2W_block :where(pre) {
150
150
  font: var(--dsl-code-block-content-font);
151
151
  white-space: pre-wrap;
152
152
  word-break: break-all;
@@ -158,72 +158,72 @@
158
158
  margin: 0 !important;
159
159
  }
160
160
 
161
- .folzqq_block :where(pre.folzqq_shiki) {
161
+ .Hbqh2W_block :where(pre.Hbqh2W_shiki) {
162
162
  background: var(--dsw-alias-markdown-code-block) !important;
163
163
  }
164
164
 
165
- .folzqq_block :where(pre) code {
165
+ .Hbqh2W_block :where(pre) code {
166
166
  font: inherit;
167
167
  background: none;
168
168
  padding: 0;
169
169
  }
170
170
 
171
- .folzqq_plain {
171
+ .Hbqh2W_plain {
172
172
  color: var(--dsw-alias-label-primary);
173
173
  }
174
- .vrxa6G_markdown {
174
+ .BL43XG_markdown {
175
175
  overflow-wrap: anywhere;
176
176
  min-width: 0;
177
177
  font: var(--dsw-font-markdown-base);
178
178
  color: var(--dsw-alias-label-primary);
179
179
  }
180
180
 
181
- .vrxa6G_markdown strong {
181
+ .BL43XG_markdown strong {
182
182
  font-weight: 600;
183
183
  }
184
184
 
185
- .vrxa6G_markdown h1 {
185
+ .BL43XG_markdown h1 {
186
186
  font: var(--dsw-font-markdown-h1);
187
187
  margin: 32px 0 16px;
188
188
  }
189
189
 
190
- .vrxa6G_markdown h2 {
190
+ .BL43XG_markdown h2 {
191
191
  font: var(--dsw-font-markdown-h2);
192
192
  margin: 32px 0 16px;
193
193
  }
194
194
 
195
- .vrxa6G_markdown h3 {
195
+ .BL43XG_markdown h3 {
196
196
  font: var(--dsw-font-markdown-h3);
197
197
  margin: 32px 0 16px;
198
198
  }
199
199
 
200
- .vrxa6G_markdown h4 {
200
+ .BL43XG_markdown h4 {
201
201
  font: var(--dsw-font-markdown-h4);
202
202
  margin: 16px 0;
203
203
  }
204
204
 
205
- .vrxa6G_markdown :where(h5, h6) {
205
+ .BL43XG_markdown :where(h5, h6) {
206
206
  font: var(--dsw-font-markdown-base-strong);
207
207
  margin: 16px 0;
208
208
  }
209
209
 
210
- .vrxa6G_markdown :where(h1, h2, h3, h4, h5, h6) strong {
210
+ .BL43XG_markdown :where(h1, h2, h3, h4, h5, h6) strong {
211
211
  font-weight: inherit;
212
212
  }
213
213
 
214
- .vrxa6G_markdown p {
214
+ .BL43XG_markdown p {
215
215
  margin: 16px 0;
216
216
  }
217
217
 
218
- .vrxa6G_markdown :where(h4, h5, h6) + :where(ul, ol) {
218
+ .BL43XG_markdown :where(h4, h5, h6) + :where(ul, ol) {
219
219
  margin-top: 8px;
220
220
  }
221
221
 
222
- .vrxa6G_markdown :where(h4, h5, h6):has( + :where(ul, ol)) {
222
+ .BL43XG_markdown :where(h4, h5, h6):has( + :where(ul, ol)) {
223
223
  margin-bottom: 8px;
224
224
  }
225
225
 
226
- .vrxa6G_markdown a {
226
+ .BL43XG_markdown a {
227
227
  color: var(--dsw-alias-link);
228
228
  transition: box-shadow var(--ds-transition-duration) var(--ds-ease-in-out);
229
229
  border: 2px solid #fff0;
@@ -235,17 +235,17 @@
235
235
  position: relative;
236
236
  }
237
237
 
238
- .vrxa6G_markdown a:hover, .vrxa6G_markdown a:focus {
238
+ .BL43XG_markdown a:hover, .BL43XG_markdown a:focus {
239
239
  text-decoration: underline dotted var(--dsw-alias-link);
240
240
  text-underline-offset: 3px;
241
241
  outline: none;
242
242
  }
243
243
 
244
- .vrxa6G_markdown a:focus-visible {
244
+ .BL43XG_markdown a:focus-visible {
245
245
  box-shadow: 0 0 0 2px var(--dsw-alias-state-business-primary);
246
246
  }
247
247
 
248
- .vrxa6G_linkIcon {
248
+ .BL43XG_linkIcon {
249
249
  vertical-align: -.25em;
250
250
  flex: none;
251
251
  width: 1.1em;
@@ -253,46 +253,46 @@
253
253
  margin-right: 5px;
254
254
  }
255
255
 
256
- .vrxa6G_markdown :where(ul, ol) {
256
+ .BL43XG_markdown :where(ul, ol) {
257
257
  margin: 16px 0;
258
258
  padding-left: 18px;
259
259
  }
260
260
 
261
- .vrxa6G_markdown li:not(:first-child) {
261
+ .BL43XG_markdown li:not(:first-child) {
262
262
  margin-top: 6px;
263
263
  }
264
264
 
265
- .vrxa6G_markdown li > :where(ul, ol) {
265
+ .BL43XG_markdown li > :where(ul, ol) {
266
266
  margin-top: 4px;
267
267
  }
268
268
 
269
- .vrxa6G_markdown li::marker {
269
+ .BL43XG_markdown li::marker {
270
270
  color: var(--dsw-alias-label-secondary);
271
271
  line-height: 24px;
272
272
  }
273
273
 
274
- .vrxa6G_markdown :where(ul, ol) ol {
274
+ .BL43XG_markdown :where(ul, ol) ol {
275
275
  padding-left: 0;
276
276
  list-style-position: inside;
277
277
  }
278
278
 
279
- .vrxa6G_markdown :where(ul, ol) ol li p {
279
+ .BL43XG_markdown :where(ul, ol) ol li p {
280
280
  display: inline;
281
281
  }
282
282
 
283
- .vrxa6G_markdown li > p {
283
+ .BL43XG_markdown li > p {
284
284
  margin: 8px 0;
285
285
  }
286
286
 
287
- .vrxa6G_markdown li > :first-child {
287
+ .BL43XG_markdown li > :first-child {
288
288
  margin-top: 0;
289
289
  }
290
290
 
291
- .vrxa6G_markdown li > :last-child:not(.md-code-block) {
291
+ .BL43XG_markdown li > :last-child:not(.md-code-block) {
292
292
  margin-bottom: 0;
293
293
  }
294
294
 
295
- .vrxa6G_markdown hr {
295
+ .BL43XG_markdown hr {
296
296
  background: var(--dsw-alias-border-l2);
297
297
  border: none;
298
298
  height: .5px;
@@ -300,19 +300,19 @@
300
300
  display: block;
301
301
  }
302
302
 
303
- .vrxa6G_markdown blockquote {
303
+ .BL43XG_markdown blockquote {
304
304
  border-left: 2px solid var(--dsw-alias-label-caption);
305
305
  margin: 16px 0 0;
306
306
  padding-left: 14px;
307
307
  }
308
308
 
309
- .vrxa6G_markdown pre {
309
+ .BL43XG_markdown pre {
310
310
  font-family: var(--ds-font-family-code);
311
311
  margin: 16px 0;
312
312
  overflow: auto;
313
313
  }
314
314
 
315
- .vrxa6G_markdown :not(pre) > code {
315
+ .BL43XG_markdown :not(pre) > code {
316
316
  box-sizing: border-box;
317
317
  font: var(--dsw-font-markdown-code);
318
318
  font-family: var(--ds-font-family-code);
@@ -325,54 +325,54 @@
325
325
  font-size: .875em !important;
326
326
  }
327
327
 
328
- .vrxa6G_markdown :where(h1, h2, h3, h4, h5, h6) code {
328
+ .BL43XG_markdown :where(h1, h2, h3, h4, h5, h6) code {
329
329
  font: inherit;
330
330
  font-family: var(--ds-font-family-code);
331
331
  }
332
332
 
333
- .vrxa6G_markdown .katex-display {
333
+ .BL43XG_markdown .katex-display {
334
334
  max-width: 100%;
335
335
  overflow: auto hidden;
336
336
  }
337
337
 
338
- .vrxa6G_markdown input[type="checkbox"] {
338
+ .BL43XG_markdown input[type="checkbox"] {
339
339
  accent-color: var(--dsw-alias-label-secondary);
340
340
  margin: 0 8px 0 0;
341
341
  }
342
342
 
343
- .vrxa6G_tableScroll {
343
+ .BL43XG_tableScroll {
344
344
  overscroll-behavior-x: contain;
345
345
  max-width: 100%;
346
346
  overflow-x: auto;
347
347
  }
348
348
 
349
- .vrxa6G_tableScroll.md-table-wide {
349
+ .BL43XG_tableScroll.md-table-wide {
350
350
  padding-bottom: var(--dsh-scrollbar-width, 8px);
351
351
  overflow-x: hidden;
352
352
  }
353
353
 
354
- .vrxa6G_tableScroll.md-table-wide:hover, .vrxa6G_tableScroll.md-table-wide:focus-visible {
354
+ .BL43XG_tableScroll.md-table-wide:hover, .BL43XG_tableScroll.md-table-wide:focus-visible {
355
355
  padding-bottom: 0;
356
356
  overflow-x: auto;
357
357
  }
358
358
 
359
- .vrxa6G_tableScroll:focus-visible {
359
+ .BL43XG_tableScroll:focus-visible {
360
360
  box-shadow: 0 0 0 2px var(--dsw-alias-state-business-primary);
361
361
  outline: none;
362
362
  }
363
363
 
364
- .vrxa6G_tableScroll table {
364
+ .BL43XG_tableScroll table {
365
365
  border-collapse: collapse;
366
366
  width: max-content;
367
367
  max-width: max-content;
368
368
  }
369
369
 
370
- .vrxa6G_tableFill table {
370
+ .BL43XG_tableFill table {
371
371
  width: 100%;
372
372
  max-width: none;
373
373
  }
374
374
 
375
- .vrxa6G_tableScroll th {
375
+ .BL43XG_tableScroll th {
376
376
  text-align: start;
377
377
  border-bottom: .5px solid var(--dsw-alias-border-l3);
378
378
  font: var(--dsw-font-markdown-table-head);
@@ -382,7 +382,7 @@
382
382
  padding: 10px 16px;
383
383
  }
384
384
 
385
- .vrxa6G_tableScroll td {
385
+ .BL43XG_tableScroll td {
386
386
  border-bottom: .5px solid var(--dsw-alias-border-l2);
387
387
  font: var(--dsw-font-markdown-table);
388
388
  min-width: 100px;
@@ -390,32 +390,32 @@
390
390
  padding: 10px 16px;
391
391
  }
392
392
 
393
- .vrxa6G_tableScroll th:first-child, .vrxa6G_tableScroll td:first-child {
393
+ .BL43XG_tableScroll th:first-child, .BL43XG_tableScroll td:first-child {
394
394
  padding-left: 0;
395
395
  }
396
396
 
397
- .vrxa6G_tableScroll td:last-child {
397
+ .BL43XG_tableScroll td:last-child {
398
398
  padding-right: 0;
399
399
  }
400
400
 
401
- .vrxa6G_tableScroll table code {
401
+ .BL43XG_tableScroll table code {
402
402
  font-size: 11px;
403
403
  }
404
404
 
405
- .vrxa6G_markdown > :first-child, .vrxa6G_markdown p:first-child {
405
+ .BL43XG_markdown > :first-child, .BL43XG_markdown p:first-child {
406
406
  margin-top: 0 !important;
407
407
  }
408
408
 
409
- .vrxa6G_markdown > :last-child, .vrxa6G_markdown p:last-child {
409
+ .BL43XG_markdown > :last-child, .BL43XG_markdown p:last-child {
410
410
  margin-bottom: 0 !important;
411
411
  }
412
412
 
413
- .vrxa6G_imageAlt {
413
+ .BL43XG_imageAlt {
414
414
  color: var(--dsw-alias-label-tertiary);
415
415
  font-style: italic;
416
416
  }
417
417
 
418
- .vrxa6G_image {
418
+ .BL43XG_image {
419
419
  background: var(--dsw-alias-bg-base);
420
420
  object-fit: contain;
421
421
  border-radius: 8px;
@@ -426,7 +426,7 @@
426
426
  display: block;
427
427
  }
428
428
 
429
- .vrxa6G_fileMention {
429
+ .BL43XG_fileMention {
430
430
  font: inherit;
431
431
  color: var(--dsw-alias-link);
432
432
  cursor: pointer;
@@ -438,7 +438,7 @@
438
438
  text-decoration: none;
439
439
  }
440
440
 
441
- .vrxa6G_fileMention:hover, .vrxa6G_fileMention:focus {
441
+ .BL43XG_fileMention:hover, .BL43XG_fileMention:focus {
442
442
  text-decoration: underline dotted var(--dsw-alias-link);
443
443
  text-underline-offset: 3px;
444
444
  outline: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/access-gate",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "dsh-plus service+ui plugin: dsh web 全量访问围栏——与官方 browser-auth 合并:官方 cookie 为唯一凭据,可选 IP 附加围栏,未认证导航渲染 token 输入页(PWA 可恢复),loopback 管理通道",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -31,20 +31,20 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@dsh-plus/shared": "0.1.10"
34
+ "@dsh-plus/shared": "0.1.11"
35
35
  },
36
36
  "peerDependencies": {
37
- "@deepseek-ai/cordis": "^4.0.1",
38
- "@deepseek-ai/dsh-home-paths": "^0.1.3-alpha.2",
39
- "@deepseek-ai/dsh-settings": "^0.1.3-alpha.2",
40
- "@deepseek-ai/schemastery": "^3.18.1"
37
+ "@deepseek-ai/cordis": "^4.0.2",
38
+ "@deepseek-ai/dsh-home-paths": "*",
39
+ "@deepseek-ai/dsh-settings": "*",
40
+ "@deepseek-ai/schemastery": "^3.18.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@deepseek-ai/cordis": "4.0.2",
44
- "@deepseek-ai/dsh-client-connection": "0.1.3-alpha.2",
45
- "@deepseek-ai/dsh-home-paths": "0.1.3-alpha.2",
46
- "@deepseek-ai/dsh-host-webserver": "0.1.3-alpha.2",
47
- "@deepseek-ai/dsh-settings": "0.1.3-alpha.2",
44
+ "@deepseek-ai/dsh-client-connection": "0.1.5-alpha.1",
45
+ "@deepseek-ai/dsh-home-paths": "0.1.5-alpha.1",
46
+ "@deepseek-ai/dsh-host-webserver": "0.1.5-alpha.1",
47
+ "@deepseek-ai/dsh-settings": "0.1.5-alpha.1",
48
48
  "@deepseek-ai/schemastery": "3.18.2",
49
49
  "@types/react": "~18.3.31",
50
50
  "react": "^18.2.0"
@@ -33,12 +33,14 @@ export interface ConfigValue {
33
33
  enabled: boolean
34
34
  allowedIps: string[]
35
35
  trustForwardedFor: boolean
36
+ autoLoginTrustedIps: boolean
36
37
  }
37
38
 
38
39
  interface Draft {
39
40
  enabled: boolean
40
41
  allowedIpsText: string
41
42
  trustForwardedFor: boolean
43
+ autoLoginTrustedIps: boolean
42
44
  }
43
45
 
44
46
  interface Diag {
@@ -50,6 +52,8 @@ interface Diag {
50
52
  ipFenceActive: boolean
51
53
  invalidEntries: string[]
52
54
  allowedCount: number
55
+ autoLoginActive: boolean
56
+ autoLoginReady: boolean
53
57
  }
54
58
 
55
59
  function draftFromValue(value: ConfigValue): Draft {
@@ -57,6 +61,7 @@ function draftFromValue(value: ConfigValue): Draft {
57
61
  enabled: value.enabled,
58
62
  allowedIpsText: value.allowedIps.join('\n'),
59
63
  trustForwardedFor: value.trustForwardedFor,
64
+ autoLoginTrustedIps: value.autoLoginTrustedIps,
60
65
  }
61
66
  }
62
67
 
@@ -68,6 +73,7 @@ function toPatch(draft: Draft): Record<string, unknown> {
68
73
  .map((s) => s.trim())
69
74
  .filter((s) => s.length > 0),
70
75
  trustForwardedFor: draft.trustForwardedFor,
76
+ autoLoginTrustedIps: draft.autoLoginTrustedIps,
71
77
  }
72
78
  }
73
79
 
@@ -75,12 +81,14 @@ function toPatch(draft: Draft): Record<string, unknown> {
75
81
  const REASON_KEYS: Record<string, string> = {
76
82
  local: 'diagLocal',
77
83
  cookie: 'diagCookie',
84
+ 'trusted-ip': 'diagTrustedIp',
78
85
  }
79
86
 
80
87
  function verdictLabel(t: (key: string) => string, diag: Diag): string {
81
88
  if (!diag.enabled) return t('diagOff')
82
89
  if (diag.verdict === 'pass') return t('diagPass')
83
90
  if (diag.verdict === 'token-page') return t('diagTokenPage')
91
+ if (diag.verdict === 'autologin') return t('diagAutoLogin')
84
92
  return t('diagBlock')
85
93
  }
86
94
 
@@ -213,6 +221,14 @@ export function AccessGateCard(props: CardProps): ReactElement | null {
213
221
  />
214
222
  <p className="dag-hint">{t('allowedIpsHint')}</p>
215
223
  </div>
224
+ <CheckRow
225
+ prefix="dag"
226
+ id="dag-autologin"
227
+ label={t('autoLoginHint')}
228
+ checked={draft.autoLoginTrustedIps}
229
+ disabled={disabled}
230
+ onEdit={(v) => edit('autoLoginTrustedIps', v)}
231
+ />
216
232
  <CheckRow
217
233
  prefix="dag"
218
234
  id="dag-xff"
@@ -244,6 +260,14 @@ export function AccessGateCard(props: CardProps): ReactElement | null {
244
260
  {diag.officialAuthed ? t('diagOfficialYes') : t('diagOfficialNo')}
245
261
  </span>
246
262
  </div>
263
+ {diag.autoLoginActive ? (
264
+ <div className="dag-diagRow">
265
+ <span className="dag-diagKey">{t('diagAutoLogin')}</span>
266
+ <span className="dag-diagVal">
267
+ {diag.autoLoginReady ? t('diagAutoLoginReady') : t('diagAutoLoginNoSecret')}
268
+ </span>
269
+ </div>
270
+ ) : null}
247
271
  {diag.invalidEntries.length > 0 ? (
248
272
  <div className="dag-diagRow">
249
273
  <span className="dag-diagKey">!</span>
@@ -18,6 +18,8 @@ const ownZh = {
18
18
  allowedIps: '附加 IP 围栏白名单',
19
19
  allowedIpsHint:
20
20
  '每行一个:精确 IP 或 CIDR(IPv4/IPv6 皆可)。非空时仅白名单来源可通行与登录;留空 = 不限制来源 IP(仅官方登录保护)。',
21
+ autoLoginHint:
22
+ 'IP 信任自动登录:白名单来源未认证时自动签发官方登录 cookie,免输启动令牌。开启即视为信任白名单内全部设备(tailnet 内),请确保白名单准确。',
21
23
  trustForwardedFor: '信任 x-forwarded-for 还原客户端 IP',
22
24
  trustForwardedForHint:
23
25
  '仅当入口代理强制覆盖该头时开启(tailscale serve 满足);直连暴露场景必须关闭。',
@@ -27,12 +29,17 @@ const ownZh = {
27
29
  diagReason: '放行原因',
28
30
  diagLocal: '本机直连',
29
31
  diagCookie: '官方 cookie',
32
+ diagTrustedIp: 'IP 信任白名单',
30
33
  diagOfficial: '官方登录状态',
31
34
  diagOfficialYes: '已登录',
32
35
  diagOfficialNo: '未登录',
33
36
  diagPass: '放行',
34
37
  diagBlock: '拦截',
35
38
  diagTokenPage: '令牌输入页',
39
+ diagAutoLogin: '自动登录',
40
+ diagAutoLoginVerdict: '自动登录(免 token 进入)',
41
+ diagAutoLoginReady: '已就绪(签名密钥可读)',
42
+ diagAutoLoginNoSecret: '不可用(签名密钥读取失败,已降级)',
36
43
  diagOff: '围栏未启用',
37
44
  diagInvalid: '白名单存在无法解析的条目:',
38
45
  }
@@ -49,6 +56,8 @@ const ownEn = {
49
56
  allowedIps: 'Additional IP fence allowlist',
50
57
  allowedIpsHint:
51
58
  'One per line: exact IP or CIDR (IPv4/IPv6). When non-empty only listed sources may pass or log in; empty = no IP restriction (official login only).',
59
+ autoLoginHint:
60
+ 'Trusted-IP auto login: mint the official login cookie automatically for unauthenticated allowlisted sources, no launch token needed. Enabling trusts every allowlisted device (inside the tailnet); keep the allowlist accurate.',
52
61
  trustForwardedFor: 'Trust x-forwarded-for for client IP',
53
62
  trustForwardedForHint:
54
63
  'Enable only when the entry proxy overwrites that header (tailscale serve does); must be off for direct exposure.',
@@ -58,12 +67,17 @@ const ownEn = {
58
67
  diagReason: 'Reason',
59
68
  diagLocal: 'loopback',
60
69
  diagCookie: 'official cookie',
70
+ diagTrustedIp: 'trusted IP allowlist',
61
71
  diagOfficial: 'Official login',
62
72
  diagOfficialYes: 'authenticated',
63
73
  diagOfficialNo: 'not authenticated',
64
74
  diagPass: 'pass',
65
75
  diagBlock: 'blocked',
66
76
  diagTokenPage: 'token page',
77
+ diagAutoLogin: 'Auto login',
78
+ diagAutoLoginVerdict: 'auto login (token-free entry)',
79
+ diagAutoLoginReady: 'ready (signing secret readable)',
80
+ diagAutoLoginNoSecret: 'unavailable (signing secret unreadable, degraded)',
67
81
  diagOff: 'gate disabled',
68
82
  diagInvalid: 'Allowlist entries that failed to parse:',
69
83
  }
package/src/config.ts CHANGED
@@ -34,6 +34,12 @@ export const Config = z.object({
34
34
  '是否信任 x-forwarded-for 还原真实客户端 IP(仅当入口代理强制覆盖该头时开启;tailscale serve 满足)',
35
35
  )
36
36
  .default(true),
37
+ autoLoginTrustedIps: z
38
+ .boolean()
39
+ .description(
40
+ 'IP 信任自动登录:来源命中 allowedIps 白名单且未认证时,服务端自动签发官方登录 cookie(免启动 token,仅对白名单来源生效);默认关闭,需显式开启',
41
+ )
42
+ .default(false),
37
43
  })
38
44
 
39
45
  export type AccessGateConfig = Schemastery.TypeT<typeof Config>
@@ -43,4 +49,5 @@ export interface GatePolicy {
43
49
  readonly enabled: boolean
44
50
  readonly allowedIps: readonly string[]
45
51
  readonly trustForwardedFor: boolean
52
+ readonly autoLoginTrustedIps: boolean
46
53
  }