fa-mcp-sdk 0.2.146 → 0.2.174

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.
Files changed (153) hide show
  1. package/README.md +1 -1
  2. package/bin/fa-mcp.js +66 -54
  3. package/cli-template/.env.example +2 -2
  4. package/cli-template/README.md +2 -2
  5. package/cli-template/fa-mcp-sdk-spec.md +122 -41
  6. package/cli-template/package.json +3 -3
  7. package/cli-template/r/TEST HTTP.xml +9 -0
  8. package/cli-template/{run/TEST SSE.run.xml → r/TEST SSE.xml } +2 -2
  9. package/cli-template/{run/TEST STDIO.run.xml → r/TEST STDIO.xml } +2 -2
  10. package/cli-template/r/generate-token.xml +14 -0
  11. package/cli-template/{run/kill-server.run.xml → r/kill-server.xml} +2 -2
  12. package/cli-template/{run/kill-token-gen-server.xml → r/remove-nul.xml} +4 -5
  13. package/{cli-template/config → config}/_local.yaml +28 -14
  14. package/{cli-template/config → config}/custom-environment-variables.yaml +3 -0
  15. package/{cli-template/config → config}/default.yaml +50 -10
  16. package/{cli-template/config → config}/development.yaml +4 -4
  17. package/config/local.yaml +81 -0
  18. package/{cli-template/config → config}/production.yaml +4 -4
  19. package/dist/core/_types_/active-directory-config.d.ts +3 -0
  20. package/dist/core/_types_/active-directory-config.d.ts.map +1 -1
  21. package/dist/core/_types_/config.d.ts +5 -1
  22. package/dist/core/_types_/config.d.ts.map +1 -1
  23. package/dist/core/_types_/types.d.ts +5 -1
  24. package/dist/core/_types_/types.d.ts.map +1 -1
  25. package/dist/core/ad/group-checker.d.ts +13 -0
  26. package/dist/core/ad/group-checker.d.ts.map +1 -0
  27. package/dist/core/ad/group-checker.js +86 -0
  28. package/dist/core/ad/group-checker.js.map +1 -0
  29. package/dist/core/auth/admin-auth.d.ts +16 -0
  30. package/dist/core/auth/admin-auth.d.ts.map +1 -0
  31. package/dist/core/auth/admin-auth.js +159 -0
  32. package/dist/core/auth/admin-auth.js.map +1 -0
  33. package/dist/core/auth/basic.d.ts +6 -0
  34. package/dist/core/auth/basic.d.ts.map +1 -0
  35. package/dist/core/auth/basic.js +26 -0
  36. package/dist/core/auth/basic.js.map +1 -0
  37. package/dist/core/auth/{jwt-validation.d.ts → jwt.d.ts} +4 -3
  38. package/dist/core/auth/jwt.d.ts.map +1 -0
  39. package/dist/core/auth/{jwt-validation.js → jwt.js} +9 -19
  40. package/dist/core/auth/jwt.js.map +1 -0
  41. package/dist/core/auth/middleware.d.ts.map +1 -1
  42. package/dist/core/auth/middleware.js +3 -3
  43. package/dist/core/auth/middleware.js.map +1 -1
  44. package/dist/core/auth/multi-auth.d.ts +14 -6
  45. package/dist/core/auth/multi-auth.d.ts.map +1 -1
  46. package/dist/core/auth/multi-auth.js +151 -141
  47. package/dist/core/auth/multi-auth.js.map +1 -1
  48. package/dist/core/auth/permanent.d.ts +6 -0
  49. package/dist/core/auth/permanent.d.ts.map +1 -0
  50. package/dist/core/auth/permanent.js +15 -0
  51. package/dist/core/auth/permanent.js.map +1 -0
  52. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts +1 -1
  53. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts.map +1 -1
  54. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js +8 -10
  55. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js.map +1 -1
  56. package/dist/core/auth/token-generator/ntlm/ntlm-integration.d.ts.map +1 -1
  57. package/dist/core/auth/token-generator/ntlm/ntlm-integration.js +9 -2
  58. package/dist/core/auth/token-generator/ntlm/ntlm-integration.js.map +1 -1
  59. package/dist/core/auth/token-generator/server.d.ts.map +1 -1
  60. package/dist/core/auth/token-generator/server.js +59 -25
  61. package/dist/core/auth/token-generator/server.js.map +1 -1
  62. package/dist/core/auth/types.d.ts +4 -3
  63. package/dist/core/auth/types.d.ts.map +1 -1
  64. package/dist/core/bootstrap/startup-info.d.ts.map +1 -1
  65. package/dist/core/bootstrap/startup-info.js +19 -0
  66. package/dist/core/bootstrap/startup-info.js.map +1 -1
  67. package/dist/core/consul/access-points-updater.js +1 -1
  68. package/dist/core/consul/access-points-updater.js.map +1 -1
  69. package/dist/core/consul/get-consul-api.d.ts +1 -1
  70. package/dist/core/consul/get-consul-api.d.ts.map +1 -1
  71. package/dist/core/consul/get-consul-api.js +1 -1
  72. package/dist/core/consul/get-consul-api.js.map +1 -1
  73. package/dist/core/consul/register.d.ts +1 -1
  74. package/dist/core/consul/register.d.ts.map +1 -1
  75. package/dist/core/index.d.ts +3 -1
  76. package/dist/core/index.d.ts.map +1 -1
  77. package/dist/core/index.js +3 -1
  78. package/dist/core/index.js.map +1 -1
  79. package/dist/core/init-mcp-server.d.ts.map +1 -1
  80. package/dist/core/init-mcp-server.js +1 -1
  81. package/dist/core/init-mcp-server.js.map +1 -1
  82. package/dist/core/utils/testing/McpSseClient.js.map +1 -1
  83. package/dist/core/web/admin-router.d.ts +10 -0
  84. package/dist/core/web/admin-router.d.ts.map +1 -0
  85. package/dist/core/web/admin-router.js +227 -0
  86. package/dist/core/web/admin-router.js.map +1 -0
  87. package/dist/core/web/favicon-svg.d.ts +1 -1
  88. package/dist/core/web/favicon-svg.d.ts.map +1 -1
  89. package/dist/core/web/favicon-svg.js +21 -3
  90. package/dist/core/web/favicon-svg.js.map +1 -1
  91. package/dist/core/web/home-api.d.ts +7 -0
  92. package/dist/core/web/home-api.d.ts.map +1 -0
  93. package/dist/core/web/home-api.js +93 -0
  94. package/dist/core/web/home-api.js.map +1 -0
  95. package/dist/core/web/server-http.d.ts +1 -0
  96. package/dist/core/web/server-http.d.ts.map +1 -1
  97. package/dist/core/web/server-http.js +60 -25
  98. package/dist/core/web/server-http.js.map +1 -1
  99. package/dist/core/web/static/home/index.html +206 -0
  100. package/dist/core/web/static/home/script.js +636 -0
  101. package/dist/core/web/{about-page/css.js → static/styles.css} +435 -105
  102. package/dist/core/web/static/token-gen/index.html +82 -0
  103. package/dist/core/web/static/token-gen/jwt-icon.svg +3 -0
  104. package/dist/core/web/static/token-gen/logout.svg +4 -0
  105. package/dist/core/web/static/token-gen/script.js +365 -0
  106. package/dist/core/web/static/token-gen/user.svg +4 -0
  107. package/dist/core/web/svg-icons.d.ts +7 -0
  108. package/dist/core/web/svg-icons.d.ts.map +1 -0
  109. package/dist/core/web/svg-icons.js +78 -0
  110. package/dist/core/web/svg-icons.js.map +1 -0
  111. package/package.json +7 -3
  112. package/scripts/copy-static.js +31 -0
  113. package/src/template/_examples/multi-auth-examples.ts +14 -47
  114. package/src/template/_types_/custom-config.ts +83 -0
  115. package/src/template/asset/logo.svg +4 -0
  116. package/src/template/start.ts +3 -3
  117. package/src/template/tools/handle-tool-call.ts +2 -1
  118. package/src/tests/mcp/test-http.js +10 -2
  119. package/src/tests/mcp/test-sse.js +10 -2
  120. package/src/tests/mcp/test-stdio.js +1 -2
  121. package/cli-template/run/TEST HTTP.run.xml +0 -5
  122. package/cli-template/run/TEST search.run.xml +0 -11
  123. package/cli-template/run/remove-nul.js.run.xml +0 -5
  124. package/dist/core/auth/jwt-validation.d.ts.map +0 -1
  125. package/dist/core/auth/jwt-validation.js.map +0 -1
  126. package/dist/core/auth/token-generator/html.d.ts +0 -9
  127. package/dist/core/auth/token-generator/html.d.ts.map +0 -1
  128. package/dist/core/auth/token-generator/html.js +0 -862
  129. package/dist/core/auth/token-generator/html.js.map +0 -1
  130. package/dist/core/web/about-page/css.d.ts +0 -2
  131. package/dist/core/web/about-page/css.d.ts.map +0 -1
  132. package/dist/core/web/about-page/css.js.map +0 -1
  133. package/dist/core/web/about-page/render.d.ts +0 -2
  134. package/dist/core/web/about-page/render.d.ts.map +0 -1
  135. package/dist/core/web/about-page/render.js +0 -773
  136. package/dist/core/web/about-page/render.js.map +0 -1
  137. /package/cli-template/{run/== START ==.run.xml → r/== START ==.xml} +0 -0
  138. /package/cli-template/{run/cb.run.xml → r/cb.xml} +0 -0
  139. /package/cli-template/{run/ci.run.xml → r/ci.xml} +0 -0
  140. /package/cli-template/{run/lint.run.xml → r/lint.xml} +0 -0
  141. /package/cli-template/{run/lint_fix.run.xml → r/lint_fix.xml} +0 -0
  142. /package/cli-template/{run/reinstall.run.xml → r/reinstall.xml} +0 -0
  143. /package/{cli-template/config → config}/test.yaml +0 -0
  144. /package/{src/template/asset/favicon.svg → dist/core/web/static/logo.svg} +0 -0
  145. /package/{cli-template/scripts → scripts}/kill-port.js +0 -0
  146. /package/{cli-template/scripts → scripts}/npm/patch_node_modules.js +0 -0
  147. /package/{cli-template/scripts → scripts}/npm/run.js +0 -0
  148. /package/{cli-template/scripts → scripts}/npm/yarn-ci.ps1 +0 -0
  149. /package/{cli-template/scripts → scripts}/npm/yarn-ci.sh +0 -0
  150. /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.ps1 +0 -0
  151. /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.sh +0 -0
  152. /package/{cli-template/scripts → scripts}/pre-commit +0 -0
  153. /package/{cli-template/scripts → scripts}/remove-nul.js +0 -0
