@opengis/fastify-table 2.4.0 → 2.4.2

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 (86) hide show
  1. package/README.md +86 -86
  2. package/dist/functions.js +20 -20
  3. package/dist/module/core/cls/constraint_action.json +9 -9
  4. package/dist/module/core/cls/constraint_matchtype.json +5 -5
  5. package/dist/module/core/cls/constraint_type_full.json +17 -17
  6. package/dist/module/core/cls/core.user_type.json +13 -13
  7. package/dist/module/core/pt/schemaItem.pt.hbs +17 -17
  8. package/dist/script/adduser +14 -14
  9. package/dist/script/dump.js +48 -48
  10. package/dist/script/dump.ts +216 -216
  11. package/dist/script/migrate.ts +41 -41
  12. package/dist/server/helpers/core/badge.js +1 -1
  13. package/dist/server/helpers/list/descriptionList.js +8 -8
  14. package/dist/server/helpers/list/tableList.js +4 -4
  15. package/dist/server/helpers/list/utils/button.js +1 -1
  16. package/dist/server/helpers/list/utils/buttonDel.js +3 -3
  17. package/dist/server/helpers/list/utils/buttonEdit.js +3 -3
  18. package/dist/server/helpers/utils/button.js +1 -1
  19. package/dist/server/helpers/utils/buttonAdd.js +15 -15
  20. package/dist/server/helpers/utils/buttonDel.js +11 -11
  21. package/dist/server/helpers/utils/buttonEdit.js +3 -3
  22. package/dist/server/migrations/0.sql +99 -99
  23. package/dist/server/migrations/cls.sql +105 -105
  24. package/dist/server/migrations/context.sql +136 -136
  25. package/dist/server/migrations/oauth.sql +79 -79
  26. package/dist/server/migrations/properties.sql +115 -115
  27. package/dist/server/migrations/roles.sql +195 -195
  28. package/dist/server/migrations/template.sql +43 -43
  29. package/dist/server/migrations/users.sql +151 -151
  30. package/dist/server/plugins/access/funcs/getUserPermissions.js +7 -7
  31. package/dist/server/plugins/auth/funcs/authorizeUser.js +4 -4
  32. package/dist/server/plugins/auth/funcs/getQuery.js +20 -20
  33. package/dist/server/plugins/crud/funcs/dataUpdate.js +7 -7
  34. package/dist/server/plugins/crud/funcs/getAccess.js +14 -14
  35. package/dist/server/plugins/crud/funcs/utils/getInsertQuery.js +6 -6
  36. package/dist/server/plugins/crud/funcs/utils/logChanges.js +18 -18
  37. package/dist/server/plugins/grpc/utils/convertp.proto +136 -136
  38. package/dist/server/plugins/grpc/utils/htmlTemplate.js +10 -10
  39. package/dist/server/plugins/grpc/utils/office2pdf.proto +13 -13
  40. package/dist/server/plugins/logger/errorMessage.d.ts.map +1 -1
  41. package/dist/server/plugins/logger/errorMessage.js +3 -0
  42. package/dist/server/plugins/metric/loggerSystem.js +1 -1
  43. package/dist/server/plugins/pg/funcs/autoIndex.js +5 -5
  44. package/dist/server/plugins/pg/funcs/getMeta.js +10 -10
  45. package/dist/server/plugins/pg/funcs/init.js +36 -36
  46. package/dist/server/plugins/sqlite/funcs/init.js +22 -22
  47. package/dist/server/plugins/table/funcs/getFilterSQL/util/getCustomQuery.js +1 -1
  48. package/dist/server/plugins/table/funcs/getSelect.js +1 -1
  49. package/dist/server/plugins/table/funcs/gisIRColumn.js +3 -3
  50. package/dist/server/plugins/usercls/index.js +2 -2
  51. package/dist/server/routes/access/controllers/access.group.js +6 -6
  52. package/dist/server/routes/access/controllers/access.group.post.js +5 -5
  53. package/dist/server/routes/access/controllers/access.interface.js +14 -14
  54. package/dist/server/routes/access/controllers/access.user.js +6 -6
  55. package/dist/server/routes/auth/controllers/2factor/providers/totp.js +5 -5
  56. package/dist/server/routes/auth/controllers/2factor/qrcode.js +1 -1
  57. package/dist/server/routes/auth/controllers/2factor/recovery.js +1 -1
  58. package/dist/server/routes/auth/controllers/2factor/verify.js +1 -1
  59. package/dist/server/routes/auth/controllers/core/getUserInfo.js +33 -33
  60. package/dist/server/routes/auth/controllers/core/passwordRecovery.js +1 -1
  61. package/dist/server/routes/auth/controllers/core/registration.js +2 -2
  62. package/dist/server/routes/auth/controllers/page/login2faTemplate.js +1 -1
  63. package/dist/server/routes/cron/controllers/cronApi.d.ts.map +1 -1
  64. package/dist/server/routes/cron/controllers/cronApi.js +9 -4
  65. package/dist/server/routes/file/controllers/resizeAll.js +6 -6
  66. package/dist/server/routes/grpc/controllers/file2geojson.js +13 -13
  67. package/dist/server/routes/menu/controllers/getMenu.js +9 -9
  68. package/dist/server/routes/notifications/controllers/readNotifications.js +4 -4
  69. package/dist/server/routes/notifications/controllers/userNotifications.js +3 -3
  70. package/dist/server/routes/table/controllers/card.js +1 -1
  71. package/dist/server/routes/table/controllers/filter.js +6 -6
  72. package/dist/server/routes/table/controllers/form.js +1 -1
  73. package/dist/server/routes/table/controllers/getFormByTable.js +6 -6
  74. package/dist/server/routes/table/controllers/suggest.js +3 -3
  75. package/dist/server/routes/table/controllers/tableData.js +2 -2
  76. package/dist/server/routes/table/controllers/tableInfo.js +10 -10
  77. package/dist/server/routes/table/functions/getData.js +13 -13
  78. package/dist/server/routes/widget/controllers/widget.get.js +33 -33
  79. package/dist/server/routes/widget/controllers/widget.set.js +3 -3
  80. package/dist/server/templates/page/2factor-recovery.html +101 -101
  81. package/dist/server/templates/page/2factor.html +140 -140
  82. package/dist/server/templates/page/login.html +90 -90
  83. package/dist/server/templates/page/loginEuSign.html +123 -123
  84. package/dist/server/templates/pt/recovery-codes-email-template.hbs +12 -12
  85. package/dist/server/templates/pt/recovery-password-email-template.html +20 -20
  86. package/package.json +98 -98
