@vitnode/core 1.2.0-canary.59 → 1.2.0-canary.61

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 (283) hide show
  1. package/dist/scripts/get-config.js +15 -0
  2. package/dist/scripts/plugin.js +188 -0
  3. package/dist/scripts/prepare-database.js +177 -0
  4. package/dist/scripts/prepare-plugins-files.js +134 -0
  5. package/dist/scripts/run-interactive-shell-command.js +21 -0
  6. package/dist/scripts/scripts.js +39 -13
  7. package/dist/scripts/shared/file-utils.js +203 -0
  8. package/dist/src/api/adapters/sso/discord.js +1 -77
  9. package/dist/src/api/adapters/sso/facebook.js +1 -76
  10. package/dist/src/api/adapters/sso/google.js +1 -87
  11. package/dist/src/api/config.js +1 -67
  12. package/dist/src/api/lib/check-plugin-id.js +1 -43
  13. package/dist/src/api/lib/cron.js +1 -22
  14. package/dist/src/api/lib/logger-middleware.js +1 -35
  15. package/dist/src/api/lib/module.js +1 -22
  16. package/dist/src/api/lib/plugin.js +1 -22
  17. package/dist/src/api/lib/route.js +1 -27
  18. package/dist/src/api/lib/with-pagination.js +1 -83
  19. package/dist/src/api/middlewares/captcha.middleware.js +1 -63
  20. package/dist/src/api/middlewares/cron-auth.middleware.js +1 -19
  21. package/dist/src/api/middlewares/global.middleware.d.ts.map +1 -1
  22. package/dist/src/api/middlewares/global.middleware.js +1 -104
  23. package/dist/src/api/middlewares/rate-limiter.middleware.js +1 -32
  24. package/dist/src/api/models/device.js +1 -61
  25. package/dist/src/api/models/email.d.ts.map +1 -1
  26. package/dist/src/api/models/email.js +1 -77
  27. package/dist/src/api/models/password.js +1 -36
  28. package/dist/src/api/models/session-admin.d.ts.map +1 -1
  29. package/dist/src/api/models/session-admin.js +1 -97
  30. package/dist/src/api/models/session.d.ts.map +1 -1
  31. package/dist/src/api/models/session.js +1 -76
  32. package/dist/src/api/models/sso.js +1 -120
  33. package/dist/src/api/models/user/get-user-by-id.js +1 -19
  34. package/dist/src/api/models/user/sign-in-with-passwords.js +1 -22
  35. package/dist/src/api/models/user/sign-up.js +1 -73
  36. package/dist/src/api/models/user.js +1 -8
  37. package/dist/src/api/modules/admin/admin.module.js +1 -19
  38. package/dist/src/api/modules/admin/advanced/advanced.admin.module.js +1 -11
  39. package/dist/src/api/modules/admin/advanced/cron/cron.admin.module.js +1 -12
  40. package/dist/src/api/modules/admin/advanced/cron/routes/get.route.js +1 -66
  41. package/dist/src/api/modules/admin/advanced/cron/routes/run.route.js +1 -95
  42. package/dist/src/api/modules/admin/debug/debug.admin.module.js +1 -10
  43. package/dist/src/api/modules/admin/debug/routes/logs.route.js +1 -94
  44. package/dist/src/api/modules/admin/routes/session.route.js +1 -47
  45. package/dist/src/api/modules/admin/users/routes/list.route.js +1 -79
  46. package/dist/src/api/modules/admin/users/routes/users.route.js +1 -84
  47. package/dist/src/api/modules/admin/users/users.admin.module.js +1 -10
  48. package/dist/src/api/modules/cron/cron/clean.cron.js +1 -24
  49. package/dist/src/api/modules/cron/cron.module.js +1 -14
  50. package/dist/src/api/modules/cron/helpers/process-cron-jobs.d.ts.map +1 -1
  51. package/dist/src/api/modules/cron/helpers/process-cron-jobs.js +1 -72
  52. package/dist/src/api/modules/cron/routes/cron.route.d.ts.map +1 -1
  53. package/dist/src/api/modules/cron/routes/cron.route.js +1 -95
  54. package/dist/src/api/modules/middleware/middleware.module.js +1 -10
  55. package/dist/src/api/modules/middleware/route.js +1 -49
  56. package/dist/src/api/modules/users/avatar-color.js +1 -43
  57. package/dist/src/api/modules/users/routes/change-password.route.js +1 -58
  58. package/dist/src/api/modules/users/routes/reset-passowrd.route.js +1 -91
  59. package/dist/src/api/modules/users/routes/session.route.js +1 -46
  60. package/dist/src/api/modules/users/routes/sign-in.route.js +1 -71
  61. package/dist/src/api/modules/users/routes/sign-out.route.js +1 -43
  62. package/dist/src/api/modules/users/routes/sign-up.route.js +1 -77
  63. package/dist/src/api/modules/users/routes/test.route.js +1 -33
  64. package/dist/src/api/modules/users/sso/routes/callback.route.js +1 -52
  65. package/dist/src/api/modules/users/sso/routes/create-url.route.js +1 -36
  66. package/dist/src/api/modules/users/sso/sso.module.js +1 -12
  67. package/dist/src/api/modules/users/users.module.js +1 -26
  68. package/dist/src/api/plugin.js +1 -15
  69. package/dist/src/app/login/page.js +1 -16
  70. package/dist/src/app/login/reset-password/page.js +1 -18
  71. package/dist/src/app/login/sso/[providerId]/page.js +1 -10
  72. package/dist/src/app/register/page.js +1 -16
  73. package/dist/src/app_admin/core/advanced/cron/page.js +1 -42
  74. package/dist/src/app_admin/core/debug/page.js +1 -45
  75. package/dist/src/app_admin/core/page.js +1 -5
  76. package/dist/src/app_admin/core/test/page.js +1 -5
  77. package/dist/src/app_admin/core/users/page.js +1 -38
  78. package/dist/src/components/avatar.js +1 -14
  79. package/dist/src/components/confirm-action/confirm-action-alert-dialog.js +1 -42
  80. package/dist/src/components/confirm-action/content.js +1 -31
  81. package/dist/src/components/date-format.js +1 -41
  82. package/dist/src/components/form/auto-form.js +1 -103
  83. package/dist/src/components/form/common/desc.js +1 -9
  84. package/dist/src/components/form/common/label.js +1 -18
  85. package/dist/src/components/form/fields/checkbox.js +1 -37
  86. package/dist/src/components/form/fields/combobox-async.js +1 -123
  87. package/dist/src/components/form/fields/combobox.js +1 -87
  88. package/dist/src/components/form/fields/input.js +1 -39
  89. package/dist/src/components/form/fields/radio-group.js +1 -50
  90. package/dist/src/components/form/fields/select.js +1 -58
  91. package/dist/src/components/form/fields/switch.js +1 -36
  92. package/dist/src/components/form/fields/textarea.js +1 -36
  93. package/dist/src/components/i18n-provider.js +1 -43
  94. package/dist/src/components/logo-vitnode.js +1 -119
  95. package/dist/src/components/switchers/langs/language-switcher.js +1 -47
  96. package/dist/src/components/switchers/themes/theme-switcher.js +1 -28
  97. package/dist/src/components/table/content.js +1 -81
  98. package/dist/src/components/table/data-table.js +1 -68
  99. package/dist/src/components/table/order-table-head.js +1 -50
  100. package/dist/src/components/table/pagination.js +1 -122
  101. package/dist/src/components/tiptap/extension.js +1 -33
  102. package/dist/src/components/tiptap/toolbar/actions/alignment-action.js +1 -91
  103. package/dist/src/components/tiptap/toolbar/actions/bold-action.js +1 -39
  104. package/dist/src/components/tiptap/toolbar/actions/headings-action.js +1 -93
  105. package/dist/src/components/tiptap/toolbar/actions/italic-action.js +1 -38
  106. package/dist/src/components/tiptap/toolbar/actions/list-action.js +1 -61
  107. package/dist/src/components/tiptap/toolbar/actions/text-format-more/text-format-more.js +1 -59
  108. package/dist/src/components/tiptap/toolbar/actions/underline-action.js +1 -38
  109. package/dist/src/components/tiptap/toolbar/actions/undo-redo-actions.js +1 -63
  110. package/dist/src/components/tiptap/toolbar/actions/utils/tooltip-shortcut.js +1 -11
  111. package/dist/src/components/tiptap/toolbar/tiptap-toolbar.js +1 -55
  112. package/dist/src/components/tiptap/toolbar/use-toolbar-editor.js +1 -5
  113. package/dist/src/components/ui/accordion.js +1 -46
  114. package/dist/src/components/ui/alert-dialog.js +1 -123
  115. package/dist/src/components/ui/alert.js +1 -39
  116. package/dist/src/components/ui/badge.js +1 -28
  117. package/dist/src/components/ui/button-client.js +1 -57
  118. package/dist/src/components/ui/button.js +1 -32
  119. package/dist/src/components/ui/card.js +1 -52
  120. package/dist/src/components/ui/checkbox.js +1 -20
  121. package/dist/src/components/ui/collapsible.js +1 -22
  122. package/dist/src/components/ui/command.js +1 -98
  123. package/dist/src/components/ui/context-menu.js +1 -140
  124. package/dist/src/components/ui/dialog.js +1 -189
  125. package/dist/src/components/ui/drawer.js +1 -83
  126. package/dist/src/components/ui/dropdown-menu.js +1 -141
  127. package/dist/src/components/ui/editor-content.js +1 -10
  128. package/dist/src/components/ui/editor.js +1 -35
  129. package/dist/src/components/ui/form.js +1 -148
  130. package/dist/src/components/ui/header-content.js +1 -30
  131. package/dist/src/components/ui/hover-card.js +1 -29
  132. package/dist/src/components/ui/input-otp.js +1 -48
  133. package/dist/src/components/ui/input.js +1 -11
  134. package/dist/src/components/ui/label.js +1 -12
  135. package/dist/src/components/ui/loader.js +1 -16
  136. package/dist/src/components/ui/menubar.js +1 -151
  137. package/dist/src/components/ui/navigation-menu.js +1 -82
  138. package/dist/src/components/ui/popover.js +1 -35
  139. package/dist/src/components/ui/progress.js +1 -19
  140. package/dist/src/components/ui/radio-group.js +1 -27
  141. package/dist/src/components/ui/scroll-area.js +1 -33
  142. package/dist/src/components/ui/select.js +1 -113
  143. package/dist/src/components/ui/separator.js +1 -14
  144. package/dist/src/components/ui/sheet.js +1 -92
  145. package/dist/src/components/ui/sidebar.js +1 -433
  146. package/dist/src/components/ui/skeleton.js +1 -10
  147. package/dist/src/components/ui/slider.js +1 -50
  148. package/dist/src/components/ui/sonner.js +1 -26
  149. package/dist/src/components/ui/switch.js +1 -16
  150. package/dist/src/components/ui/table.js +1 -63
  151. package/dist/src/components/ui/tabs.js +1 -33
  152. package/dist/src/components/ui/textarea.js +1 -10
  153. package/dist/src/components/ui/toggle-group.js +1 -45
  154. package/dist/src/components/ui/toggle.js +1 -34
  155. package/dist/src/components/ui/tooltip.js +1 -57
  156. package/dist/src/config.js +1 -4
  157. package/dist/src/database/admins.js +1 -74
  158. package/dist/src/database/cron.js +1 -22
  159. package/dist/src/database/languages.js +1 -52
  160. package/dist/src/database/logs.js +1 -34
  161. package/dist/src/database/moderators.js +1 -37
  162. package/dist/src/database/roles.js +1 -13
  163. package/dist/src/database/sessions.js +1 -60
  164. package/dist/src/database/users.js +1 -145
  165. package/dist/src/drizzle.config.js +1 -67
  166. package/dist/src/emails/default-template.js +1 -99
  167. package/dist/src/emails/reset-password.js +1 -132
  168. package/dist/src/emails/ui/button.js +1 -36
  169. package/dist/src/emails/ui/card.js +1 -39
  170. package/dist/src/hooks/use-before-unload.js +1 -30
  171. package/dist/src/hooks/use-captcha.js +1 -122
  172. package/dist/src/hooks/use-mobile.js +1 -16
  173. package/dist/src/lib/api/get-middleware-api.js +1 -13
  174. package/dist/src/lib/api/get-next-cron-run-date.js +1 -14
  175. package/dist/src/lib/api/get-next-cron-run-date.test.js +1 -55
  176. package/dist/src/lib/api/get-session-admin-api.js +1 -19
  177. package/dist/src/lib/api/get-session-api.js +1 -14
  178. package/dist/src/lib/api/should-cron-job-run.js +1 -16
  179. package/dist/src/lib/api/should-cron-job-run.test.js +1 -213
  180. package/dist/src/lib/api/validate-cron-schedule.d.ts +2 -0
  181. package/dist/src/lib/api/validate-cron-schedule.d.ts.map +1 -0
  182. package/dist/src/lib/api/validate-cron-schedule.js +1 -0
  183. package/dist/src/lib/api/validate-cron-schedule.test.d.ts +2 -0
  184. package/dist/src/lib/api/validate-cron-schedule.test.d.ts.map +1 -0
  185. package/dist/src/lib/api/validate-cron-schedule.test.js +1 -0
  186. package/dist/src/lib/colors.js +1 -140
  187. package/dist/src/lib/colors.test.js +1 -173
  188. package/dist/src/lib/config.js +1 -14
  189. package/dist/src/lib/ctrl-or-command-character.js +1 -5
  190. package/dist/src/lib/fetcher/cookie-from-string-to-object.js +1 -12
  191. package/dist/src/lib/fetcher/cookie-from-string-to-object.test.js +1 -78
  192. package/dist/src/lib/fetcher/core.js +3 -48
  193. package/dist/src/lib/fetcher/helpers-server.js +1 -18
  194. package/dist/src/lib/fetcher/helpers.js +1 -16
  195. package/dist/src/lib/fetcher/helpers.test.js +1 -37
  196. package/dist/src/lib/fetcher/types.js +1 -1
  197. package/dist/src/lib/fetcher-client.js +1 -17
  198. package/dist/src/lib/fetcher.js +1 -33
  199. package/dist/src/lib/helpers/auto-form.js +1 -98
  200. package/dist/src/lib/helpers/auto-form.test.js +1 -336
  201. package/dist/src/lib/navigation.d.ts +15 -13
  202. package/dist/src/lib/navigation.d.ts.map +1 -1
  203. package/dist/src/lib/navigation.js +1 -11
  204. package/dist/src/lib/plugin.js +1 -3
  205. package/dist/src/lib/special-characters.js +1 -4
  206. package/dist/src/lib/special-characters.test.js +1 -56
  207. package/dist/src/lib/utils.js +1 -5
  208. package/dist/src/locales/en.json +308 -0
  209. package/dist/src/tests/setup.js +1 -6
  210. package/dist/src/views/admin/layouts/admin-layout.js +1 -73
  211. package/dist/src/views/admin/layouts/sidebar/nav/item.js +1 -98
  212. package/dist/src/views/admin/layouts/sidebar/nav/nav.js +1 -60
  213. package/dist/src/views/admin/layouts/sidebar/sidebar.js +1 -28
  214. package/dist/src/views/admin/layouts/sidebar/sign-out.js +1 -14
  215. package/dist/src/views/admin/layouts/user-bar/client.js +1 -69
  216. package/dist/src/views/admin/layouts/user-bar/user-bar.js +1 -32
  217. package/dist/src/views/admin/sign-in/sign-in-admin-view.js +1 -27
  218. package/dist/src/views/admin/views/core/advanced/cron/cron-table-view.js +1 -97
  219. package/dist/src/views/admin/views/core/advanced/cron/run-action/mutation-api.js +1 -23
  220. package/dist/src/views/admin/views/core/advanced/cron/run-action/run-action.js +1 -38
  221. package/dist/src/views/admin/views/core/dashboard/dashboard-admin-view.js +1 -44
  222. package/dist/src/views/admin/views/core/debug/actions/clear-cache/clear-cache.js +1 -35
  223. package/dist/src/views/admin/views/core/debug/actions/clear-cache/mutation-api.js +1 -5
  224. package/dist/src/views/admin/views/core/debug/system-logs/actions/more/content.js +1 -281
  225. package/dist/src/views/admin/views/core/debug/system-logs/actions/more/more.js +1 -67
  226. package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-status.js +1 -14
  227. package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-type-log.js +1 -30
  228. package/dist/src/views/admin/views/core/debug/system-logs/system-logs-view.js +1 -96
  229. package/dist/src/views/admin/views/core/test.js +1 -161
  230. package/dist/src/views/admin/views/core/users/users-admin-view.js +1 -89
  231. package/dist/src/views/auth/password-reset/change-password-form/form.js +1 -48
  232. package/dist/src/views/auth/password-reset/change-password-form/mutation-api.js +1 -22
  233. package/dist/src/views/auth/password-reset/change-password-form/use-form.js +1 -36
  234. package/dist/src/views/auth/password-reset/form/form.js +1 -104
  235. package/dist/src/views/auth/password-reset/form/mutation-api.js +1 -21
  236. package/dist/src/views/auth/password-reset/form/use-form.js +1 -33
  237. package/dist/src/views/auth/password-reset/password-reset-view.js +1 -37
  238. package/dist/src/views/auth/sign-in/form/form.js +1 -64
  239. package/dist/src/views/auth/sign-in/form/mutation-api.js +1 -33
  240. package/dist/src/views/auth/sign-in/form/use-form.d.ts.map +1 -1
  241. package/dist/src/views/auth/sign-in/form/use-form.js +1 -38
  242. package/dist/src/views/auth/sign-in/sign-in-view.js +1 -61
  243. package/dist/src/views/auth/sign-up/components/password-input.js +1 -96
  244. package/dist/src/views/auth/sign-up/email-confirmation-view.js +1 -55
  245. package/dist/src/views/auth/sign-up/form/form.js +1 -89
  246. package/dist/src/views/auth/sign-up/form/mutation-api.js +1 -30
  247. package/dist/src/views/auth/sign-up/form/use-form.js +1 -70
  248. package/dist/src/views/auth/sign-up/sign-up-view.js +1 -65
  249. package/dist/src/views/auth/sign-up/wrapper.js +1 -22
  250. package/dist/src/views/auth/sso/buttons/client.js +1 -22
  251. package/dist/src/views/auth/sso/buttons/mutation-api.js +1 -24
  252. package/dist/src/views/auth/sso/buttons/sso-buttons.js +1 -56
  253. package/dist/src/views/auth/sso/callback/callback-sso-view.js +1 -32
  254. package/dist/src/views/auth/sso/callback/client/client.js +1 -68
  255. package/dist/src/views/auth/sso/callback/client/mutation-api.js +1 -32
  256. package/dist/src/views/error/back-button.js +1 -14
  257. package/dist/src/views/error/error-view.js +1 -58
  258. package/dist/src/views/error/global-error-view.js +1 -50
  259. package/dist/src/views/layouts/provider.js +1 -44
  260. package/dist/src/views/layouts/root-layout.js +1 -25
  261. package/dist/src/views/layouts/theme/header/header.js +1 -38
  262. package/dist/src/views/layouts/theme/header/user/auth/auth.js +1 -33
  263. package/dist/src/views/layouts/theme/header/user/auth/client.js +1 -47
  264. package/dist/src/views/layouts/theme/header/user/auth/log-out-mutation-api.js +1 -27
  265. package/dist/src/views/layouts/theme/header/user/user.js +1 -32
  266. package/dist/src/views/layouts/theme/layout.js +1 -16
  267. package/dist/src/vitnode.config.d.ts.map +1 -1
  268. package/dist/src/vitnode.config.js +1 -42
  269. package/dist/tsconfig.tsbuildinfo +1 -1
  270. package/dist/tsup.config.js +18 -0
  271. package/dist/vitest.config.d.ts.map +1 -1
  272. package/dist/vitest.config.js +49 -0
  273. package/eslint.config.mjs +2 -0
  274. package/package.json +40 -44
  275. package/dist/src/api/adapters/cron/node-cron.adapter.d.ts +0 -3
  276. package/dist/src/api/adapters/cron/node-cron.adapter.d.ts.map +0 -1
  277. package/dist/src/api/adapters/cron/node-cron.adapter.js +0 -11
  278. package/dist/src/api/adapters/email/nodemailer.d.ts +0 -10
  279. package/dist/src/api/adapters/email/nodemailer.d.ts.map +0 -1
  280. package/dist/src/api/adapters/email/nodemailer.js +0 -30
  281. package/dist/src/api/adapters/email/resend.d.ts +0 -6
  282. package/dist/src/api/adapters/email/resend.d.ts.map +0 -1
  283. package/dist/src/api/adapters/email/resend.js +0 -21