@@ -1,862 +0,0 @@
1
- import { encodeSvgForDataUri } from '../../utils/utils.js';
2
- const jwtSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#0052cc" d="M10.2 0v6.5L12 8.9l1.8-2.4V0Zm3.6 6.5v3l3-1 3.7-5.1-2.9-2.2zm3 2-1.9 2.6 3 1 6.1-2-1.1-3.5Zm1 3.5L15 13l1.8 2.5 6.2 2 1-3.5Zm-1 3.5-3-1v3l3.8 5.3 3-2.1zm-3 2L12 15.2l-1.8 2.5V24h3.6zm-3.6 0v-3l-3 1-3.7 5.2 2.9 2.1zm-3-2 2-2.5-3-1L0 14l1.1 3.5Zm-1-3.5L9 11 7.3 8.7 1 6.6 0 10Zm1-3.4 3 1V6.4L6.4 1.2l-3 2.2Z"/></svg>';
3
- const iconEncoded = encodeSvgForDataUri(jwtSvg);
4
- export const getHTMLPage = (authStatus) => `<!DOCTYPE html>
5
- <html lang='ru'>
6
- <head>
7
- <meta charset='UTF-8'>
8
- <meta name='viewport' content='width=device-width, initial-scale=1.0'>
9
- <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,${iconEncoded}">
10
- <title>Token Generator & Validator</title>
11
-
12
- <style>
13
- * {
14
- box-sizing: border-box;
15
- margin: 0;
16
- padding: 0;
17
- }
18
-
19
- html, body {
20
- height: 100%;
21
- }
22
-
23
- body {
24
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
25
- font-size: 14px;
26
- line-height: 1.5;
27
- color: #253858;
28
- background: white;
29
- margin: 0;
30
- padding: 24px;
31
- -webkit-font-smoothing: antialiased;
32
- -moz-osx-font-smoothing: grayscale;
33
- min-height: 100vh;
34
- display: flex;
35
- align-items: flex-start;
36
- justify-content: center;
37
- }
38
-
39
- /* Simple Layout */
40
- .simple-container {
41
- width: 100%;
42
- max-width: 670px;
43
- background: white;
44
- border: 1px solid #c1c7d0;
45
- border-radius: 6px;
46
- box-shadow: 0 1px 3px rgba(9, 30, 66, 0.25), 0 0 1px rgba(9, 30, 66, 0.31);
47
- margin-top: 40px;
48
- }
49
-
50
- /* Simple Header */
51
- .simple-header {
52
- padding: 16px 24px 12px;
53
- border-bottom: 1px solid #c1c7d0;
54
- background: #fafbfc;
55
- border-radius: 6px 6px 0 0;
56
- display: flex;
57
- align-items: center;
58
- gap: 16px;
59
- }
60
-
61
- .simple-header h1 {
62
- font-size: 24px;
63
- font-weight: 600;
64
- margin: 0;
65
- color: #0052cc;
66
- }
67
-
68
- /* Simple Main Content */
69
- .simple-main {
70
- padding: 24px 24px;
71
- }
72
-
73
- .tab-container {
74
- margin-bottom: 0;
75
- }
76
-
77
- .tabs {
78
- display: flex;
79
- border-bottom: 1px solid #c1c7d0;
80
- margin-bottom: 24px;
81
- }
82
-
83
- .tab {
84
- background: none;
85
- border: none;
86
- padding: 12px 16px;
87
- cursor: pointer;
88
- font-size: 14px;
89
- font-weight: 500;
90
- color: #505f79;
91
- border-bottom: 2px solid transparent;
92
- transition: all 0.2s ease;
93
- }
94
-
95
- .tab.active {
96
- color: #0052cc;
97
- border-bottom-color: #0052cc;
98
- }
99
-
100
- .tab:hover {
101
- color: #253858;
102
- background: #fafbfc;
103
- }
104
-
105
- .tab-content {
106
- display: none;
107
- }
108
-
109
- .tab-content.active {
110
- display: block;
111
- }
112
-
113
- .form-group {
114
- margin-bottom: 12px;
115
- }
116
-
117
- .form-row {
118
- display: flex;
119
- gap: 12px;
120
- align-items: center;
121
- }
122
-
123
- label {
124
- display: block;
125
- margin-bottom: 4px;
126
- font-weight: 500;
127
- color: #42526e;
128
- font-size: 14px;
129
- }
130
-
131
- input, select, textarea {
132
- width: 100%;
133
- padding: 4px 6px;
134
- border: 1px solid #c1c7d0;
135
- border-radius: 3px;
136
- font-size: 14px;
137
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
138
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
139
- background: white;
140
- }
141
-
142
- select {
143
- padding: 3px 6px;
144
- }
145
-
146
- input:focus, select:focus, textarea:focus {
147
- outline: none;
148
- border-color: #0065ff;
149
- box-shadow: 0 0 0 2px rgba(0, 101, 255, 0.1);
150
- }
151
-
152
- input::placeholder, textarea::placeholder {
153
- color: #505f79;
154
- }
155
-
156
- .time-input {
157
- flex: 1;
158
- }
159
-
160
- .time-unit {
161
- flex: 0 0 120px;
162
- }
163
-
164
- .key-value-pair {
165
- display: flex;
166
- gap: 8px;
167
- margin-bottom: 12px;
168
- align-items: center;
169
- }
170
-
171
- .key-value-pair input {
172
- margin-bottom: 0;
173
- }
174
-
175
- .key-value-pair input[name="keys"] {
176
- width: 180px;
177
- flex-shrink: 0;
178
- }
179
-
180
- .key-value-pair input[name="values"] {
181
- flex: 1;
182
- }
183
-
184
- .remove-btn {
185
- background: white;
186
- color: #bf2600;
187
- border: 0px;
188
- width: 28px;
189
- height: 28px;
190
- cursor: pointer;
191
- font-size: 22px;
192
- display: flex;
193
- align-items: center;
194
- justify-content: center;
195
- flex-shrink: 0;
196
- }
197
-
198
- .remove-btn:hover {
199
- background: #bf2600;
200
- color: white;
201
- border-color: #bf2600;
202
- }
203
-
204
- .add-btn {
205
- background: #d7ffd4;
206
- color: #089300;
207
- border: none;
208
- border-radius: 14px;
209
- width: 28px;
210
- height: 28px;
211
- cursor: pointer;
212
- font-size: 24px;
213
- font-weight: 500;
214
- align-items: center;
215
- transition: background 0.2s ease;
216
- }
217
-
218
- .add-btn:hover {
219
- background: #c9ffc4;
220
- }
221
-
222
- .btn {
223
- background: #0052cc;
224
- color: white;
225
- border: none;
226
- padding: 12px 24px;
227
- border-radius: 3px;
228
- font-size: 14px;
229
- font-weight: 500;
230
- cursor: pointer;
231
- transition: all 0.2s ease;
232
- width: 100%;
233
- margin-bottom: 16px;
234
- }
235
-
236
- .btn:hover {
237
- background: #0065ff;
238
- box-shadow: 0 1px 1px rgba(9, 30, 66, 0.25), 0 0 1px rgba(9, 30, 66, 0.31);
239
- }
240
-
241
- .btn:active {
242
- transform: translateY(1px);
243
- box-shadow: none;
244
- }
245
-
246
-
247
- .result {
248
- margin-top: 24px;
249
- padding: 24px;
250
- border-radius: 3px;
251
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
252
- }
253
-
254
- .result.success {
255
- background: rgba(0, 102, 68, 0.1);
256
- color: #006644;
257
- border: 1px solid rgba(0, 102, 68, 0.2);
258
- }
259
-
260
- .result.error {
261
- background: rgba(191, 38, 0, 0.1);
262
- color: #bf2600;
263
- border: 1px solid rgba(255, 86, 48, 0.2);
264
- }
265
-
266
- .token-output {
267
- background: #ebecf0;
268
- border: 1px solid #c1c7d0;
269
- border-radius: 3px;
270
- padding: 16px;
271
- font-family: ui-monospace, 'SF Mono', 'Consolas', 'Roboto Mono', 'Ubuntu Mono', monospace;
272
- font-size: 12px;
273
- line-height: 1.4;
274
- word-break: break-all;
275
- min-height: 100px;
276
- resize: vertical;
277
- color: #172b4d;
278
- position: relative;
279
- }
280
-
281
- .copy-button, .validate-token-button {
282
- position: absolute;
283
- bottom: 5px;
284
- right: 35px;
285
- background: #ffffff73;
286
- border-radius: 4px;
287
- width: 28px;
288
- height: 28px;
289
- cursor: pointer;
290
- font-size: 12px;
291
- display: flex;
292
- align-items: center;
293
- justify-content: center;
294
- border: none;
295
- transition: all 0.2s ease;
296
- z-index: 10;
297
- }
298
-
299
- .validate-token-button {
300
- right: 5px;
301
- }
302
-
303
- .copy-button:hover, .validate-token-button:hover {
304
- background: #ffffff;
305
- transform: scale(1.05);
306
- }
307
-
308
- .copy-notification {
309
- position: absolute;
310
- top: 8px;
311
- right: 42px;
312
- background: #006644;
313
- color: white;
314
- padding: 4px 8px;
315
- border-radius: 4px;
316
- font-size: 12px;
317
- font-weight: 500;
318
- opacity: 0;
319
- transform: translateY(-10px);
320
- transition: all 0.3s ease;
321
- z-index: 11;
322
- white-space: nowrap;
323
- }
324
-
325
- .copy-notification.show {
326
- opacity: 1;
327
- transform: translateY(0);
328
- }
329
-
330
- .token-info {
331
- background: rgba(0, 102, 68, 0.05);
332
- border: 1px solid rgba(0, 102, 68, 0.1);
333
- border-radius: 3px;
334
- padding: 16px;
335
- margin-top: 12px;
336
- }
337
-
338
- .token-info h4 {
339
- margin-bottom: 8px;
340
- color: #006644;
341
- font-weight: 600;
342
- font-size: 14px;
343
- }
344
-
345
- .token-info p {
346
- margin: 4px 0;
347
- font-family: ui-monospace, 'SF Mono', 'Consolas', 'Roboto Mono', 'Ubuntu Mono', monospace;
348
- font-size: 14px;
349
- color: #172b4d;
350
- }
351
- .service-icon {
352
- width: 26px;
353
- height: 26px;
354
- display: flex;
355
- align-items: center;
356
- justify-content: center;
357
- }
358
- .service-icon svg {
359
- width: 100%;
360
- height: 100%;
361
- }
362
-
363
- /* Authentication status */
364
- .auth-status {
365
- padding: 12px 16px;
366
- margin-bottom: 16px;
367
- border-radius: 4px;
368
- display: flex;
369
- justify-content: space-between;
370
- align-items: center;
371
- font-size: 14px;
372
- }
373
-
374
- .auth-status.auth-enabled {
375
- background: rgba(0, 102, 68, 0.1);
376
- border: 1px solid rgba(0, 102, 68, 0.2);
377
- color: #006644;
378
- }
379
-
380
- .auth-status.auth-disabled {
381
- background: rgba(245, 158, 11, 0.1);
382
- border: 1px solid rgba(245, 158, 11, 0.2);
383
- color: #d97706;
384
- }
385
-
386
- .auth-info {
387
- display: flex;
388
- align-items: center;
389
- gap: 8px;
390
- }
391
-
392
- .auth-indicator {
393
- display: inline-block;
394
- width: 8px;
395
- height: 8px;
396
- border-radius: 50%;
397
- margin-right: 4px;
398
- }
399
-
400
- .auth-indicator.enabled {
401
- background: #006644;
402
- }
403
-
404
- .auth-indicator.disabled {
405
- background: #d97706;
406
- }
407
-
408
- .logout-btn {
409
- background: #dc2626;
410
- color: white;
411
- border: none;
412
- padding: 6px 12px;
413
- border-radius: 3px;
414
- font-size: 12px;
415
- cursor: pointer;
416
- transition: background 0.2s ease;
417
- }
418
-
419
- .logout-btn:hover {
420
- background: #b91c1c;
421
- }
422
- /* Responsive Design */
423
- @media (max-width: 640px) {
424
- body {
425
- padding: 16px;
426
- }
427
-
428
- .simple-container {
429
- margin-top: 24px;
430
- max-width: 100%;
431
- }
432
-
433
- .simple-header {
434
- padding: 16px 20px 12px;
435
- }
436
-
437
- .simple-header h1 {
438
- font-size: 20px;
439
- }
440
-
441
- .simple-main {
442
- padding: 20px 20px;
443
- }
444
-
445
- .form-row {
446
- flex-direction: column;
447
- gap: 8px;
448
- }
449
-
450
- .key-value-pair {
451
- flex-direction: column;
452
- align-items: stretch;
453
- gap: 8px;
454
- }
455
-
456
- .key-value-pair input[name="keys"] {
457
- width: 100%;
458
- }
459
-
460
- .remove-btn {
461
- width: 100%;
462
- margin-top: 8px;
463
- }
464
- }
465
- </style>
466
- </head>
467
- <body>
468
- <div class="simple-container">
469
- <!-- Header -->
470
- <header class="simple-header">
471
- <div class="service-icon">${jwtSvg}</div>
472
- <h1>Token Generator & Validator</h1>
473
- </header>
474
-
475
- <!-- Authentication Status -->
476
- <main class="simple-main">
477
- ${authStatus ? `
478
- <div class="auth-status ${authStatus.ntlmEnabled ? 'auth-enabled' : 'auth-disabled'}">
479
- <div class="auth-info">
480
- <span>
481
- <span class="auth-indicator ${authStatus.ntlmEnabled ? 'enabled' : 'disabled'}"></span>
482
- NTLM Authentication: ${authStatus.ntlmEnabled ? 'Enabled' : 'Disabled'}
483
- ${authStatus.ntlmEnabled && authStatus.isAuthenticated ? `- Logged in as ${authStatus.domain}\\${authStatus.username}` : ''}
484
- </span>
485
- </div>
486
- ${authStatus.ntlmEnabled && authStatus.isAuthenticated ? `
487
- <button class="logout-btn" onclick="logout()">Log out</button>
488
- ` : ''}
489
- </div>
490
- ` : ''}
491
-
492
- <div class="tab-container">
493
- <div class="tabs">
494
- <button class="tab active" onclick="switchTab('generate')">Token generation</button>
495
- <button class="tab" onclick="switchTab('validate')">Token validation</button>
496
- </div>
497
-
498
- <!-- Token generation -->
499
- <div id="generate" class="tab-content active">
500
- <form id="generateForm">
501
- <div class="form-group">
502
- <div class="form-row" style="gap: 20px;">
503
- <div style="flex: 1;">
504
- <label for="tokenUser">Who is the token issued to:</label>
505
- <input type="text" id="tokenUser" name="user" required>
506
- </div>
507
- <div style="flex: 1;">
508
- <label>For how long:</label>
509
- <div class="form-row">
510
- <input type="number" id="timeValue" name="timeValue" class="time-input" min="1" required>
511
- <select id="timeUnit" name="timeUnit" class="time-unit">
512
- <option value="minutes">minutes</option>
513
- <option value="hours">hours</option>
514
- <option value="days" selected>days</option>
515
- <option value="months">months</option>
516
- <option value="years">years</option>
517
- </select>
518
- </div>
519
- </div>
520
- </div>
521
- </div>
522
- <div class="form-group">
523
- <label>Additional data (key-value):</label>
524
- <div id="keyValuePairs"></div>
525
- <button type="button" class="add-btn" onclick="addKeyValuePair()">+</button>
526
- </div>
527
- <button type="submit" class="btn">Generate a token</button>
528
- </form>
529
- <div id="generateResult"></div>
530
- </div>
531
-
532
- <!-- Token validation -->
533
- <div id="validate" class="tab-content">
534
- <form id="validateForm">
535
- <div class="form-group">
536
- <label for="tokenInput">Enter the token for verification:</label>
537
- <textarea id="tokenInput" name="token" rows="4" required></textarea>
538
- </div>
539
- <button type="submit" class="btn">Check Token</button>
540
- </form>
541
- <div id="validateResult"></div>
542
- </div>
543
- </div>
544
- </main>
545
- </div>
546
-
547
- <script>
548
- let keyValuePairCount = 0;
549
-
550
- function switchTab (tabName) {
551
- document.querySelectorAll('.tab-content').forEach(content => {
552
- content.classList.remove('active');
553
- });
554
- document.querySelectorAll('.tab').forEach(tab => {
555
- tab.classList.remove('active');
556
- });
557
- document.getElementById(tabName).classList.add('active');
558
-
559
- // Activate the corresponding tab button
560
- const tabs = document.querySelectorAll('.tab');
561
- tabs.forEach(tab => {
562
- const onclick = tab.getAttribute('onclick');
563
- if (onclick && onclick.includes('switchTab(\'' + tabName + '\')')) {
564
- tab.classList.add('active');
565
- }
566
- });
567
- }
568
-
569
- function addKeyValuePair (key = '', value = '', readonly = false, placeholder = 'Value') {
570
- if (keyValuePairCount >= 15) {
571
- alert('Maximum of 15 key-value pairs');
572
- return;
573
- }
574
- const container = document.getElementById('keyValuePairs');
575
- const pairDiv = document.createElement('div');
576
- pairDiv.className = 'key-value-pair';
577
-
578
- const keyInput = readonly ?
579
- '<input type="text" placeholder="Key" name="keys" value="' + key + '" readonly style="background-color: #f8f9fa;">' :
580
- '<input type="text" placeholder="Key" name="keys" value="' + key + '">';
581
-
582
- const valueInput = '<input type="text" placeholder="' + placeholder + '" name="values" value="' + value + '">';
583
-
584
- pairDiv.innerHTML = keyInput + valueInput +
585
- '<button type="button" class="remove-btn" onclick="removeKeyValuePair(this)">×</button>';
586
- container.appendChild(pairDiv);
587
- keyValuePairCount++;
588
- }
589
-
590
- function removeKeyValuePair (button) {
591
- button.parentElement.remove();
592
- keyValuePairCount--;
593
- }
594
-
595
- function addCopyButtonToTokenOutput(tokenOutput, token) {
596
- if (!tokenOutput || tokenOutput.hasAttribute('data-copy-added')) {
597
- return;
598
- }
599
-
600
- tokenOutput.setAttribute('data-copy-added', 'true');
601
-
602
- const copyButton = document.createElement('button');
603
- copyButton.className = 'copy-button';
604
- copyButton.innerHTML = '📋';
605
- copyButton.title = 'Copy to clipboard';
606
- copyButton.setAttribute('aria-label', 'Copy to clipboard');
607
-
608
- const validateButton = document.createElement('button');
609
- validateButton.className = 'validate-token-button';
610
- validateButton.innerHTML = '✓';
611
- validateButton.title = 'Validate token';
612
- validateButton.setAttribute('aria-label', 'Validate token');
613
-
614
- const notification = document.createElement('div');
615
- notification.className = 'copy-notification';
616
- notification.textContent = 'Copied';
617
-
618
- tokenOutput.appendChild(copyButton);
619
- tokenOutput.appendChild(validateButton);
620
- tokenOutput.appendChild(notification);
621
-
622
- copyButton.addEventListener('click', async function() {
623
- try {
624
- await navigator.clipboard.writeText(token);
625
-
626
- // Show notification
627
- notification.classList.add('show');
628
-
629
- // Hide notification after 1 second
630
- setTimeout(() => {
631
- notification.classList.remove('show');
632
- }, 1000);
633
-
634
- } catch (err) {
635
- // Fallback for browsers that don't support clipboard API
636
- const textArea = document.createElement('textarea');
637
- textArea.value = token;
638
- textArea.style.position = 'fixed';
639
- textArea.style.opacity = '0';
640
- document.body.appendChild(textArea);
641
- textArea.focus();
642
- textArea.select();
643
-
644
- try {
645
- document.execCommand('copy');
646
-
647
- // Show notification
648
- notification.classList.add('show');
649
-
650
- // Hide notification after 1 second
651
- setTimeout(() => {
652
- notification.classList.remove('show');
653
- }, 1000);
654
- } catch (fallbackErr) {
655
- console.error('Failed to copy text:', fallbackErr);
656
- }
657
-
658
- document.body.removeChild(textArea);
659
- }
660
- });
661
-
662
- validateButton.addEventListener('click', function() {
663
- // Switch to validation tab
664
- switchTab('validate');
665
-
666
- // Set the token in the validation textarea
667
- const tokenTextarea = document.getElementById('tokenInput');
668
- if (tokenTextarea) {
669
- tokenTextarea.value = token;
670
-
671
- // Trigger validation form submit
672
- const validateForm = document.getElementById('validateForm');
673
- if (validateForm) {
674
- validateForm.dispatchEvent(new Event('submit'));
675
- }
676
- }
677
- });
678
- }
679
-
680
- function formatTime (ms) {
681
- const seconds = Math.floor(ms / 1000);
682
- const minutes = Math.floor(seconds / 60);
683
- const hours = Math.floor(minutes / 60);
684
- const days = Math.floor(hours / 24);
685
-
686
- if (days > 0) return days + ' d. ' + (hours % 24) + ' h.';
687
- if (hours > 0) return hours + ' h. ' + (minutes % 60) + ' min.';
688
- if (minutes > 0) return minutes + ' min.';
689
- return seconds + ' s.';
690
- }
691
-
692
- // Processing the Generation Form
693
- document.getElementById('generateForm').addEventListener('submit', async (e) => {
694
- e.preventDefault();
695
-
696
- const formData = new FormData(e.target);
697
- const keys = formData.getAll('keys').filter(k => k.trim());
698
- const values = formData.getAll('values').filter(v => v.trim());
699
-
700
- const payload = {};
701
- for (let i = 0; i < keys.length; i++) {
702
- if (keys[i] && values[i]) {
703
- payload[keys[i]] = values[i];
704
- }
705
- }
706
-
707
- const requestData = {
708
- user: formData.get('user'),
709
- timeValue: parseInt(formData.get('timeValue')),
710
- timeUnit: formData.get('timeUnit'),
711
- payload: payload,
712
- };
713
-
714
- try {
715
- const response = await fetch('/api/generate-token', {
716
- method: 'POST',
717
- headers: { 'Content-Type': 'application/json' },
718
- body: JSON.stringify(requestData),
719
- });
720
-
721
- const result = await response.json();
722
- const resultDiv = document.getElementById('generateResult');
723
-
724
- if (result.success) {
725
- resultDiv.innerHTML =
726
- '<div class="result success">' +
727
- '<strong>The token has been successfully created!</strong><br>' +
728
- '<div class="token-output">' + result.token + '</div>' +
729
- '</div>';
730
-
731
- // Add floating copy button to the token output
732
- const tokenOutput = resultDiv.querySelector('.token-output');
733
- if (tokenOutput) {
734
- addCopyButtonToTokenOutput(tokenOutput, result.token);
735
- }
736
-
737
- // Automatically populate the validation field with the generated token
738
- const tokenTextarea = document.getElementById('tokenInput');
739
- if (tokenTextarea) {
740
- tokenTextarea.value = result.token;
741
- }
742
- } else {
743
- resultDiv.innerHTML =
744
- '<div class="result error">' +
745
- '<strong>Error:</strong> ' + result.error +
746
- '</div>';
747
- }
748
- } catch (error) {
749
- document.getElementById('generateResult').innerHTML =
750
- '<div class="result error">' +
751
- '<strong>Error:</strong> ' + error.message +
752
- '</div>';
753
- }
754
- });
755
-
756
- // Processing the Verification Form
757
- document.getElementById('validateForm').addEventListener('submit', async (e) => {
758
- e.preventDefault();
759
-
760
- const formData = new FormData(e.target);
761
- const token = formData.get('token').trim();
762
-
763
- try {
764
- const response = await fetch('/api/validate-token', {
765
- method: 'POST',
766
- headers: { 'Content-Type': 'application/json' },
767
- body: JSON.stringify({ token }),
768
- });
769
-
770
- const result = await response.json();
771
- const resultDiv = document.getElementById('validateResult');
772
-
773
- if (result.success) {
774
- const remainingTime = result.payload.expire - Date.now();
775
- const payloadKeys = Object.keys(result.payload).filter((k) => !/^(user|expire|iat|service)$/.test(k));
776
-
777
- let payloadHtml = '';
778
- if (payloadKeys.length > 0) {
779
- payloadHtml = '<h4>Additional data:</h4>';
780
- payloadKeys.forEach(key => {
781
- payloadHtml += '<p><strong>' + key + ':</strong> ' + result.payload[key] + '</p>';
782
- });
783
- }
784
-
785
- // Format issued at time
786
- const issuedAtTime = result.payload.iat ? new Date(result.payload.iat).toLocaleString('ru-RU') : 'N/A';
787
-
788
- resultDiv.innerHTML =
789
- '<div class="result success">' +
790
- '<strong>The token is valid!</strong>' +
791
- '<div class="token-info">' +
792
- '<h4>Token Information:</h4>' +
793
- '<p><strong>User:</strong> ' + result.payload.user + '</p>' +
794
- ( result.payload.service ? '<p><strong>Service:</strong> ' + result.payload.service + '</p>' : '') +
795
- '<p><strong>Issued at:</strong> ' + issuedAtTime + '</p>' +
796
- '<p><strong>Time remaining:</strong> ' + formatTime(remainingTime) + '</p>' +
797
- '<p><strong>Expires:</strong> ' + new Date(result.payload.expire).toLocaleString('ru-RU') + '</p>' +
798
- payloadHtml +
799
- '</div>' +
800
- '</div>';
801
- } else {
802
- resultDiv.innerHTML =
803
- '<div class="result error">' +
804
- '<strong>Token invalid!</strong><br>' +
805
- 'Reason: ' + result.error +
806
- '</div>';
807
- }
808
- } catch (error) {
809
- document.getElementById('validateResult').innerHTML =
810
- '<div class="result error">' +
811
- '<strong>Error:</strong> ' + error.message +
812
- '</div>';
813
- }
814
- });
815
-
816
- // Function to initialize the form
817
- async function initializeForm () {
818
- try {
819
- // Getting information about the service
820
- const response = await fetch('/api/service-info');
821
- const data = await response.json();
822
- const serviceName = data.serviceName;
823
-
824
- // Adding a pre-filled pair serviceName
825
- addKeyValuePair('service', serviceName, true);
826
- addKeyValuePair('issue', '', true, 'URL of request for the issuance of a token in JIRA');
827
-
828
- } catch (error) {
829
- console.error('Error loading service info:', error);
830
- }
831
- // Add one empty pair for the user
832
- addKeyValuePair();
833
- }
834
-
835
- // Logout function
836
- async function logout() {
837
- try {
838
- const response = await fetch('/logout', {
839
- method: 'GET',
840
- credentials: 'include',
841
- });
842
-
843
- if (response.status === 401) {
844
- // Authentication cleared, reload page to trigger browser auth prompt
845
- window.location.reload();
846
- } else {
847
- console.error('Logout failed');
848
- alert('Logout failed. Please clear your browser cache and reload the page.');
849
- }
850
- } catch (error) {
851
- console.error('Error during logout:', error);
852
- alert('Error during logout. Please clear your browser cache and reload the page.');
853
- }
854
- }
855
-
856
- // Initialization on page load
857
- initializeForm();
858
- </script>
859
- </body>
860
- </html>
861
- `;
862
- //# sourceMappingURL=html.js.map