@@ -39,49 +39,49 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
39
39
  }
40
40
  const sqls = {
41
41
  comment: pg.pk["admin.users"]
42
- ? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
43
- ${username} as username, u.login, u.avatar
44
- from crm.communications c
45
- left join admin.users u on u.uid=c.uid
42
+ ? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
43
+ ${username} as username, u.login, u.avatar
44
+ from crm.communications c
45
+ left join admin.users u on u.uid=c.uid
46
46
  where entity_id=$1 order by cdate desc`
47
47
  : "select communication_id, entity_id, body, subject, cdate, uid from crm.communications where entity_id=$1 order by cdate desc",
48
- history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
49
- ${username} as username, u.login, u.avatar
50
- FROM log.table_changes a
51
- left join admin.users u on a.change_user_id = u.uid
52
- left join lateral(
53
- select json_agg(row_to_json(q)) from (
54
- select change_data_id, entity_key, value_new, value_old from log.table_changes_data
55
- where change_id=a.change_id
56
- )q
57
- )b on 1=1
58
- where a.change_type != 'INSERT' and b.json_agg is not null and (entity_id=$1 or entity_id in (
59
- select communication_id from crm.communications where entity_id=$1
60
- union all select checklist_id from crm.checklists where entity_id=$1
61
- union all select file_id from crm.files where entity_id=$1)
62
- )
48
+ history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
49
+ ${username} as username, u.login, u.avatar
50
+ FROM log.table_changes a
51
+ left join admin.users u on a.change_user_id = u.uid
52
+ left join lateral(
53
+ select json_agg(row_to_json(q)) from (
54
+ select change_data_id, entity_key, value_new, value_old from log.table_changes_data
55
+ where change_id=a.change_id
56
+ )q
57
+ )b on 1=1
58
+ where a.change_type != 'INSERT' and b.json_agg is not null and (entity_id=$1 or entity_id in (
59
+ select communication_id from crm.communications where entity_id=$1
60
+ union all select checklist_id from crm.checklists where entity_id=$1
61
+ union all select file_id from crm.files where entity_id=$1)
62
+ )
63
63
  order by cdate desc limit 100`,
64
64
  checklist: pg.pk["admin.users"]
65
- ? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
66
- FROM crm.checklists c
67
- left join admin.users u on u.uid=c.uid
65
+ ? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
66
+ FROM crm.checklists c
67
+ left join admin.users u on u.uid=c.uid
68
68
  where entity_id=$1 order by cdate desc`
69
69
  : "SELECT checklist_id, entity_id, subject, is_done, done_date, uid, cdate FROM crm.checklists where entity_id=$1 order by cdate desc",
70
70
  file: pg.pk["admin.users"]
71
- ? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
72
- ${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
73
- FROM crm.files c
74
- left join admin.users u on u.uid=c.uid
71
+ ? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
72
+ ${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
73
+ FROM crm.files c
74
+ left join admin.users u on u.uid=c.uid
75
75
  where entity_id=$1 and file_status<>3 order by cdate desc`
76
- : `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
76
+ : `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
77
77
  isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and file_status<>3 order by cdate desc`,
78
78
  gallery: pg.pk["admin.users"]
79
- ? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
80
- ${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
81
- FROM crm.files c
82
- left join admin.users u on u.uid=c.uid
79
+ ? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
80
+ ${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
81
+ FROM crm.files c
82
+ left join admin.users u on u.uid=c.uid
83
83
  where entity_id=$1 and file_status<>3 and ext = any($2) order by cdate desc`
84
- : `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
84
+ : `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
85
85
  isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and file_status<>3 and ext = any($2) order by cdate desc`,
86
86
  reaction: "SELECT count(*), reaction_type FROM crm.reactions c where entity_id=$1 group by reaction_type",
87
87
  };
@@ -127,7 +127,7 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
127
127
  }
128
128
  const cdateColumn = columns.find((col) => ["cdate", "created_at"].includes(col.name))?.name;
129
129
  const editorDateColumn = columns.find((col) => ["editor_date", "updated_at"].includes(col.name))?.name;
130
- const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${tableName} a
130
+ const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${tableName} a
131
131
  left join admin.users u on a.${authorIdColumn}=u.uid where a.${pk}=$1 limit 1`;
132
132
  const data = pg.pk["admin.users"] && pk && tableName
133
133
  ? await pg
@@ -68,9 +68,9 @@ export default async function widgetSet(req, reply) {
68
68
  uid: user?.uid,
69
69
  });