@@ -1,61 +1 @@
1
- import { eq } from "drizzle-orm";
2
- import { getCookie, setCookie } from "hono/cookie";
3
- import { randomBytes } from "node:crypto";
4
- import { core_sessions_known_devices } from "../../database/sessions.js";
5
- import { CONFIG } from "../../lib/config.js";
6
- export class DeviceModel {
7
- constructor(c){
8
- this.c = c;
9
- }
10
- c;
11
- async createDevice() {
12
- const publicId = randomBytes(16).toString("hex");
13
- const [device] = await this.c.get("db").insert(core_sessions_known_devices).values({
14
- publicId,
15
- ipAddress: this.c.get("ipAddress"),
16
- userAgent: this.getUserAgent()
17
- }).returning({
18
- id: core_sessions_known_devices.id
19
- });
20
- this.setCookieDevice(publicId);
21
- return {
22
- id: device.id,
23
- publicId
24
- };
25
- }
26
- getUserAgent() {
27
- return this.c.req.header("User-Agent") ?? "node";
28
- }
29
- setCookieDevice(publicDeviceId) {
30
- setCookie(this.c, this.c.get("core").authorization.deviceCookieName, publicDeviceId, {
31
- httpOnly: true,
32
- secure: this.c.get("core").authorization.cookieSecure,
33
- path: "/",
34
- domain: CONFIG.web.hostname,
35
- expires: new Date(Date.now() + this.c.get("core").authorization.deviceCookieExpires)
36
- });
37
- }
38
- async getDeviceId() {
39
- const deviceIdFromCookie = getCookie(this.c, this.c.get("core").authorization.deviceCookieName);
40
- try {
41
- if (deviceIdFromCookie) {
42
- const [device] = await this.c.get("db").select({
43
- id: core_sessions_known_devices.id
44
- }).from(core_sessions_known_devices).where(eq(core_sessions_known_devices.publicId, deviceIdFromCookie));
45
- if (!device) {
46
- return await this.createDevice();
47
- }
48
- await this.c.get("db").update(core_sessions_known_devices).set({
49
- ipAddress: this.c.get("ipAddress"),
50
- userAgent: this.getUserAgent()
51
- }).where(eq(core_sessions_known_devices.publicId, deviceIdFromCookie));
52
- return {
53
- id: device.id,
54
- publicId: deviceIdFromCookie
55
- };
56
- }
57
- } catch {
58
- /* empty */ }
59
- return await this.createDevice();
60
- }
61
- }
1
+ import{eq}from"drizzle-orm";import{getCookie,setCookie}from"hono/cookie";import{randomBytes}from"node:crypto";import{core_sessions_known_devices}from"../../database/sessions.js";import{CONFIG}from"../../lib/config.js";export class DeviceModel{constructor(c){this.c=c}c;async createDevice(){const publicId=randomBytes(16).toString("hex");const[device]=await this.c.get("db").insert(core_sessions_known_devices).values({publicId,ipAddress:this.c.get("ipAddress"),userAgent:this.getUserAgent()}).returning({id:core_sessions_known_devices.id});this.setCookieDevice(publicId);return{id:device.id,publicId}}getUserAgent(){return this.c.req.header("User-Agent")??"node"}setCookieDevice(publicDeviceId){setCookie(this.c,this.c.get("core").authorization.deviceCookieName,publicDeviceId,{httpOnly:true,secure:this.c.get("core").authorization.cookieSecure,path:"/",domain:CONFIG.web.hostname,expires:new Date(Date.now()+this.c.get("core").authorization.deviceCookieExpires)})}async getDeviceId(){const deviceIdFromCookie=getCookie(this.c,this.c.get("core").authorization.deviceCookieName);try{if(deviceIdFromCookie){const[device]=await this.c.get("db").select({id:core_sessions_known_devices.id}).from(core_sessions_known_devices).where(eq(core_sessions_known_devices.publicId,deviceIdFromCookie));if(!device){return await this.createDevice()}await this.c.get("db").update(core_sessions_known_devices).set({ipAddress:this.c.get("ipAddress"),userAgent:this.getUserAgent()}).where(eq(core_sessions_known_devices.publicId,deviceIdFromCookie));return{id:device.id,publicId:deviceIdFromCookie}}}catch{}return await this.createDevice()}}
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../../src/api/models/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAC;AACxD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAI/E,UAAU,0BAA0B;IAClC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,UAAU,2BAA2B;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QACjD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;KACZ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,CACP,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,GAChD,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAC/B,KAAK,CAAC,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EACH,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,GAC5D,MAAM,CAAC;CAEZ,GAAG,CAAC,2BAA2B,GAAG,0BAA0B,CAAC,CAAC;AAE/D,qBAAa,UAAU;gBACT,CAAC,EAAE,OAAO;IAItB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IAExB,IAAI,CAAC,EACT,IAAI,EACJ,OAAO,EACP,OAAO,EACP,EAAE,EACF,IAAI,EACJ,OAAO,EACP,MAAM,EAAE,cAAc,GACvB,EAAE,kBAAkB;CA+EtB"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../../src/api/models/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAC;AACxD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAI/E,UAAU,0BAA0B;IAClC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,UAAU,2BAA2B;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QACjD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;KACZ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,CACP,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,GAChD,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAC/B,KAAK,CAAC,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EACH,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,GAC5D,MAAM,CAAC;CAEZ,GAAG,CAAC,2BAA2B,GAAG,0BAA0B,CAAC,CAAC;AAE/D,qBAAa,UAAU;gBACT,CAAC,EAAE,OAAO;IAItB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IAExB,IAAI,CAAC,EACT,IAAI,EACJ,OAAO,EACP,OAAO,EACP,EAAE,EACF,IAAI,EACJ,OAAO,EACP,MAAM,EAAE,cAAc,GACvB,EAAE,kBAAkB;CAkFtB"}
@@ -1,77 +1 @@
1
- import { render } from "@react-email/components";
2
- import { HTTPException } from "hono/http-exception";
3
- import { CONFIG } from "../../lib/config.js";
4
- export class EmailModel {
5
- constructor(c){
6
- this.c = c;
7
- }
8
- c;
9
- async send({ html, replyTo, subject, to, user, content, locale: localeFromArgs }) {
10
- const core = this.c.get("core");
11
- const provider = core.email?.adapter;
12
- if (!provider) {
13
- throw new HTTPException(500, {
14
- message: "Email provider not found"
15
- });
16
- }
17
- const locale = localeFromArgs ?? user?.language ?? "en";
18
- const pluginIds = [
19
- "@vitnode/core",
20
- ...this.c.get("core").plugins.map((plugin)=>plugin.id)
21
- ];
22
- const messagesPromises = pluginIds.map(async (pluginId)=>{
23
- try {
24
- const path = `${pluginId}/${locale}.json`;
25
- const messages = await core.pathToMessages(path);
26
- return messages.default;
27
- } catch {
28
- return {};
29
- }
30
- });
31
- const allMessages = await Promise.all(messagesPromises);
32
- const messages = allMessages.reduce((acc, curr)=>({
33
- ...acc,
34
- ...curr
35
- }), {});
36
- const htmlContent = html ?? content({
37
- i18n: {
38
- locale,
39
- messages
40
- },
41
- templateProps: {
42
- metadata: {
43
- ...core.metadata,
44
- url: CONFIG.web.href
45
- },
46
- logo: core.email?.logo
47
- },
48
- user
49
- });
50
- const emailTo = user?.email ?? to;
51
- if (!emailTo) {
52
- throw new HTTPException(400, {
53
- message: "Email address is required"
54
- });
55
- }
56
- try {
57
- await provider.sendEmail({
58
- html: await render(htmlContent),
59
- to: emailTo,
60
- subject: typeof subject === "function" ? subject({
61
- i18n: {
62
- locale,
63
- messages
64
- }
65
- }) : subject,
66
- replyTo,
67
- metadata: core.metadata,
68
- text: await render(htmlContent, {
69
- plainText: true
70
- })
71
- });
72
- } catch (err) {
73
- const error = err instanceof Error ? err : new Error("Unknown error from email provider");
74
- await this.c.get("log").error(`Failed to send email: ${error.message}`);
75
- }
76
- }
77
- }
1
+ import{render}from"@react-email/components";import{HTTPException}from"hono/http-exception";import{CONFIG}from"../../lib/config.js";export class EmailModel{constructor(c){this.c=c}c;async send({html,replyTo,subject,to,user,content,locale:localeFromArgs}){const core=this.c.get("core");const provider=core.email?.adapter;if(!provider){throw new HTTPException(500,{message:"Email provider not found"})}const locale=localeFromArgs??user?.language??"en";const pluginIds=["@vitnode/core",...this.c.get("core").plugins.map(plugin=>plugin.id)];const messagesPromises=pluginIds.map(async pluginId=>{try{const path=`${pluginId}/${locale}.json`;const messages=await core.pathToMessages(path);return messages.default}catch{return{}}});const allMessages=await Promise.all(messagesPromises);const messages=Object.assign({},...allMessages);const htmlContent=html??content({i18n:{locale,messages},templateProps:{metadata:{...core.metadata,url:CONFIG.web.href},logo:core.email?.logo},user});const emailTo=user?.email??to;if(!emailTo){throw new HTTPException(400,{message:"Email address is required"})}try{await provider.sendEmail({html:await render(htmlContent),to:emailTo,subject:typeof subject==="function"?subject({i18n:{locale,messages}}):subject,replyTo,metadata:core.metadata,text:await render(htmlContent,{plainText:true})})}catch(err){const error=err instanceof Error?err:new Error("Unknown error from email provider");await this.c.get("log").error(`Failed to send email: ${error.message}`)}}}
@@ -1,36 +1 @@
1
- import crypto from "node:crypto";
2
- export class PasswordModel {
3
- async encryptPassword(password) {
4
- return await new Promise((resolve, reject)=>{
5
- const salt = crypto.randomBytes(8).toString("hex");
6
- crypto.scrypt(password, salt, 64, (err, derivedKey)=>{
7
- if (err) reject(err);
8
- resolve(`${salt}:${derivedKey.toString("hex")}`);
9
- });
10
- });
11
- }
12
- async verifyPassword(password, hash) {
13
- return await new Promise((resolve, reject)=>{
14
- const [salt, key] = hash.split(":");
15
- const keyBuffer = Buffer.from(key, "hex");
16
- crypto.scrypt(password, salt, 64, (err, derivedKey)=>{
17
- if (err) reject(err);
18
- if (keyBuffer.length !== derivedKey.length) {
19
- crypto.timingSafeEqual(derivedKey, derivedKey);
20
- resolve(false);
21
- } else {
22
- const areEqual = crypto.timingSafeEqual(keyBuffer, derivedKey);
23
- resolve(areEqual);
24
- }
25
- });
26
- });
27
- }
28
- }
29
- export class ForgotPasswordTokenModel {
30
- generateResetToken() {
31
- return crypto.randomBytes(32).toString("base64url");
32
- }
33
- hashResetToken(token) {
34
- return crypto.createHash("sha256").update(token).digest("hex");
35
- }
36
- }
1
+ import crypto from"node:crypto";export class PasswordModel{async encryptPassword(password){return await new Promise((resolve,reject)=>{const salt=crypto.randomBytes(8).toString("hex");crypto.scrypt(password,salt,64,(err,derivedKey)=>{if(err)reject(err);resolve(`${salt}:${derivedKey.toString("hex")}`)})})}async verifyPassword(password,hash){return await new Promise((resolve,reject)=>{const[salt,key]=hash.split(":");const keyBuffer=Buffer.from(key,"hex");crypto.scrypt(password,salt,64,(err,derivedKey)=>{if(err)reject(err);if(keyBuffer.length!==derivedKey.length){crypto.timingSafeEqual(derivedKey,derivedKey);resolve(false)}else{const areEqual=crypto.timingSafeEqual(keyBuffer,derivedKey);resolve(areEqual)}})})}}export class ForgotPasswordTokenModel{generateResetToken(){return crypto.randomBytes(32).toString("base64url")}hashResetToken(token){return crypto.createHash("sha256").update(token).digest("hex")}}
@@ -1 +1 @@
1
- {"version":3,"file":"session-admin.d.ts","sourceRoot":"","sources":["../../../../src/api/models/session-admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAYpC,qBAAa,iBAAiB;gBAChB,CAAC,EAAE,OAAO;IAGtB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;YAEhB,SAAS;IASjB,kBAAkB,CAAC,MAAM,EAAE,MAAM;IAmBjC,qBAAqB,CAAC,MAAM,EAAE,MAAM;;;IAwCpC,aAAa;IAoBb,OAAO;;;;;;;;;;;;;CAiDd"}
1
+ {"version":3,"file":"session-admin.d.ts","sourceRoot":"","sources":["../../../../src/api/models/session-admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAYpC,qBAAa,iBAAiB;gBAChB,CAAC,EAAE,OAAO;IAGtB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;YAEhB,SAAS;IAejB,kBAAkB,CAAC,MAAM,EAAE,MAAM;IAmBjC,qBAAqB,CAAC,MAAM,EAAE,MAAM;;;IA2CpC,aAAa;IAoBb,OAAO;;;;;;;;;;;;;CAiDd"}
@@ -1,97 +1 @@
1
- import { and, eq, gt, or } from "drizzle-orm"; // Removed 'or' as it's safer not to use it here
2
- import { deleteCookie, getCookie, setCookie } from "hono/cookie";
3
- import { HTTPException } from "hono/http-exception";
4
- import { core_admin_permissions, core_admin_sessions } from "../../database/admins.js";
5
- import { CONFIG } from "../../lib/config.js";
6
- import { DeviceModel } from "./device.js";
7
- import { UserModel } from "./user.js";
8
- export class SessionAdminModel {
9
- constructor(c){
10
- this.c = c;
11
- }
12
- c;
13
- async hashToken(token) {
14
- const encoder = new TextEncoder();
15
- const data = encoder.encode(token);
16
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
17
- const hashArray = Array.from(new Uint8Array(hashBuffer));
18
- return hashArray.map((b)=>b.toString(16).padStart(2, "0")).join("");
19
- }
20
- async checkIfUserIsAdmin(userId) {
21
- const user = await new UserModel().getUserById({
22
- id: userId,
23
- c: this.c
24
- });
25
- if (!user) return false;
26
- const [permission] = await this.c.get("db").select().from(core_admin_permissions).where(or(eq(core_admin_permissions.userId, user.id), eq(core_admin_permissions.roleId, user.roleId))).limit(1);
27
- return !!permission;
28
- }
29
- async createSessionByUserId(userId) {
30
- const isAdmin = await this.checkIfUserIsAdmin(userId);
31
- if (!isAdmin) {
32
- throw new HTTPException(403, {
33
- message: "Forbidden"
34
- });
35
- }
36
- const randomBytes = new Uint8Array(64);
37
- crypto.getRandomValues(randomBytes);
38
- const token = Array.from(randomBytes).map((b)=>b.toString(16).padStart(2, "0")).join("");
39
- const hashedToken = await this.hashToken(token);
40
- const device = await new DeviceModel(this.c).getDeviceId();
41
- await this.c.get("db").insert(core_admin_sessions).values({
42
- token: hashedToken,
43
- userId,
44
- expiresAt: new Date(Date.now() + this.c.get("core").authorization.adminCookieExpires),
45
- deviceId: device.id
46
- });
47
- setCookie(this.c, this.c.get("core").authorization.adminCookieName, token, {
48
- httpOnly: true,
49
- secure: this.c.get("core").authorization.cookieSecure,
50
- path: "/",
51
- expires: new Date(Date.now() + this.c.get("core").authorization.adminCookieExpires),
52
- domain: CONFIG.web.hostname
53
- });
54
- return {
55
- token
56
- };
57
- }
58
- async deleteSession() {
59
- const token = getCookie(this.c, this.c.get("core").authorization.adminCookieName);
60
- if (!token) return;
61
- const hashedToken = await this.hashToken(token);
62
- await this.c.get("db").delete(core_admin_sessions).where(eq(core_admin_sessions.token, hashedToken));
63
- deleteCookie(this.c, this.c.get("core").authorization.adminCookieName, {
64
- path: "/",
65
- domain: CONFIG.web.hostname
66
- });
67
- }
68
- async getUser() {
69
- const { authorization } = this.c.get("core");
70
- const token = getCookie(this.c, authorization.adminCookieName);
71
- if (!token) return null;
72
- const device = await new DeviceModel(this.c).getDeviceId();
73
- if (!device) return null;
74
- const hashedToken = await this.hashToken(token);
75
- const [session] = await this.c.get("db").select({
76
- userId: core_admin_sessions.userId
77
- }).from(core_admin_sessions).where(and(eq(core_admin_sessions.token, hashedToken), eq(core_admin_sessions.deviceId, device.id), gt(core_admin_sessions.expiresAt, new Date()))).limit(1);
78
- if (!session) {
79
- deleteCookie(this.c, this.c.get("core").authorization.adminCookieName, {
80
- path: "/",
81
- domain: CONFIG.web.hostname
82
- });
83
- return null;
84
- }
85
- const user = await new UserModel().getUserById({
86
- id: session.userId,
87
- c: this.c
88
- });
89
- if (!user) return null;
90
- const isStillAdmin = await this.checkIfUserIsAdmin(user.id);
91
- if (!isStillAdmin) {
92
- await this.deleteSession();
93
- return null;
94
- }
95
- return user;
96
- }
97
- }
1
+ import{and,eq,gt,or}from"drizzle-orm";import{deleteCookie,getCookie,setCookie}from"hono/cookie";import{HTTPException}from"hono/http-exception";import{core_admin_permissions,core_admin_sessions}from"../../database/admins.js";import{CONFIG}from"../../lib/config.js";import{DeviceModel}from"./device.js";import{UserModel}from"./user.js";export class SessionAdminModel{constructor(c){this.c=c}c;async hashToken(token){const encoder=new TextEncoder;const data=encoder.encode(token);const hashBuffer=await crypto.subtle.digest("SHA-256",data);const bytes=new Uint8Array(hashBuffer);let result="";for(const byte of bytes){result+=byte.toString(16).padStart(2,"0")}return result}async checkIfUserIsAdmin(userId){const user=await new UserModel().getUserById({id:userId,c:this.c});if(!user)return false;const[permission]=await this.c.get("db").select().from(core_admin_permissions).where(or(eq(core_admin_permissions.userId,user.id),eq(core_admin_permissions.roleId,user.roleId))).limit(1);return!!permission}async createSessionByUserId(userId){const isAdmin=await this.checkIfUserIsAdmin(userId);if(!isAdmin){throw new HTTPException(403,{message:"Forbidden"})}const randomBytes=new Uint8Array(64);crypto.getRandomValues(randomBytes);let token="";for(const byte of randomBytes){token+=byte.toString(16).padStart(2,"0")}const hashedToken=await this.hashToken(token);const device=await new DeviceModel(this.c).getDeviceId();await this.c.get("db").insert(core_admin_sessions).values({token:hashedToken,userId,expiresAt:new Date(Date.now()+this.c.get("core").authorization.adminCookieExpires),deviceId:device.id});setCookie(this.c,this.c.get("core").authorization.adminCookieName,token,{httpOnly:true,secure:this.c.get("core").authorization.cookieSecure,path:"/",expires:new Date(Date.now()+this.c.get("core").authorization.adminCookieExpires),domain:CONFIG.web.hostname});return{token}}async deleteSession(){const token=getCookie(this.c,this.c.get("core").authorization.adminCookieName);if(!token)return;const hashedToken=await this.hashToken(token);await this.c.get("db").delete(core_admin_sessions).where(eq(core_admin_sessions.token,hashedToken));deleteCookie(this.c,this.c.get("core").authorization.adminCookieName,{path:"/",domain:CONFIG.web.hostname})}async getUser(){const{authorization}=this.c.get("core");const token=getCookie(this.c,authorization.adminCookieName);if(!token)return null;const device=await new DeviceModel(this.c).getDeviceId();if(!device)return null;const hashedToken=await this.hashToken(token);const[session]=await this.c.get("db").select({userId:core_admin_sessions.userId}).from(core_admin_sessions).where(and(eq(core_admin_sessions.token,hashedToken),eq(core_admin_sessions.deviceId,device.id),gt(core_admin_sessions.expiresAt,new Date))).limit(1);if(!session){deleteCookie(this.c,this.c.get("core").authorization.adminCookieName,{path:"/",domain:CONFIG.web.hostname});return null}const user=await new UserModel().getUserById({id:session.userId,c:this.c});if(!user)return null;const isStillAdmin=await this.checkIfUserIsAdmin(user.id);if(!isStillAdmin){await this.deleteSession();return null}return user}}
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/api/models/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAWpC,qBAAa,YAAY;gBACX,CAAC,EAAE,OAAO;IAGtB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;YAEhB,SAAS;IASjB,qBAAqB,CAAC,MAAM,EAAE,MAAM;;;IAsCpC,aAAa;IA8Bb,OAAO;;;;;;;;;;;;;CA0Cd"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/api/models/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAWpC,qBAAa,YAAY;gBACX,CAAC,EAAE,OAAO;IAGtB,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;YAEhB,SAAS;IAejB,qBAAqB,CAAC,MAAM,EAAE,MAAM;;;IA0CpC,aAAa;IA8Bb,OAAO;;;;;;;;;;;;;CA0Cd"}
@@ -1,76 +1 @@
1
- import { and, eq, gt } from "drizzle-orm";
2
- import { deleteCookie, getCookie, setCookie } from "hono/cookie";
3
- import { core_sessions } from "../../database/sessions.js";
4
- import { CONFIG } from "../../lib/config.js";
5
- import { DeviceModel } from "./device.js";
6
- import { UserModel } from "./user.js";
7
- export class SessionModel {
8
- constructor(c){
9
- this.c = c;
10
- }
11
- c;
12
- async hashToken(token) {
13
- const encoder = new TextEncoder();
14
- const data = encoder.encode(token);
15
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
16
- const hashArray = Array.from(new Uint8Array(hashBuffer));
17
- return hashArray.map((b)=>b.toString(16).padStart(2, "0")).join("");
18
- }
19
- async createSessionByUserId(userId) {
20
- // Generate secure random bytes using Web Crypto API
21
- const randomBytes = new Uint8Array(64);
22
- crypto.getRandomValues(randomBytes);
23
- const token = Array.from(randomBytes).map((b)=>b.toString(16).padStart(2, "0")).join("");
24
- const device = await new DeviceModel(this.c).getDeviceId();
25
- const hashedToken = await this.hashToken(token);
26
- await this.c.get("db").insert(core_sessions).values({
27
- token: hashedToken,
28
- userId,
29
- expiresAt: new Date(Date.now() + this.c.get("core").authorization.cookie_expires),
30
- deviceId: device.id
31
- });
32
- setCookie(this.c, this.c.get("core").authorization.cookieName, token, {
33
- httpOnly: true,
34
- secure: this.c.get("core").authorization.cookieSecure,
35
- path: "/",
36
- expires: this.c.get("core").authorization.cookie_expires > 0 ? new Date(Date.now() + this.c.get("core").authorization.cookie_expires) : undefined,
37
- domain: CONFIG.web.hostname
38
- });
39
- return {
40
- token
41
- };
42
- }
43
- async deleteSession() {
44
- const token = getCookie(this.c, this.c.get("core").authorization.cookieName);
45
- const device = await new DeviceModel(this.c).getDeviceId();
46
- // Ensure both token and deviceId exist before proceeding
47
- if (!(token && device.id)) {
48
- deleteCookie(this.c, this.c.get("core").authorization.cookieName);
49
- return;
50
- }
51
- const hashedToken = await this.hashToken(token);
52
- await this.c.get("db").delete(core_sessions)// Harden the query to ensure a user can only delete their own device's session
53
- .where(and(eq(core_sessions.token, hashedToken), eq(core_sessions.deviceId, device.id)));
54
- deleteCookie(this.c, this.c.get("core").authorization.cookieName);
55
- }
56
- async getUser() {
57
- const token = getCookie(this.c, this.c.get("core").authorization.cookieName);
58
- if (!token) return null;
59
- const device = await new DeviceModel(this.c).getDeviceId();
60
- if (!device) return null;
61
- const hashedToken = await this.hashToken(token);
62
- const [session] = await this.c.get("db").select({
63
- userId: core_sessions.userId
64
- }).from(core_sessions).where(and(eq(core_sessions.token, hashedToken), eq(core_sessions.deviceId, device.id), gt(core_sessions.expiresAt, new Date()))).limit(1);
65
- if (!session) {
66
- deleteCookie(this.c, this.c.get("core").authorization.cookieName);
67
- return null;
68
- }
69
- const user = await new UserModel().getUserById({
70
- id: session.userId,
71
- c: this.c
72
- });
73
- if (!user) return null;
74
- return user;
75
- }
76
- }
1
+ import{and,eq,gt}from"drizzle-orm";import{deleteCookie,getCookie,setCookie}from"hono/cookie";import{core_sessions}from"../../database/sessions.js";import{CONFIG}from"../../lib/config.js";import{DeviceModel}from"./device.js";import{UserModel}from"./user.js";export class SessionModel{constructor(c){this.c=c}c;async hashToken(token){const encoder=new TextEncoder;const data=encoder.encode(token);const hashBuffer=await crypto.subtle.digest("SHA-256",data);const bytes=new Uint8Array(hashBuffer);let result="";for(const byte of bytes){result+=byte.toString(16).padStart(2,"0")}return result}async createSessionByUserId(userId){const randomBytes=new Uint8Array(64);crypto.getRandomValues(randomBytes);let token="";for(const byte of randomBytes){token+=byte.toString(16).padStart(2,"0")}const device=await new DeviceModel(this.c).getDeviceId();const hashedToken=await this.hashToken(token);await this.c.get("db").insert(core_sessions).values({token:hashedToken,userId,expiresAt:new Date(Date.now()+this.c.get("core").authorization.cookie_expires),deviceId:device.id});setCookie(this.c,this.c.get("core").authorization.cookieName,token,{httpOnly:true,secure:this.c.get("core").authorization.cookieSecure,path:"/",expires:this.c.get("core").authorization.cookie_expires>0?new Date(Date.now()+this.c.get("core").authorization.cookie_expires):undefined,domain:CONFIG.web.hostname});return{token}}async deleteSession(){const token=getCookie(this.c,this.c.get("core").authorization.cookieName);const device=await new DeviceModel(this.c).getDeviceId();if(!(token&&device.id)){deleteCookie(this.c,this.c.get("core").authorization.cookieName);return}const hashedToken=await this.hashToken(token);await this.c.get("db").delete(core_sessions).where(and(eq(core_sessions.token,hashedToken),eq(core_sessions.deviceId,device.id)));deleteCookie(this.c,this.c.get("core").authorization.cookieName)}async getUser(){const token=getCookie(this.c,this.c.get("core").authorization.cookieName);if(!token)return null;const device=await new DeviceModel(this.c).getDeviceId();if(!device)return null;const hashedToken=await this.hashToken(token);const[session]=await this.c.get("db").select({userId:core_sessions.userId}).from(core_sessions).where(and(eq(core_sessions.token,hashedToken),eq(core_sessions.deviceId,device.id),gt(core_sessions.expiresAt,new Date))).limit(1);if(!session){deleteCookie(this.c,this.c.get("core").authorization.cookieName);return null}const user=await new UserModel().getUserById({id:session.userId,c:this.c});if(!user)return null;return user}}
@@ -1,120 +1 @@
1
- import { and, eq } from "drizzle-orm";
2
- import { deleteCookie, getCookie, setCookie } from "hono/cookie";
3
- import { HTTPException } from "hono/http-exception";
4
- import crypto from "node:crypto";
5
- import { core_users, core_users_sso } from "../../database/users.js";
6
- import { CONFIG } from "../../lib/config.js";
7
- import { removeSpecialCharacters } from "../../lib/special-characters.js";
8
- import { UserModel } from "./user.js";
9
- export const getRedirectUri = (code)=>new URL(`${CONFIG.web.href}login/sso/${code}`).toString();
10
- export class SSOModel {
11
- constructor(c){
12
- this.c = c;
13
- this.plugins = c.get("core").authorization.ssoAdapters;
14
- }
15
- c;
16
- plugins;
17
- signUpUser = async ({ providerId, user, c })=>{
18
- const data = await new UserModel().signUp({
19
- email: user.email,
20
- name: removeSpecialCharacters(user.username, false),
21
- newsletter: false,
22
- hashedPassword: undefined
23
- }, c);
24
- await c.get("db").insert(core_users_sso).values({
25
- userId: data.id,
26
- providerId: providerId,
27
- providerAccountId: user.id
28
- });
29
- return {
30
- userId: data.id
31
- };
32
- };
33
- async callback({ code, providerId, state }) {
34
- await this.verifyState(state);
35
- const provider = this.plugins.find((p)=>p.id === providerId);
36
- if (!provider) {
37
- throw new HTTPException(404);
38
- }
39
- const ssoToken = await provider.fetchToken(code);
40
- const userFromSSO = await provider.fetchUser(ssoToken);
41
- return await this.c.get("db").transaction(async (tx)=>{
42
- const [dataSSOFromDb] = await tx.select({
43
- userId: core_users_sso.userId
44
- }).from(core_users_sso).leftJoin(core_users, eq(core_users.id, core_users_sso.userId)).where(and(eq(core_users_sso.providerId, providerId), eq(core_users_sso.providerAccountId, userFromSSO.id))).limit(1);
45
- if (!dataSSOFromDb) {
46
- const [userWithEmail] = await tx.select({
47
- id: core_users.id,
48
- email: core_users.email
49
- }).from(core_users).where(eq(core_users.email, userFromSSO.email)).limit(1);
50
- if (!userWithEmail) {
51
- const signUpUser = await this.signUpUser({
52
- providerId,
53
- user: userFromSSO,
54
- c: this.c
55
- });
56
- return signUpUser;
57
- }
58
- // If email exists, throw an error
59
- throw new HTTPException(409, {
60
- message: "Email already exists"
61
- });
62
- // await tx.insert(core_users_sso).values({
63
- // providerId: providerId,
64
- // providerAccountId: userFromSSO.id,
65
- // userId: userWithEmail.id,
66
- // });
67
- }
68
- return {
69
- userId: dataSSOFromDb.userId
70
- };
71
- });
72
- }
73
- async encryptState() {
74
- const state = crypto.randomBytes(8).toString("hex");
75
- const encryptedState = await new Promise((resolve, reject)=>{
76
- const salt = crypto.randomBytes(4).toString("hex");
77
- crypto.scrypt(state, salt, 16, (err, derivedKey)=>{
78
- if (err) reject(err);
79
- resolve(`${salt}:${derivedKey.toString("hex")}`);
80
- });
81
- });
82
- setCookie(this.c, `${this.c.get("core").authorization.cookieName}--state-sso`, encryptedState, {
83
- httpOnly: true,
84
- secure: this.c.get("core").authorization.cookieSecure,
85
- path: "/",
86
- domain: CONFIG.web.hostname
87
- });
88
- return state;
89
- }
90
- async getUrl(providerId) {
91
- const provider = this.plugins.find((p)=>p.id === providerId);
92
- if (!provider) {
93
- throw new HTTPException(404);
94
- }
95
- return provider.getUrl({
96
- state: await this.encryptState()
97
- });
98
- }
99
- async verifyState(state) {
100
- const storedState = getCookie(this.c, `${this.c.get("core").authorization.cookieName}--state-sso`);
101
- if (!storedState) {
102
- throw new HTTPException(400, {
103
- message: "Invalid state"
104
- });
105
- }
106
- const isValid = await new Promise((resolve, reject)=>{
107
- const [salt, storedHash] = storedState.split(":");
108
- crypto.scrypt(state, salt, 16, (err, derivedKey)=>{
109
- if (err) reject(err);
110
- resolve(storedHash === derivedKey.toString("hex"));
111
- });
112
- });
113
- if (!isValid) {
114
- throw new HTTPException(400, {
115
- message: "Invalid state"
116
- });
117
- }
118
- deleteCookie(this.c, `${this.c.get("core").authorization.cookieName}--state-sso`);
119
- }
120
- }
1
+ import{and,eq}from"drizzle-orm";import{deleteCookie,getCookie,setCookie}from"hono/cookie";import{HTTPException}from"hono/http-exception";import crypto from"node:crypto";import{core_users,core_users_sso}from"../../database/users.js";import{CONFIG}from"../../lib/config.js";import{removeSpecialCharacters}from"../../lib/special-characters.js";import{UserModel}from"./user.js";export const getRedirectUri=code=>new URL(`${CONFIG.web.href}login/sso/${code}`).toString();export class SSOModel{constructor(c){this.c=c;this.plugins=c.get("core").authorization.ssoAdapters}c;plugins;signUpUser=async({providerId,user,c})=>{const data=await new UserModel().signUp({email:user.email,name:removeSpecialCharacters(user.username,false),newsletter:false,hashedPassword:undefined},c);await c.get("db").insert(core_users_sso).values({userId:data.id,providerId:providerId,providerAccountId:user.id});return{userId:data.id}};async callback({code,providerId,state}){await this.verifyState(state);const provider=this.plugins.find(p=>p.id===providerId);if(!provider){throw new HTTPException(404)}const ssoToken=await provider.fetchToken(code);const userFromSSO=await provider.fetchUser(ssoToken);return await this.c.get("db").transaction(async tx=>{const[dataSSOFromDb]=await tx.select({userId:core_users_sso.userId}).from(core_users_sso).leftJoin(core_users,eq(core_users.id,core_users_sso.userId)).where(and(eq(core_users_sso.providerId,providerId),eq(core_users_sso.providerAccountId,userFromSSO.id))).limit(1);if(!dataSSOFromDb){const[userWithEmail]=await tx.select({id:core_users.id,email:core_users.email}).from(core_users).where(eq(core_users.email,userFromSSO.email)).limit(1);if(!userWithEmail){const signUpUser=await this.signUpUser({providerId,user:userFromSSO,c:this.c});return signUpUser}throw new HTTPException(409,{message:"Email already exists"})}return{userId:dataSSOFromDb.userId}})}async encryptState(){const state=crypto.randomBytes(8).toString("hex");const encryptedState=await new Promise((resolve,reject)=>{const salt=crypto.randomBytes(4).toString("hex");crypto.scrypt(state,salt,16,(err,derivedKey)=>{if(err)reject(err);resolve(`${salt}:${derivedKey.toString("hex")}`)})});setCookie(this.c,`${this.c.get("core").authorization.cookieName}--state-sso`,encryptedState,{httpOnly:true,secure:this.c.get("core").authorization.cookieSecure,path:"/",domain:CONFIG.web.hostname});return state}async getUrl(providerId){const provider=this.plugins.find(p=>p.id===providerId);if(!provider){throw new HTTPException(404)}return provider.getUrl({state:await this.encryptState()})}async verifyState(state){const storedState=getCookie(this.c,`${this.c.get("core").authorization.cookieName}--state-sso`);if(!storedState){throw new HTTPException(400,{message:"Invalid state"})}const isValid=await new Promise((resolve,reject)=>{const[salt,storedHash]=storedState.split(":");crypto.scrypt(state,salt,16,(err,derivedKey)=>{if(err)reject(err);resolve(storedHash===derivedKey.toString("hex"))})});if(!isValid){throw new HTTPException(400,{message:"Invalid state"})}deleteCookie(this.c,`${this.c.get("core").authorization.cookieName}--state-sso`)}}
@@ -1,19 +1 @@
1
- import { eq } from "drizzle-orm";
2
- import { core_users } from "../../../database/users.js";
3
- export const getUserById = async ({ id, c })=>{
4
- const [user] = await c.get("db").select({
5
- id: core_users.id,
6
- email: core_users.email,
7
- name: core_users.name,
8
- nameCode: core_users.nameCode,
9
- createdAt: core_users.createdAt,
10
- newsletter: core_users.newsletter,
11
- avatarColor: core_users.avatarColor,
12
- emailVerified: core_users.emailVerified,
13
- roleId: core_users.roleId,
14
- birthday: core_users.birthday,
15
- language: core_users.language
16
- }).from(core_users).where(eq(core_users.id, id)).limit(1);
17
- if (!user) return null;
18
- return user;
19
- };
1
+ import{eq}from"drizzle-orm";import{core_users}from"../../../database/users.js";export const getUserById=async({id,c})=>{const[user]=await c.get("db").select({id:core_users.id,email:core_users.email,name:core_users.name,nameCode:core_users.nameCode,createdAt:core_users.createdAt,newsletter:core_users.newsletter,avatarColor:core_users.avatarColor,emailVerified:core_users.emailVerified,roleId:core_users.roleId,birthday:core_users.birthday,language:core_users.language}).from(core_users).where(eq(core_users.id,id)).limit(1);if(!user)return null;return user};
@@ -1,22 +1 @@
1
- import { eq } from "drizzle-orm";
2
- import { HTTPException } from "hono/http-exception";
3
- import { core_users } from "../../../database/users.js";
4
- import { PasswordModel } from "../password.js";
5
- export const signInWithPassword = async ({ email, password, c })=>{
6
- const [user] = await c.get("db").select({
7
- id: core_users.id,
8
- email: core_users.email,
9
- password: core_users.password
10
- }).from(core_users).where(eq(core_users.email, email)).limit(1);
11
- if (!user?.password) {
12
- throw new HTTPException(403);
13
- }
14
- const validPassword = await new PasswordModel().verifyPassword(password, user.password);
15
- if (!validPassword) {
16
- throw new HTTPException(403);
17
- }
18
- return {
19
- id: user.id,
20
- email: user.email
21
- };
22
- };
1
+ import{eq}from"drizzle-orm";import{HTTPException}from"hono/http-exception";import{core_users}from"../../../database/users.js";import{PasswordModel}from"../password.js";export const signInWithPassword=async({email,password,c})=>{const[user]=await c.get("db").select({id:core_users.id,email:core_users.email,password:core_users.password}).from(core_users).where(eq(core_users.email,email)).limit(1);if(!user?.password){throw new HTTPException(403)}const validPassword=await new PasswordModel().verifyPassword(password,user.password);if(!validPassword){throw new HTTPException(403)}return{id:user.id,email:user.email}};
@@ -1,73 +1 @@
1
- import { and, count, eq, or } from "drizzle-orm";
2
- import { HTTPException } from "hono/http-exception";
3
- import { generateAvatarColor } from "../../modules/users/avatar-color.js";
4
- import { core_roles } from "../../../database/roles.js";
5
- import { core_users } from "../../../database/users.js";
6
- import { removeSpecialCharacters } from "../../../lib/special-characters.js";
7
- const getDefaultData = async (c)=>{
8
- const [countUsers] = await c.get("db").select({
9
- count: count()
10
- }).from(core_users);
11
- // If no users, return root group
12
- if (countUsers.count === 0) {
13
- const [defaultRole] = await c.get("db").select({
14
- id: core_roles.id
15
- }).from(core_roles).where(and(eq(core_roles.default, false), eq(core_roles.root, true))).limit(1);
16
- if (!defaultRole) {
17
- throw new HTTPException(400, {
18
- message: "Default group not found."
19
- });
20
- }
21
- return {
22
- roleId: defaultRole.id,
23
- emailVerified: true
24
- };
25
- }
26
- const [defaultRole] = await c.get("db").select({
27
- id: core_roles.id
28
- }).from(core_roles).where(and(eq(core_roles.default, true), eq(core_roles.root, false))).limit(1);
29
- if (!defaultRole) {
30
- throw new HTTPException(400, {
31
- message: "Default role not found."
32
- });
33
- }
34
- return {
35
- roleId: defaultRole.id,
36
- emailVerified: !c.get("core").email?.adapter
37
- };
38
- };
39
- export const signUp = async ({ email, name, newsletter, hashedPassword }, c)=>{
40
- const convertToNameSEO = removeSpecialCharacters(name);
41
- const checkIfUserExist = await c.get("db").select({
42
- email: core_users.email,
43
- name_code: core_users.nameCode
44
- }).from(core_users).where(or(eq(core_users.email, email), eq(core_users.nameCode, convertToNameSEO)));
45
- const findEmail = checkIfUserExist.find((user)=>user.email === email);
46
- if (findEmail) {
47
- throw new HTTPException(409, {
48
- message: "Email already exists"
49
- });
50
- }
51
- const findName = checkIfUserExist.find((user)=>user.name_code === convertToNameSEO);
52
- if (findName) {
53
- throw new HTTPException(409, {
54
- message: "Name already exists"
55
- });
56
- }
57
- const { roleId, emailVerified } = await getDefaultData(c);
58
- const [data] = await c.get("db").insert(core_users).values({
59
- email,
60
- name,
61
- nameCode: convertToNameSEO,
62
- // TODO: Handle newsletter only if email is allowed
63
- newsletter,
64
- password: hashedPassword,
65
- avatarColor: generateAvatarColor(name),
66
- roleId,
67
- emailVerified,
68
- ipAddress: c.get("ipAddress")
69
- }).returning();
70
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
- const { password: _, ...user } = data;
72
- return user;
73
- };
1
+ import{and,count,eq,or}from"drizzle-orm";import{HTTPException}from"hono/http-exception";import{generateAvatarColor}from"../../modules/users/avatar-color.js";import{core_roles}from"../../../database/roles.js";import{core_users}from"../../../database/users.js";import{removeSpecialCharacters}from"../../../lib/special-characters.js";const getDefaultData=async c=>{const[countUsers]=await c.get("db").select({count:count()}).from(core_users);if(countUsers.count===0){const[defaultRole]=await c.get("db").select({id:core_roles.id}).from(core_roles).where(and(eq(core_roles.default,false),eq(core_roles.root,true))).limit(1);if(!defaultRole){throw new HTTPException(400,{message:"Default group not found."})}return{roleId:defaultRole.id,emailVerified:true}}const[defaultRole]=await c.get("db").select({id:core_roles.id}).from(core_roles).where(and(eq(core_roles.default,true),eq(core_roles.root,false))).limit(1);if(!defaultRole){throw new HTTPException(400,{message:"Default role not found."})}return{roleId:defaultRole.id,emailVerified:!c.get("core").email?.adapter}};export const signUp=async({email,name,newsletter,hashedPassword},c)=>{const convertToNameSEO=removeSpecialCharacters(name);const checkIfUserExist=await c.get("db").select({email:core_users.email,name_code:core_users.nameCode}).from(core_users).where(or(eq(core_users.email,email),eq(core_users.nameCode,convertToNameSEO)));const findEmail=checkIfUserExist.find(user=>user.email===email);if(findEmail){throw new HTTPException(409,{message:"Email already exists"})}const findName=checkIfUserExist.find(user=>user.name_code===convertToNameSEO);if(findName){throw new HTTPException(409,{message:"Name already exists"})}const{roleId,emailVerified}=await getDefaultData(c);const[data]=await c.get("db").insert(core_users).values({email,name,nameCode:convertToNameSEO,newsletter,password:hashedPassword,avatarColor:generateAvatarColor(name),roleId,emailVerified,ipAddress:c.get("ipAddress")}).returning();const{password:_,...user}=data;return user};