70
70
  if (type === "gallery") {
71
- await pg.query(`update crm.files set ismain=true
72
- where entity_id=$1
73
- and file_id=$2
71
+ await pg.query(`update crm.files set ismain=true
72
+ where entity_id=$1
73
+ and file_id=$2
74
74
  and (select count(*) = 0 from crm.files where entity_id=$1 and ismain)`, [objectid, rows[0]?.file_id]);
75
75
  }
76
76
  return {
@@ -1,102 +1,102 @@
1
- <!DOCTYPE html>
2
- <html lang="en" dir="" class="relative min-h-full">
3
- <head>
4
- <!-- scripts -->
5
- <script src="https://cdn.tailwindcss.com"></script>
6
- </head>
7
- <body class="bg-white dark:bg-neutral-900">
8
- <main class="flex min-h-full">
9
-
10
- <!-- Content -->
11
- <div class="grow px-5">
12
- <div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
13
-
14
- {{#if codes}}
15
- <div><h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Your recovery codes:</h1></div>
16
- {{/if}}
17
- {{#if recovery}}
18
- <div>
19
- <a href="/2factor/recovery" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
20
- Send recovery code via E-mail
21
- </a>
22
- <h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Reset 2factor settings via recovery code:</h1>
23
- </div>
24
- {{/if}}
25
-
26
- <form action="/2factor/recovery" method="post">
27
- <div class="space-y-5">
28
- <div>
29
- <label for="hs-pro-dale" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">
30
- Code
31
- </label>
32
-
33
- {{#if query.recovery}}
34
- <input type="text" name="code" id="hs-pro-dale" class="py-2.5 px-3 block w-full border-gray-200 rounded-lg text-sm placeholder:text-gray-400 focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-transparent dark:border-neutral-700 dark:text-neutral-300 dark:placeholder:text-white/60 dark:focus:ring-neutral-600" placeholder="999999">
35
- {{^}}
36
-
37
- <input type="text" name="code" class="form-input hidden">
38
- <div class="space-y-5">
39
- <div class="my-3 flex gap-x-2" data-hs-pin-input>
40
- <input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
41
- <input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
42
- <input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
43
- <input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
44
- <input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
45
- <input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
46
- </div>
47
- </div>
48
- {{/if}}
49
- </div>
50
-
51
- <button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
52
- Reset 2factor settings
53
- </button>
54
- </div>
55
- </form>
56
-
57
- </div>
58
- </div>
59
- <!-- End Content -->
60
- </main><script>
61
- const inputs = document.querySelectorAll(".input-code");
62
- const formInput = document.querySelector(".form-input");
63
-
64
- const inputTextToInput = (event) => {
65
- const input = event.target;
66
- const value = input.value;
67
-
68
- if (value.length > 1) {
69
- input.value = value.slice(-1);
70
- }
71
-
72
- const currentIndex = Array.from(inputs).indexOf(input);
73
- if (value && currentIndex < inputs.length - 1) {
74
- inputs[currentIndex + 1].focus();
75
- }
76
-
77
- const combinedValue = Array.from(inputs)
78
- .map((input) => input.value)
79
- .join("");
80
-
81
- formInput.value = combinedValue;
82
- };
83
-
84
- const handleBackspace = (event) => {
85
- const input = event.target;
86
-
87
- if (event.key === "Backspace" && input.value === "") {
88
- const currentIndex = Array.from(inputs).indexOf(input);
89
- if (currentIndex > 0) {
90
- inputs[currentIndex - 1].focus();
91
- }
92
- }
93
- };
94
-
95
- inputs.forEach((input) => {
96
- input.addEventListener("input", inputTextToInput);
97
- input.addEventListener("keydown", handleBackspace);
98
- });
99
- </script>
100
- </body>
101
-
1
+ <!DOCTYPE html>
2
+ <html lang="en" dir="" class="relative min-h-full">
3
+ <head>
4
+ <!-- scripts -->
5
+ <script src="https://cdn.tailwindcss.com"></script>
6
+ </head>
7
+ <body class="bg-white dark:bg-neutral-900">
8
+ <main class="flex min-h-full">
9
+
10
+ <!-- Content -->
11
+ <div class="grow px-5">
12
+ <div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
13
+
14
+ {{#if codes}}
15
+ <div><h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Your recovery codes:</h1></div>
16
+ {{/if}}
17
+ {{#if recovery}}
18
+ <div>
19
+ <a href="/2factor/recovery" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
20
+ Send recovery code via E-mail
21
+ </a>
22
+ <h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Reset 2factor settings via recovery code:</h1>
23
+ </div>
24
+ {{/if}}
25
+
26
+ <form action="/2factor/recovery" method="post">
27
+ <div class="space-y-5">
28
+ <div>
29
+ <label for="hs-pro-dale" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">
30
+ Code
31
+ </label>
32
+
33
+ {{#if query.recovery}}
34
+ <input type="text" name="code" id="hs-pro-dale" class="py-2.5 px-3 block w-full border-gray-200 rounded-lg text-sm placeholder:text-gray-400 focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-transparent dark:border-neutral-700 dark:text-neutral-300 dark:placeholder:text-white/60 dark:focus:ring-neutral-600" placeholder="999999">
35
+ {{^}}
36
+
37
+ <input type="text" name="code" class="form-input hidden">
38
+ <div class="space-y-5">
39
+ <div class="my-3 flex gap-x-2" data-hs-pin-input>
40
+ <input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
41
+ <input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
42
+ <input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
43
+ <input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
44
+ <input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
45
+ <input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
46
+ </div>
47
+ </div>
48
+ {{/if}}
49
+ </div>
50
+
51
+ <button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
52
+ Reset 2factor settings
53
+ </button>
54
+ </div>
55
+ </form>
56
+
57
+ </div>
58
+ </div>
59
+ <!-- End Content -->
60
+ </main><script>
61
+ const inputs = document.querySelectorAll(".input-code");
62
+ const formInput = document.querySelector(".form-input");
63
+
64
+ const inputTextToInput = (event) => {
65
+ const input = event.target;
66
+ const value = input.value;
67
+
68
+ if (value.length > 1) {
69
+ input.value = value.slice(-1);
70
+ }
71
+
72
+ const currentIndex = Array.from(inputs).indexOf(input);
73
+ if (value && currentIndex < inputs.length - 1) {
74
+ inputs[currentIndex + 1].focus();
75
+ }
76
+
77
+ const combinedValue = Array.from(inputs)
78
+ .map((input) => input.value)
79
+ .join("");
80
+
81
+ formInput.value = combinedValue;
82
+ };
83
+
84
+ const handleBackspace = (event) => {
85
+ const input = event.target;
86
+
87
+ if (event.key === "Backspace" && input.value === "") {
88
+ const currentIndex = Array.from(inputs).indexOf(input);
89
+ if (currentIndex > 0) {
90
+ inputs[currentIndex - 1].focus();
91
+ }
92
+ }
93
+ };
94
+
95
+ inputs.forEach((input) => {
96
+ input.addEventListener("input", inputTextToInput);
97
+ input.addEventListener("keydown", handleBackspace);
98
+ });
99
+ </script>
100
+ </body>
101
+
102
102
  </html>
@@ -1,141 +1,141 @@
1
- <!DOCTYPE html>
2
- <html lang="en" dir="" class="relative min-h-full">
3
- <head>
4
- <!-- scripts -->
5
- <script src="https://cdn.tailwindcss.com"></script>
6
- </head>
7
- <body class="bg-white dark:bg-neutral-900">
8
- <main class="flex min-h-full">
9
-
10
- <!-- Content -->
11
- <div class="grow px-5">
12
- <div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
13
- {{#if enabled}}
14
- <a href="/2factor?recovery=1" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none focus:outline-none focus:bg-gray-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700">
15
- <svg class="flex-shrink-0 size-4" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
16
- <path d="M27.2192 10.9088C27.0336 11.0528 23.7568 12.8992 23.7568 17.0048C23.7568 21.7536 27.9264 23.4336 28.0512 23.4752C28.032 23.5776 27.3888 25.776 25.8528 28.016C24.4832 29.9872 23.0528 31.9552 20.8768 31.9552C18.7008 31.9552 18.1408 30.6912 15.6288 30.6912C13.1808 30.6912 12.3104 31.9968 10.32 31.9968C8.3296 31.9968 6.9408 30.1728 5.344 27.9328C3.4944 25.3024 2 21.216 2 17.3376C2 11.1168 6.0448 7.8176 10.0256 7.8176C12.1408 7.8176 13.904 9.2064 15.232 9.2064C16.496 9.2064 18.4672 7.7344 20.8736 7.7344C21.7856 7.7344 25.0624 7.8176 27.2192 10.9088ZM19.7312 5.1008C20.7264 3.92 21.4304 2.2816 21.4304 0.6432C21.4304 0.416 21.4112 0.1856 21.3696 0C19.7504 0.0608 17.824 1.0784 16.6624 2.4256C15.7504 3.4624 14.8992 5.1008 14.8992 6.7616C14.8992 7.0112 14.9408 7.2608 14.96 7.3408C15.0624 7.36 15.2288 7.3824 15.3952 7.3824C16.848 7.3824 18.6752 6.4096 19.7312 5.1008Z" class="fill-black dark:fill-neutral-200" fill="currentColor"/>
17
- </svg>
18
- I lost access to my 2factor app
19
- </a>
20
- {{/if}}
21
- <ul>
22
- <!-- Step -->
23
- <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
24
- <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
25
- Download app
26
- </span>
27
- <a target="_blank" href="{{coalesce url 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&pcampaignid=web_share'}}" class="text-[13px] text-gray-500 dark:text-neutral-500">
28
- Download a mobile authentication app.
29
- </a>
30
- </li>
31
- <!-- End Step -->
32
-
33
- {{#if qrCode}}
34
- {{#unless enabled}}
35
- <!-- Step -->
36
- <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
37
- <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
38
- Scan QR Code
39
- </span>
40
- <p class="text-[13px] text-gray-500 dark:text-neutral-500">
41
- Scan this QR code using a mobile authentication app. This will generate a verification code.
42
- </p>
43
-
44
- <div class="mt-3">
45
- <span class="p-1 inline-block border border-gray-300 rounded-lg dark:border-neutral-600">
46
- {{{qrCode}}}
47
- </span>
48
- </div>
49
- <div class="mt-3">
50
- <span class="p-1 inline-block border border-gray-300 rounded-lg dark:border-neutral-600">
51
- {{secretKey}}
52
- </span>
53
- </div>
54
- </li>
55
- {{/unless}}
56
- <!-- End Step -->
57
- {{/if}}
58
-
59
- <!-- Step -->
60
- <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
61
- <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
62
- Enter Code
63
- </span>
64
- <p class="text-[13px] text-gray-500 dark:text-neutral-500">
65
- Enter the 2-step verification code from your authenticator app.
66
- </p>
67
-
68
- <form action="/2factor/verify" method="post">
69
- <div class="space-y-5">
70
- <div>
71
- <input type="text" name="code" class="form-input hidden">
72
- <div class="space-y-5">
73
- <div class="my-3 flex gap-x-2" data-hs-pin-input>
74
- <input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
75
- <input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
76
- <input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
77
- <input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
78
- <input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
79
- <input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
80
- </div>
81
- </div>
82
-
83
- <button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
84
- Sign in
85
- </button>
86
- </div>
87
- </form>
88
- </li>
89
- <!-- End Step -->
90
-
91
- </ul>
92
- </div>
93
- </div>
94
- <!-- End Content -->
95
- </main>
96
- <script>
97
- const inputs = document.querySelectorAll(".input-code");
98
- const formInput = document.querySelector(".form-input");
99
-
100
- const inputTextToInput = (event) => {
101
- const input = event.target;
102
- const value = input.value;
103
-
104
- if (value.length > 1) {
105
- input.value = value.slice(-1);
106
- }
107
-
108
- const currentIndex = Array.from(inputs).indexOf(input);
109
- if (value && currentIndex < inputs.length - 1) {
110
- inputs[currentIndex + 1].focus();
111
- }
112
-
113
- const combinedValue = Array.from(inputs)
114
- .map((input) => input.value)
115
- .join("");
116
-
117
- formInput.value = combinedValue;
118
- };
119
-
120
- const handleBackspace = (event) => {
121
- const input = event.target;
122
-
123
- if (event.key === "Backspace" && input.value === "") {
124
- const currentIndex = Array.from(inputs).indexOf(input);
125
- if (currentIndex > 0) {
126
- inputs[currentIndex - 1].focus();
127
- }
128
- }
129
- };
130
-
131
- inputs.forEach((input) => {
132
- input.addEventListener("input", inputTextToInput);
133
- input.addEventListener("keydown", handleBackspace);
134
- });
135
- </script>
136
-
137
-
138
-
139
- </body>
140
-
1
+ <!DOCTYPE html>
2
+ <html lang="en" dir="" class="relative min-h-full">
3
+ <head>
4
+ <!-- scripts -->
5
+ <script src="https://cdn.tailwindcss.com"></script>
6
+ </head>
7
+ <body class="bg-white dark:bg-neutral-900">
8
+ <main class="flex min-h-full">
9
+
10
+ <!-- Content -->
11
+ <div class="grow px-5">
12
+ <div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
13
+ {{#if enabled}}
14
+ <a href="/2factor?recovery=1" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none focus:outline-none focus:bg-gray-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700">
15
+ <svg class="flex-shrink-0 size-4" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
16
+ <path d="M27.2192 10.9088C27.0336 11.0528 23.7568 12.8992 23.7568 17.0048C23.7568 21.7536 27.9264 23.4336 28.0512 23.4752C28.032 23.5776 27.3888 25.776 25.8528 28.016C24.4832 29.9872 23.0528 31.9552 20.8768 31.9552C18.7008 31.9552 18.1408 30.6912 15.6288 30.6912C13.1808 30.6912 12.3104 31.9968 10.32 31.9968C8.3296 31.9968 6.9408 30.1728 5.344 27.9328C3.4944 25.3024 2 21.216 2 17.3376C2 11.1168 6.0448 7.8176 10.0256 7.8176C12.1408 7.8176 13.904 9.2064 15.232 9.2064C16.496 9.2064 18.4672 7.7344 20.8736 7.7344C21.7856 7.7344 25.0624 7.8176 27.2192 10.9088ZM19.7312 5.1008C20.7264 3.92 21.4304 2.2816 21.4304 0.6432C21.4304 0.416 21.4112 0.1856 21.3696 0C19.7504 0.0608 17.824 1.0784 16.6624 2.4256C15.7504 3.4624 14.8992 5.1008 14.8992 6.7616C14.8992 7.0112 14.9408 7.2608 14.96 7.3408C15.0624 7.36 15.2288 7.3824 15.3952 7.3824C16.848 7.3824 18.6752 6.4096 19.7312 5.1008Z" class="fill-black dark:fill-neutral-200" fill="currentColor"/>
17
+ </svg>
18
+ I lost access to my 2factor app
19
+ </a>
20
+ {{/if}}
21
+ <ul>
22
+ <!-- Step -->
23
+ <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
24
+ <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
25
+ Download app
26
+ </span>
27
+ <a target="_blank" href="{{coalesce url 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&pcampaignid=web_share'}}" class="text-[13px] text-gray-500 dark:text-neutral-500">
28
+ Download a mobile authentication app.
29
+ </a>
30
+ </li>
31
+ <!-- End Step -->
32
+
33
+ {{#if qrCode}}
34
+ {{#unless enabled}}
35
+ <!-- Step -->
36
+ <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
37
+ <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
38
+ Scan QR Code
39
+ </span>
40
+ <p class="text-[13px] text-gray-500 dark:text-neutral-500">
41
+ Scan this QR code using a mobile authentication app. This will generate a verification code.
42
+ </p>
43
+
44
+ <div class="mt-3">
45
+ <span class="p-1 inline-block border border-gray-300 rounded-lg dark:border-neutral-600">
46
+ {{{qrCode}}}
47
+ </span>
48
+ </div>
49
+ <div class="mt-3">
50
+ <span class="p-1 inline-block border border-gray-300 rounded-lg dark:border-neutral-600">
51
+ {{secretKey}}
52
+ </span>
53
+ </div>
54
+ </li>
55
+ {{/unless}}
56
+ <!-- End Step -->
57
+ {{/if}}
58
+
59
+ <!-- Step -->
60
+ <li class="relative ps-12 pb-10 last:pb-0 before:content-[counter(step)] before:absolute before:start-0 before:flex before:items-center before:justify-center before:size-7 before:text-xs before:font-semibold before:text-gray-800 before:rounded-full before:bg-gray-100 dark:before:bg-neutral-700 dark:before:text-neutral-200 last:after:hidden after:absolute after:top-9 after:bottom-2 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700" style="counter-increment: step 1;">
61
+ <span class="block font-semibold text-sm text-gray-800 dark:text-neutral-200">
62
+ Enter Code
63
+ </span>
64
+ <p class="text-[13px] text-gray-500 dark:text-neutral-500">
65
+ Enter the 2-step verification code from your authenticator app.
66
+ </p>
67
+
68
+ <form action="/2factor/verify" method="post">
69
+ <div class="space-y-5">
70
+ <div>
71
+ <input type="text" name="code" class="form-input hidden">
72
+ <div class="space-y-5">
73
+ <div class="my-3 flex gap-x-2" data-hs-pin-input>
74
+ <input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
75
+ <input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
76
+ <input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
77
+ <input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
78
+ <input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
79
+ <input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
80
+ </div>
81
+ </div>
82
+
83
+ <button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
84
+ Sign in
85
+ </button>
86
+ </div>
87
+ </form>
88
+ </li>
89
+ <!-- End Step -->
90
+
91
+ </ul>
92
+ </div>
93
+ </div>
94
+ <!-- End Content -->
95
+ </main>
96
+ <script>
97
+ const inputs = document.querySelectorAll(".input-code");
98
+ const formInput = document.querySelector(".form-input");
99
+
100
+ const inputTextToInput = (event) => {
101
+ const input = event.target;
102
+ const value = input.value;
103
+
104
+ if (value.length > 1) {
105
+ input.value = value.slice(-1);
106
+ }
107
+
108
+ const currentIndex = Array.from(inputs).indexOf(input);
109
+ if (value && currentIndex < inputs.length - 1) {
110
+ inputs[currentIndex + 1].focus();
111
+ }
112
+
113
+ const combinedValue = Array.from(inputs)
114
+ .map((input) => input.value)
115
+ .join("");
116
+
117
+ formInput.value = combinedValue;
118
+ };
119
+
120
+ const handleBackspace = (event) => {
121
+ const input = event.target;
122
+
123
+ if (event.key === "Backspace" && input.value === "") {
124
+ const currentIndex = Array.from(inputs).indexOf(input);
125
+ if (currentIndex > 0) {
126
+ inputs[currentIndex - 1].focus();
127
+ }
128
+ }
129
+ };
130
+
131
+ inputs.forEach((input) => {
132
+ input.addEventListener("input", inputTextToInput);
133
+ input.addEventListener("keydown", handleBackspace);
134
+ });
135
+ </script>
136
+
137
+
138
+
139
+ </body>
140
+
141
141
  </html>