@vitnode/core 1.2.0-canary.60 → 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 (274) 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.js +1 -93
  22. package/dist/src/api/middlewares/rate-limiter.middleware.js +1 -32
  23. package/dist/src/api/models/device.js +1 -61
  24. package/dist/src/api/models/email.js +1 -76
  25. package/dist/src/api/models/password.js +1 -36
  26. package/dist/src/api/models/session-admin.js +1 -106
  27. package/dist/src/api/models/session.js +1 -85
  28. package/dist/src/api/models/sso.js +1 -120
  29. package/dist/src/api/models/user/get-user-by-id.js +1 -19
  30. package/dist/src/api/models/user/sign-in-with-passwords.js +1 -22
  31. package/dist/src/api/models/user/sign-up.js +1 -73
  32. package/dist/src/api/models/user.js +1 -8
  33. package/dist/src/api/modules/admin/admin.module.js +1 -19
  34. package/dist/src/api/modules/admin/advanced/advanced.admin.module.js +1 -11
  35. package/dist/src/api/modules/admin/advanced/cron/cron.admin.module.js +1 -12
  36. package/dist/src/api/modules/admin/advanced/cron/routes/get.route.js +1 -66
  37. package/dist/src/api/modules/admin/advanced/cron/routes/run.route.js +1 -95
  38. package/dist/src/api/modules/admin/debug/debug.admin.module.js +1 -10
  39. package/dist/src/api/modules/admin/debug/routes/logs.route.js +1 -94
  40. package/dist/src/api/modules/admin/routes/session.route.js +1 -47
  41. package/dist/src/api/modules/admin/users/routes/list.route.js +1 -79
  42. package/dist/src/api/modules/admin/users/routes/users.route.js +1 -84
  43. package/dist/src/api/modules/admin/users/users.admin.module.js +1 -10
  44. package/dist/src/api/modules/cron/cron/clean.cron.js +1 -24
  45. package/dist/src/api/modules/cron/cron.module.js +1 -14
  46. package/dist/src/api/modules/cron/helpers/process-cron-jobs.d.ts.map +1 -1
  47. package/dist/src/api/modules/cron/helpers/process-cron-jobs.js +1 -76
  48. package/dist/src/api/modules/cron/routes/cron.route.js +1 -101
  49. package/dist/src/api/modules/middleware/middleware.module.js +1 -10
  50. package/dist/src/api/modules/middleware/route.js +1 -49
  51. package/dist/src/api/modules/users/avatar-color.js +1 -43
  52. package/dist/src/api/modules/users/routes/change-password.route.js +1 -58
  53. package/dist/src/api/modules/users/routes/reset-passowrd.route.js +1 -91
  54. package/dist/src/api/modules/users/routes/session.route.js +1 -46
  55. package/dist/src/api/modules/users/routes/sign-in.route.js +1 -71
  56. package/dist/src/api/modules/users/routes/sign-out.route.js +1 -43
  57. package/dist/src/api/modules/users/routes/sign-up.route.js +1 -77
  58. package/dist/src/api/modules/users/routes/test.route.js +1 -33
  59. package/dist/src/api/modules/users/sso/routes/callback.route.js +1 -52
  60. package/dist/src/api/modules/users/sso/routes/create-url.route.js +1 -36
  61. package/dist/src/api/modules/users/sso/sso.module.js +1 -12
  62. package/dist/src/api/modules/users/users.module.js +1 -26
  63. package/dist/src/api/plugin.js +1 -15
  64. package/dist/src/app/login/page.js +1 -16
  65. package/dist/src/app/login/reset-password/page.js +1 -18
  66. package/dist/src/app/login/sso/[providerId]/page.js +1 -10
  67. package/dist/src/app/register/page.js +1 -16
  68. package/dist/src/app_admin/core/advanced/cron/page.js +1 -42
  69. package/dist/src/app_admin/core/debug/page.js +1 -45
  70. package/dist/src/app_admin/core/page.js +1 -5
  71. package/dist/src/app_admin/core/test/page.js +1 -5
  72. package/dist/src/app_admin/core/users/page.js +1 -38
  73. package/dist/src/components/avatar.js +1 -14
  74. package/dist/src/components/confirm-action/confirm-action-alert-dialog.js +1 -42
  75. package/dist/src/components/confirm-action/content.js +1 -31
  76. package/dist/src/components/date-format.js +1 -41
  77. package/dist/src/components/form/auto-form.js +1 -103
  78. package/dist/src/components/form/common/desc.js +1 -9
  79. package/dist/src/components/form/common/label.js +1 -18
  80. package/dist/src/components/form/fields/checkbox.js +1 -37
  81. package/dist/src/components/form/fields/combobox-async.js +1 -123
  82. package/dist/src/components/form/fields/combobox.js +1 -87
  83. package/dist/src/components/form/fields/input.js +1 -39
  84. package/dist/src/components/form/fields/radio-group.js +1 -50
  85. package/dist/src/components/form/fields/select.js +1 -58
  86. package/dist/src/components/form/fields/switch.js +1 -36
  87. package/dist/src/components/form/fields/textarea.js +1 -36
  88. package/dist/src/components/i18n-provider.js +1 -43
  89. package/dist/src/components/logo-vitnode.js +1 -119
  90. package/dist/src/components/switchers/langs/language-switcher.js +1 -47
  91. package/dist/src/components/switchers/themes/theme-switcher.js +1 -28
  92. package/dist/src/components/table/content.js +1 -81
  93. package/dist/src/components/table/data-table.js +1 -68
  94. package/dist/src/components/table/order-table-head.js +1 -50
  95. package/dist/src/components/table/pagination.js +1 -122
  96. package/dist/src/components/tiptap/extension.js +1 -33
  97. package/dist/src/components/tiptap/toolbar/actions/alignment-action.js +1 -91
  98. package/dist/src/components/tiptap/toolbar/actions/bold-action.js +1 -39
  99. package/dist/src/components/tiptap/toolbar/actions/headings-action.js +1 -93
  100. package/dist/src/components/tiptap/toolbar/actions/italic-action.js +1 -38
  101. package/dist/src/components/tiptap/toolbar/actions/list-action.js +1 -61
  102. package/dist/src/components/tiptap/toolbar/actions/text-format-more/text-format-more.js +1 -59
  103. package/dist/src/components/tiptap/toolbar/actions/underline-action.js +1 -38
  104. package/dist/src/components/tiptap/toolbar/actions/undo-redo-actions.js +1 -63
  105. package/dist/src/components/tiptap/toolbar/actions/utils/tooltip-shortcut.js +1 -11
  106. package/dist/src/components/tiptap/toolbar/tiptap-toolbar.js +1 -55
  107. package/dist/src/components/tiptap/toolbar/use-toolbar-editor.js +1 -5
  108. package/dist/src/components/ui/accordion.js +1 -46
  109. package/dist/src/components/ui/alert-dialog.js +1 -123
  110. package/dist/src/components/ui/alert.js +1 -39
  111. package/dist/src/components/ui/badge.js +1 -28
  112. package/dist/src/components/ui/button-client.js +1 -57
  113. package/dist/src/components/ui/button.js +1 -32
  114. package/dist/src/components/ui/card.js +1 -52
  115. package/dist/src/components/ui/checkbox.js +1 -20
  116. package/dist/src/components/ui/collapsible.js +1 -22
  117. package/dist/src/components/ui/command.js +1 -98
  118. package/dist/src/components/ui/context-menu.js +1 -140
  119. package/dist/src/components/ui/dialog.js +1 -189
  120. package/dist/src/components/ui/drawer.js +1 -83
  121. package/dist/src/components/ui/dropdown-menu.js +1 -141
  122. package/dist/src/components/ui/editor-content.js +1 -10
  123. package/dist/src/components/ui/editor.js +1 -35
  124. package/dist/src/components/ui/form.js +1 -148
  125. package/dist/src/components/ui/header-content.js +1 -30
  126. package/dist/src/components/ui/hover-card.js +1 -29
  127. package/dist/src/components/ui/input-otp.js +1 -48
  128. package/dist/src/components/ui/input.js +1 -11
  129. package/dist/src/components/ui/label.js +1 -12
  130. package/dist/src/components/ui/loader.js +1 -16
  131. package/dist/src/components/ui/menubar.js +1 -151
  132. package/dist/src/components/ui/navigation-menu.js +1 -82
  133. package/dist/src/components/ui/popover.js +1 -35
  134. package/dist/src/components/ui/progress.js +1 -19
  135. package/dist/src/components/ui/radio-group.js +1 -27
  136. package/dist/src/components/ui/scroll-area.js +1 -33
  137. package/dist/src/components/ui/select.js +1 -113
  138. package/dist/src/components/ui/separator.js +1 -14
  139. package/dist/src/components/ui/sheet.js +1 -92
  140. package/dist/src/components/ui/sidebar.js +1 -433
  141. package/dist/src/components/ui/skeleton.js +1 -10
  142. package/dist/src/components/ui/slider.js +1 -50
  143. package/dist/src/components/ui/sonner.js +1 -26
  144. package/dist/src/components/ui/switch.js +1 -16
  145. package/dist/src/components/ui/table.js +1 -63
  146. package/dist/src/components/ui/tabs.js +1 -33
  147. package/dist/src/components/ui/textarea.js +1 -10
  148. package/dist/src/components/ui/toggle-group.js +1 -45
  149. package/dist/src/components/ui/toggle.js +1 -34
  150. package/dist/src/components/ui/tooltip.js +1 -57
  151. package/dist/src/config.js +1 -4
  152. package/dist/src/database/admins.js +1 -74
  153. package/dist/src/database/cron.js +1 -22
  154. package/dist/src/database/languages.js +1 -52
  155. package/dist/src/database/logs.js +1 -34
  156. package/dist/src/database/moderators.js +1 -37
  157. package/dist/src/database/roles.js +1 -13
  158. package/dist/src/database/sessions.js +1 -60
  159. package/dist/src/database/users.js +1 -145
  160. package/dist/src/drizzle.config.js +1 -67
  161. package/dist/src/emails/default-template.js +1 -99
  162. package/dist/src/emails/reset-password.js +1 -132
  163. package/dist/src/emails/ui/button.js +1 -36
  164. package/dist/src/emails/ui/card.js +1 -39
  165. package/dist/src/hooks/use-before-unload.js +1 -30
  166. package/dist/src/hooks/use-captcha.js +1 -122
  167. package/dist/src/hooks/use-mobile.js +1 -16
  168. package/dist/src/lib/api/get-middleware-api.js +1 -13
  169. package/dist/src/lib/api/get-next-cron-run-date.js +1 -14
  170. package/dist/src/lib/api/get-next-cron-run-date.test.js +1 -55
  171. package/dist/src/lib/api/get-session-admin-api.js +1 -19
  172. package/dist/src/lib/api/get-session-api.js +1 -14
  173. package/dist/src/lib/api/should-cron-job-run.js +1 -16
  174. package/dist/src/lib/api/should-cron-job-run.test.js +1 -213
  175. package/dist/src/lib/api/validate-cron-schedule.d.ts +2 -0
  176. package/dist/src/lib/api/validate-cron-schedule.d.ts.map +1 -0
  177. package/dist/src/lib/api/validate-cron-schedule.js +1 -0
  178. package/dist/src/lib/api/validate-cron-schedule.test.d.ts +2 -0
  179. package/dist/src/lib/api/validate-cron-schedule.test.d.ts.map +1 -0
  180. package/dist/src/lib/api/validate-cron-schedule.test.js +1 -0
  181. package/dist/src/lib/colors.js +1 -140
  182. package/dist/src/lib/colors.test.js +1 -173
  183. package/dist/src/lib/config.js +1 -14
  184. package/dist/src/lib/ctrl-or-command-character.js +1 -5
  185. package/dist/src/lib/fetcher/cookie-from-string-to-object.js +1 -12
  186. package/dist/src/lib/fetcher/cookie-from-string-to-object.test.js +1 -78
  187. package/dist/src/lib/fetcher/core.js +3 -48
  188. package/dist/src/lib/fetcher/helpers-server.js +1 -18
  189. package/dist/src/lib/fetcher/helpers.js +1 -16
  190. package/dist/src/lib/fetcher/helpers.test.js +1 -37
  191. package/dist/src/lib/fetcher/types.js +1 -1
  192. package/dist/src/lib/fetcher-client.js +1 -17
  193. package/dist/src/lib/fetcher.js +1 -33
  194. package/dist/src/lib/helpers/auto-form.js +1 -98
  195. package/dist/src/lib/helpers/auto-form.test.js +1 -336
  196. package/dist/src/lib/navigation.js +1 -11
  197. package/dist/src/lib/plugin.js +1 -3
  198. package/dist/src/lib/special-characters.js +1 -4
  199. package/dist/src/lib/special-characters.test.js +1 -56
  200. package/dist/src/lib/utils.js +1 -5
  201. package/dist/src/locales/en.json +308 -0
  202. package/dist/src/tests/setup.js +1 -6
  203. package/dist/src/views/admin/layouts/admin-layout.js +1 -73
  204. package/dist/src/views/admin/layouts/sidebar/nav/item.js +1 -98
  205. package/dist/src/views/admin/layouts/sidebar/nav/nav.js +1 -60
  206. package/dist/src/views/admin/layouts/sidebar/sidebar.js +1 -28
  207. package/dist/src/views/admin/layouts/sidebar/sign-out.js +1 -14
  208. package/dist/src/views/admin/layouts/user-bar/client.js +1 -69
  209. package/dist/src/views/admin/layouts/user-bar/user-bar.js +1 -32
  210. package/dist/src/views/admin/sign-in/sign-in-admin-view.js +1 -27
  211. package/dist/src/views/admin/views/core/advanced/cron/cron-table-view.js +1 -97
  212. package/dist/src/views/admin/views/core/advanced/cron/run-action/mutation-api.js +1 -23
  213. package/dist/src/views/admin/views/core/advanced/cron/run-action/run-action.js +1 -38
  214. package/dist/src/views/admin/views/core/dashboard/dashboard-admin-view.js +1 -44
  215. package/dist/src/views/admin/views/core/debug/actions/clear-cache/clear-cache.js +1 -35
  216. package/dist/src/views/admin/views/core/debug/actions/clear-cache/mutation-api.js +1 -5
  217. package/dist/src/views/admin/views/core/debug/system-logs/actions/more/content.js +1 -281
  218. package/dist/src/views/admin/views/core/debug/system-logs/actions/more/more.js +1 -67
  219. package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-status.js +1 -14
  220. package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-type-log.js +1 -30
  221. package/dist/src/views/admin/views/core/debug/system-logs/system-logs-view.js +1 -96
  222. package/dist/src/views/admin/views/core/test.js +1 -161
  223. package/dist/src/views/admin/views/core/users/users-admin-view.js +1 -89
  224. package/dist/src/views/auth/password-reset/change-password-form/form.js +1 -48
  225. package/dist/src/views/auth/password-reset/change-password-form/mutation-api.js +1 -22
  226. package/dist/src/views/auth/password-reset/change-password-form/use-form.js +1 -36
  227. package/dist/src/views/auth/password-reset/form/form.js +1 -104
  228. package/dist/src/views/auth/password-reset/form/mutation-api.js +1 -21
  229. package/dist/src/views/auth/password-reset/form/use-form.js +1 -33
  230. package/dist/src/views/auth/password-reset/password-reset-view.js +1 -37
  231. package/dist/src/views/auth/sign-in/form/form.js +1 -64
  232. package/dist/src/views/auth/sign-in/form/mutation-api.js +1 -33
  233. package/dist/src/views/auth/sign-in/form/use-form.js +1 -38
  234. package/dist/src/views/auth/sign-in/sign-in-view.js +1 -61
  235. package/dist/src/views/auth/sign-up/components/password-input.js +1 -96
  236. package/dist/src/views/auth/sign-up/email-confirmation-view.js +1 -55
  237. package/dist/src/views/auth/sign-up/form/form.js +1 -89
  238. package/dist/src/views/auth/sign-up/form/mutation-api.js +1 -30
  239. package/dist/src/views/auth/sign-up/form/use-form.js +1 -70
  240. package/dist/src/views/auth/sign-up/sign-up-view.js +1 -65
  241. package/dist/src/views/auth/sign-up/wrapper.js +1 -22
  242. package/dist/src/views/auth/sso/buttons/client.js +1 -22
  243. package/dist/src/views/auth/sso/buttons/mutation-api.js +1 -24
  244. package/dist/src/views/auth/sso/buttons/sso-buttons.js +1 -56
  245. package/dist/src/views/auth/sso/callback/callback-sso-view.js +1 -32
  246. package/dist/src/views/auth/sso/callback/client/client.js +1 -68
  247. package/dist/src/views/auth/sso/callback/client/mutation-api.js +1 -32
  248. package/dist/src/views/error/back-button.js +1 -14
  249. package/dist/src/views/error/error-view.js +1 -58
  250. package/dist/src/views/error/global-error-view.js +1 -50
  251. package/dist/src/views/layouts/provider.js +1 -44
  252. package/dist/src/views/layouts/root-layout.js +1 -25
  253. package/dist/src/views/layouts/theme/header/header.js +1 -38
  254. package/dist/src/views/layouts/theme/header/user/auth/auth.js +1 -33
  255. package/dist/src/views/layouts/theme/header/user/auth/client.js +1 -47
  256. package/dist/src/views/layouts/theme/header/user/auth/log-out-mutation-api.js +1 -27
  257. package/dist/src/views/layouts/theme/header/user/user.js +1 -32
  258. package/dist/src/views/layouts/theme/layout.js +1 -16
  259. package/dist/src/vitnode.config.js +1 -42
  260. package/dist/tsconfig.tsbuildinfo +1 -1
  261. package/dist/tsup.config.js +18 -0
  262. package/dist/vitest.config.d.ts.map +1 -1
  263. package/dist/vitest.config.js +49 -0
  264. package/eslint.config.mjs +2 -0
  265. package/package.json +6 -10
  266. package/dist/src/api/adapters/cron/node-cron.adapter.d.ts +0 -3
  267. package/dist/src/api/adapters/cron/node-cron.adapter.d.ts.map +0 -1
  268. package/dist/src/api/adapters/cron/node-cron.adapter.js +0 -11
  269. package/dist/src/api/adapters/email/nodemailer.d.ts +0 -10
  270. package/dist/src/api/adapters/email/nodemailer.d.ts.map +0 -1
  271. package/dist/src/api/adapters/email/nodemailer.js +0 -30
  272. package/dist/src/api/adapters/email/resend.d.ts +0 -6
  273. package/dist/src/api/adapters/email/resend.d.ts.map +0 -1
  274. package/dist/src/api/adapters/email/resend.js +0 -21
@@ -1,67 +1 @@
1
- import { swaggerUI } from "@hono/swagger-ui";
2
- import { cors } from "hono/cors";
3
- import { csrf } from "hono/csrf";
4
- import { HTTPException } from "hono/http-exception";
5
- import { newBuildPluginApiCore } from "./plugin.js";
6
- import { CONFIG_PLUGIN } from "../config.js";
7
- import { globalAdminMiddleware, globalMiddleware } from "./middlewares/global.middleware.js";
8
- import { rateLimiterMiddleware } from "./middlewares/rate-limiter.middleware.js";
9
- export function VitNodeAPI({ app, cors: corsOptions, csrf: csrfOptions, vitNodeApiConfig }) {
10
- app.doc("/swagger/doc", {
11
- openapi: "3.0.0",
12
- info: {
13
- version: CONFIG_PLUGIN.version,
14
- title: "VitNode API"
15
- }
16
- });
17
- app.use(cors(corsOptions));
18
- app.use(csrf(csrfOptions));
19
- app.use("*", rateLimiterMiddleware(vitNodeApiConfig.rateLimiter));
20
- app.get("/swagger", swaggerUI({
21
- url: "/api/swagger/doc"
22
- }));
23
- app.use("*", globalMiddleware({
24
- pathToMessages: vitNodeApiConfig.pathToMessages,
25
- email: vitNodeApiConfig.email,
26
- metadata: vitNodeApiConfig.metadata,
27
- authorization: vitNodeApiConfig.authorization,
28
- dbProvider: vitNodeApiConfig.dbProvider,
29
- captcha: vitNodeApiConfig.captcha,
30
- plugins: [
31
- newBuildPluginApiCore,
32
- ...vitNodeApiConfig.plugins
33
- ]
34
- }));
35
- app.use(async (c, next)=>{
36
- if (c.req.path.includes("/admin/")) {
37
- return await globalAdminMiddleware()(c, next);
38
- }
39
- return next();
40
- });
41
- if (vitNodeApiConfig.cronAdapter) {
42
- vitNodeApiConfig.cronAdapter.schedule();
43
- }
44
- [
45
- newBuildPluginApiCore,
46
- ...vitNodeApiConfig.plugins
47
- ].map((root)=>{
48
- app.route(`/${root.pluginId}`, root.hono);
49
- });
50
- app.onError(async (error, c)=>{
51
- if (error instanceof HTTPException) {
52
- return error.getResponse();
53
- }
54
- const errorMessage = error?.message ?? "Unknown error";
55
- try {
56
- const logger = c.get("log");
57
- if (logger) {
58
- await logger.error(`Unhandled error: ${errorMessage}`);
59
- }
60
- } catch {
61
- /* empty */ }
62
- return new Response(process.env.NODE_ENV === "development" ? errorMessage : "Internal Server Error", {
63
- status: 500
64
- });
65
- });
66
- return app;
67
- }
1
+ import{swaggerUI}from"@hono/swagger-ui";import{cors}from"hono/cors";import{csrf}from"hono/csrf";import{HTTPException}from"hono/http-exception";import{newBuildPluginApiCore}from"./plugin.js";import{CONFIG_PLUGIN}from"../config.js";import{globalAdminMiddleware,globalMiddleware}from"./middlewares/global.middleware.js";import{rateLimiterMiddleware}from"./middlewares/rate-limiter.middleware.js";export function VitNodeAPI({app,cors:corsOptions,csrf:csrfOptions,vitNodeApiConfig}){app.doc("/swagger/doc",{openapi:"3.0.0",info:{version:CONFIG_PLUGIN.version,title:"VitNode API"}});app.use(cors(corsOptions));app.use(csrf(csrfOptions));app.use("*",rateLimiterMiddleware(vitNodeApiConfig.rateLimiter));app.get("/swagger",swaggerUI({url:"/api/swagger/doc"}));app.use("*",globalMiddleware({pathToMessages:vitNodeApiConfig.pathToMessages,email:vitNodeApiConfig.email,metadata:vitNodeApiConfig.metadata,authorization:vitNodeApiConfig.authorization,dbProvider:vitNodeApiConfig.dbProvider,captcha:vitNodeApiConfig.captcha,plugins:[newBuildPluginApiCore,...vitNodeApiConfig.plugins]}));app.use(async(c,next)=>{if(c.req.path.includes("/admin/")){return await globalAdminMiddleware()(c,next)}return next()});if(vitNodeApiConfig.cronAdapter){vitNodeApiConfig.cronAdapter.schedule()}[newBuildPluginApiCore,...vitNodeApiConfig.plugins].map(root=>{app.route(`/${root.pluginId}`,root.hono)});app.onError(async(error,c)=>{if(error instanceof HTTPException){return error.getResponse()}const errorMessage=error?.message??"Unknown error";try{const logger=c.get("log");if(logger){await logger.error(`Unhandled error: ${errorMessage}`)}}catch{}return new Response(process.env.NODE_ENV==="development"?errorMessage:"Internal Server Error",{status:500})});return app}
@@ -1,43 +1 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- import { CONFIG } from "../../lib/config.js";
4
- export const checkPluginId = (pluginName)=>{
5
- if (!CONFIG.node_development) return null;
6
- const findMonorepoRoot = (startPath)=>{
7
- let currentPath = startPath;
8
- while(currentPath !== resolve(currentPath, "..")){
9
- const turboConfigPath = join(currentPath, "turbo.json");
10
- if (existsSync(turboConfigPath)) {
11
- return currentPath;
12
- }
13
- currentPath = resolve(currentPath, "..");
14
- }
15
- return null;
16
- };
17
- const findPluginPath = (pluginName)=>{
18
- const cwd = process.cwd();
19
- const monorepoRoot = findMonorepoRoot(cwd);
20
- // Check in current working directory first
21
- const cwdPluginPath = join(cwd, "node_modules", pluginName, "package.json");
22
- if (existsSync(cwdPluginPath)) {
23
- return cwdPluginPath;
24
- }
25
- // Check in monorepo root if it exists and is different from cwd
26
- if (monorepoRoot && monorepoRoot !== cwd) {
27
- const rootPluginPath = join(monorepoRoot, "node_modules", pluginName, "package.json");
28
- if (existsSync(rootPluginPath)) {
29
- return rootPluginPath;
30
- }
31
- }
32
- return null;
33
- };
34
- const path = findPluginPath(pluginName);
35
- if (!path) {
36
- throw new Error(`package.json file not found for plugin "${pluginName}". Please ensure you are using the correct plugin name as specified in its package.json.`);
37
- }
38
- const content = JSON.parse(readFileSync(path, "utf-8"));
39
- if (content.name !== pluginName) {
40
- throw new Error(`The plugin name in package.json (${content.name}) does not match the requested plugin name (${pluginName}). Please check the plugin name.`);
41
- }
42
- return content;
43
- };
1
+ import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";import{CONFIG}from"../../lib/config.js";export const checkPluginId=pluginName=>{if(!CONFIG.node_development)return null;const findMonorepoRoot=startPath=>{let currentPath=startPath;while(currentPath!==resolve(currentPath,"..")){const turboConfigPath=join(currentPath,"turbo.json");if(existsSync(turboConfigPath)){return currentPath}currentPath=resolve(currentPath,"..")}return null};const findPluginPath=pluginName=>{const cwd=process.cwd();const monorepoRoot=findMonorepoRoot(cwd);const cwdPluginPath=join(cwd,"node_modules",pluginName,"package.json");if(existsSync(cwdPluginPath)){return cwdPluginPath}if(monorepoRoot&&monorepoRoot!==cwd){const rootPluginPath=join(monorepoRoot,"node_modules",pluginName,"package.json");if(existsSync(rootPluginPath)){return rootPluginPath}}return null};const path=findPluginPath(pluginName);if(!path){throw new Error(`package.json file not found for plugin "${pluginName}". Please ensure you are using the correct plugin name as specified in its package.json.`)}const content=JSON.parse(readFileSync(path,"utf-8"));if(content.name!==pluginName){throw new Error(`The plugin name in package.json (${content.name}) does not match the requested plugin name (${pluginName}). Please check the plugin name.`)}return content};
@@ -1,22 +1 @@
1
- import { CONFIG } from "../../lib/config.js";
2
- export function buildCron({ name, schedule, handler, description }) {
3
- return {
4
- name,
5
- schedule,
6
- handler,
7
- description
8
- };
9
- }
10
- export const handleCronJobs = async ()=>{
11
- const url = new URL("/api/@vitnode/core/cron", CONFIG.api.origin);
12
- const headers = {
13
- "Content-Type": "application/json"
14
- };
15
- if (CONFIG.cronJobSecret) {
16
- headers.authorization = `Bearer ${CONFIG.cronJobSecret}`;
17
- }
18
- await fetch(url.toString(), {
19
- method: "POST",
20
- headers
21
- });
22
- };
1
+ import{CONFIG}from"../../lib/config.js";export function buildCron({name,schedule,handler,description}){return{name,schedule,handler,description}}export const handleCronJobs=async()=>{const url=new URL("/api/@vitnode/core/cron",CONFIG.api.origin);const headers={"Content-Type":"application/json"};if(CONFIG.cronJobSecret){headers.authorization=`Bearer ${CONFIG.cronJobSecret}`}await fetch(url.toString(),{method:"POST",headers})};
@@ -1,35 +1 @@
1
- /* eslint-disable no-console */ import { core_logs } from "../../database/logs.js";
2
- export const loggerMiddleware = (c)=>{
3
- const logToDbAndConsole = async (content, type)=>{
4
- const pluginId = c.get("plugin")?.id ?? "core";
5
- const ipAddress = c.get("ipAddress");
6
- await c.get("db").insert(core_logs).values({
7
- pluginId,
8
- type,
9
- content,
10
- ipAddress,
11
- method: c.req.method.toUpperCase(),
12
- path: c.req.path,
13
- userAgent: c.req.header("User-Agent"),
14
- statusCode: c.res.status,
15
- userId: c.get("user")?.id
16
- });
17
- const loggers = {
18
- debug: (msg)=>console.debug(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mDebug (${pluginId})\x1b[0m: ${msg}`),
19
- error: (msg)=>console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[31mError (${pluginId})\x1b[0m: ${msg}`),
20
- warn: (msg)=>console.warn(`\x1b[34m[VitNode]\x1b[0m \x1b[33mWarning (${pluginId})\x1b[0m: ${msg}`)
21
- };
22
- (loggers[type] ?? loggers.debug)(content);
23
- };
24
- return {
25
- debug: async (content)=>{
26
- await logToDbAndConsole(content, "debug");
27
- },
28
- error: async (content)=>{
29
- await logToDbAndConsole(content, "error");
30
- },
31
- warn: async (content)=>{
32
- await logToDbAndConsole(content, "warn");
33
- }
34
- };
35
- };
1
+ import{core_logs}from"../../database/logs.js";export const loggerMiddleware=c=>{const logToDbAndConsole=async(content,type)=>{const pluginId=c.get("plugin")?.id??"core";const ipAddress=c.get("ipAddress");await c.get("db").insert(core_logs).values({pluginId,type,content,ipAddress,method:c.req.method.toUpperCase(),path:c.req.path,userAgent:c.req.header("User-Agent"),statusCode:c.res.status,userId:c.get("user")?.id});const loggers={debug:msg=>console.debug(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mDebug (${pluginId})\x1b[0m: ${msg}`),error:msg=>console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[31mError (${pluginId})\x1b[0m: ${msg}`),warn:msg=>console.warn(`\x1b[34m[VitNode]\x1b[0m \x1b[33mWarning (${pluginId})\x1b[0m: ${msg}`)};(loggers[type]??loggers.debug)(content)};return{debug:async content=>{await logToDbAndConsole(content,"debug")},error:async content=>{await logToDbAndConsole(content,"error")},warn:async content=>{await logToDbAndConsole(content,"warn")}}};
@@ -1,22 +1 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- export function buildModule({ routes, pluginId, name, modules, cronJobs = [] }) {
3
- const hono = new OpenAPIHono();
4
- if (routes) {
5
- routes.forEach(({ handler, route })=>{
6
- hono.openapi(route, handler);
7
- });
8
- }
9
- if (modules) {
10
- modules.forEach((module)=>{
11
- hono.route(`/${module.name}`, module.hono);
12
- });
13
- }
14
- return {
15
- routes,
16
- pluginId,
17
- hono,
18
- name,
19
- modules,
20
- cronJobs
21
- };
22
- }
1
+ import{OpenAPIHono}from"@hono/zod-openapi";export function buildModule({routes,pluginId,name,modules,cronJobs=[]}){const hono=new OpenAPIHono;if(routes){routes.forEach(({handler,route})=>{hono.openapi(route,handler)})}if(modules){modules.forEach(module=>{hono.route(`/${module.name}`,module.hono)})}return{routes,pluginId,hono,name,modules,cronJobs}}
@@ -1,22 +1 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import { checkPluginId } from "./check-plugin-id.js";
3
- export function buildApiPlugin({ pluginId, modules = [] }) {
4
- // Run for checking if the plugin is valid
5
- checkPluginId(pluginId);
6
- const hono = new OpenAPIHono();
7
- const cronJobs = [];
8
- modules.forEach((handler)=>{
9
- hono.route(`/${handler.name}`, handler.hono);
10
- handler.cronJobs?.forEach((cron)=>{
11
- cronJobs.push({
12
- ...cron,
13
- module: handler.name
14
- });
15
- });
16
- });
17
- return {
18
- pluginId,
19
- hono,
20
- cronJobs
21
- };
22
- }
1
+ import{OpenAPIHono}from"@hono/zod-openapi";import{checkPluginId}from"./check-plugin-id.js";export function buildApiPlugin({pluginId,modules=[]}){checkPluginId(pluginId);const hono=new OpenAPIHono;const cronJobs=[];modules.forEach(handler=>{hono.route(`/${handler.name}`,handler.hono);handler.cronJobs?.forEach(cron=>{cronJobs.push({...cron,module:handler.name})})});return{pluginId,hono,cronJobs}}
@@ -1,27 +1 @@
1
- import { createRoute as createRouteHono } from "@hono/zod-openapi";
2
- import { captchaMiddleware } from "../middlewares/captcha.middleware.js";
3
- import { pluginMiddleware } from "../middlewares/global.middleware.js";
4
- export const buildRoute = ({ route, handler, pluginId })=>{
5
- const pluginTag = pluginId.split(/[-_]/).map((word)=>word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
6
- const tags = [
7
- pluginTag,
8
- ...route.tags ?? []
9
- ];
10
- return {
11
- route: createRouteHono({
12
- tags,
13
- middleware: [
14
- pluginMiddleware(pluginId),
15
- ...route.withCaptcha ? [
16
- captchaMiddleware()
17
- ] : [],
18
- ...Array.isArray(route.middleware) ? route.middleware : route.middleware ? [
19
- route.middleware
20
- ] : []
21
- ],
22
- ...route
23
- }),
24
- handler: handler,
25
- pluginId
26
- };
27
- };
1
+ import{createRoute as createRouteHono}from"@hono/zod-openapi";import{captchaMiddleware}from"../middlewares/captcha.middleware.js";import{pluginMiddleware}from"../middlewares/global.middleware.js";export const buildRoute=({route,handler,pluginId})=>{const pluginTag=pluginId.split(/[-_]/).map(word=>word.charAt(0).toUpperCase()+word.slice(1)).join(" ");const tags=[pluginTag,...route.tags??[]];return{route:createRouteHono({tags,middleware:[pluginMiddleware(pluginId),...route.withCaptcha?[captchaMiddleware()]:[],...Array.isArray(route.middleware)?route.middleware:route.middleware?[route.middleware]:[]],...route}),handler:handler,pluginId}};
@@ -1,83 +1 @@
1
- import { z } from "@hono/zod-openapi";
2
- import { and, asc, count, desc, gt, lt } from "drizzle-orm";
3
- function parsePaginationParams(params) {
4
- const cursor = params.query.cursor ? parseInt(params.query.cursor, 10) : undefined;
5
- const first = params.query.first ? Math.min(parseInt(params.query.first, 10), 100) : undefined;
6
- const last = params.query.last ? Math.min(parseInt(params.query.last, 10), 100) : undefined;
7
- if (first !== undefined && last !== undefined) {
8
- throw new Error("Cannot specify both first and last");
9
- }
10
- if (first !== undefined && first < 0) {
11
- throw new Error("first must be positive");
12
- }
13
- if (last !== undefined && last < 0) {
14
- throw new Error("last must be positive");
15
- }
16
- return {
17
- cursor,
18
- first,
19
- last
20
- };
21
- }
22
- function getOrderFn(isForward, order) {
23
- if (isForward) {
24
- return order === "asc" ? asc : desc;
25
- }
26
- return order === "asc" ? desc : asc;
27
- }
28
- function buildWhereWithCursor(baseWhere, cursor, isForward, order, table, primaryCursor) {
29
- if (!cursor) return baseWhere;
30
- const cursorFilter = isForward && order === "asc" || !isForward && order === "desc" ? gt : lt;
31
- const cursorWhere = cursorFilter(table[primaryCursor.name], cursor);
32
- return baseWhere ? and(baseWhere, cursorWhere) : cursorWhere;
33
- }
34
- async function fetchTotalCount(c, table, where) {
35
- const [{ count: totalCount }] = await c.get("db").select({
36
- count: count()
37
- }).from(table).where(where);
38
- return totalCount;
39
- }
40
- export async function withPagination({ query, table, params, where: whereFromParams, primaryCursor, orderBy: orderByFromParams, c }) {
41
- const { cursor, first, last } = parsePaginationParams(params);
42
- const isForward = last === undefined;
43
- const orderFn = getOrderFn(isForward, orderByFromParams.order);
44
- const orderBy = orderFn(table[orderByFromParams.column.name]);
45
- const where = buildWhereWithCursor(whereFromParams, cursor, isForward, orderByFromParams.order, table, primaryCursor);
46
- const totalCount = await fetchTotalCount(c, table, whereFromParams);
47
- const limit = (first ?? last ?? 50) + 1;
48
- const edges = await query({
49
- limit,
50
- where,
51
- orderBy
52
- });
53
- const requested = first ?? last ?? edges.length;
54
- const hasMore = edges.length > requested;
55
- const slicedEdges = edges.slice(0, requested);
56
- const finalEdges = isForward ? slicedEdges : slicedEdges.reverse();
57
- const startCursor = finalEdges[0]?.[primaryCursor.name] ?? null;
58
- const endCursor = finalEdges.at(-1)?.[primaryCursor.name] ?? null;
59
- return {
60
- pageInfo: {
61
- totalCount,
62
- count: finalEdges.length,
63
- hasNextPage: isForward ? hasMore : !!cursor,
64
- hasPreviousPage: isForward ? !!cursor : hasMore,
65
- startCursor,
66
- endCursor
67
- },
68
- edges: finalEdges
69
- };
70
- }
71
- export const zodPaginationPageInfo = z.object({
72
- totalCount: z.number(),
73
- count: z.number(),
74
- hasNextPage: z.boolean(),
75
- hasPreviousPage: z.boolean(),
76
- startCursor: z.number().nullable(),
77
- endCursor: z.number().nullable()
78
- });
79
- export const zodPaginationQuery = z.object({
80
- cursor: z.string().optional(),
81
- first: z.string().optional(),
82
- last: z.string().optional()
83
- });
1
+ import{z}from"@hono/zod-openapi";import{and,asc,count,desc,gt,lt}from"drizzle-orm";function parsePaginationParams(params){const cursor=params.query.cursor?parseInt(params.query.cursor,10):undefined;const first=params.query.first?Math.min(parseInt(params.query.first,10),100):undefined;const last=params.query.last?Math.min(parseInt(params.query.last,10),100):undefined;if(first!==undefined&&last!==undefined){throw new Error("Cannot specify both first and last")}if(first!==undefined&&first<0){throw new Error("first must be positive")}if(last!==undefined&&last<0){throw new Error("last must be positive")}return{cursor,first,last}}function getOrderFn(isForward,order){if(isForward){return order==="asc"?asc:desc}return order==="asc"?desc:asc}function buildWhereWithCursor(baseWhere,cursor,isForward,order,table,primaryCursor){if(!cursor)return baseWhere;const cursorFilter=isForward&&order==="asc"||!isForward&&order==="desc"?gt:lt;const cursorWhere=cursorFilter(table[primaryCursor.name],cursor);return baseWhere?and(baseWhere,cursorWhere):cursorWhere}async function fetchTotalCount(c,table,where){const[{count:totalCount}]=await c.get("db").select({count:count()}).from(table).where(where);return totalCount}export async function withPagination({query,table,params,where:whereFromParams,primaryCursor,orderBy:orderByFromParams,c}){const{cursor,first,last}=parsePaginationParams(params);const isForward=last===undefined;const orderFn=getOrderFn(isForward,orderByFromParams.order);const orderBy=orderFn(table[orderByFromParams.column.name]);const where=buildWhereWithCursor(whereFromParams,cursor,isForward,orderByFromParams.order,table,primaryCursor);const totalCount=await fetchTotalCount(c,table,whereFromParams);const limit=(first??last??50)+1;const edges=await query({limit,where,orderBy});const requested=first??last??edges.length;const hasMore=edges.length>requested;const slicedEdges=edges.slice(0,requested);const finalEdges=isForward?slicedEdges:slicedEdges.reverse();const startCursor=finalEdges[0]?.[primaryCursor.name]??null;const endCursor=finalEdges.at(-1)?.[primaryCursor.name]??null;return{pageInfo:{totalCount,count:finalEdges.length,hasNextPage:isForward?hasMore:!!cursor,hasPreviousPage:isForward?!!cursor:hasMore,startCursor,endCursor},edges:finalEdges}}export const zodPaginationPageInfo=z.object({totalCount:z.number(),count:z.number(),hasNextPage:z.boolean(),hasPreviousPage:z.boolean(),startCursor:z.number().nullable(),endCursor:z.number().nullable()});export const zodPaginationQuery=z.object({cursor:z.string().optional(),first:z.string().optional(),last:z.string().optional()});
@@ -1,63 +1 @@
1
- import { HTTPException } from "hono/http-exception";
2
- const getResFromReCaptcha = async ({ token, userIp, captchaConfig })=>{
3
- if (captchaConfig.type === "cloudflare_turnstile") {
4
- const res = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
5
- method: "POST",
6
- body: JSON.stringify({
7
- secret: captchaConfig.secretKey,
8
- response: token,
9
- remoteip: userIp
10
- }),
11
- headers: {
12
- "Content-Type": "application/json"
13
- }
14
- });
15
- const data = await res.json();
16
- return {
17
- success: data.success,
18
- score: data.success ? 1 : 0,
19
- "error-codes": data["error-codes"]
20
- };
21
- }
22
- if (captchaConfig.type === "recaptcha_v3") {
23
- const res = await fetch(`https://www.google.com/recaptcha/api/siteverify?secret=${captchaConfig.secretKey}&response=${token}&remoteip=${userIp}`, {
24
- method: "POST"
25
- });
26
- const data = await res.json();
27
- return {
28
- success: data.success,
29
- score: data.score ?? 0,
30
- "error-codes": data["error-codes"]
31
- };
32
- }
33
- return {
34
- success: false,
35
- score: 0
36
- };
37
- };
38
- export const captchaMiddleware = ()=>{
39
- return async (c, next)=>{
40
- const token = c.req.header("x-vitnode-captcha-token");
41
- const captchaConfig = c.get("core").captcha;
42
- if (!captchaConfig) {
43
- await next();
44
- return;
45
- }
46
- if (!token) {
47
- throw new HTTPException(400, {
48
- message: "Captcha token is required"
49
- });
50
- }
51
- const res = await getResFromReCaptcha({
52
- token,
53
- userIp: c.get("ipAddress"),
54
- captchaConfig
55
- });
56
- if (!res.success || res.score < 0.5) {
57
- throw new HTTPException(400, {
58
- message: "Captcha validation failed"
59
- });
60
- }
61
- await next();
62
- };
63
- };
1
+ import{HTTPException}from"hono/http-exception";const getResFromReCaptcha=async({token,userIp,captchaConfig})=>{if(captchaConfig.type==="cloudflare_turnstile"){const res=await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify",{method:"POST",body:JSON.stringify({secret:captchaConfig.secretKey,response:token,remoteip:userIp}),headers:{"Content-Type":"application/json"}});const data=await res.json();return{success:data.success,score:data.success?1:0,"error-codes":data["error-codes"]}}if(captchaConfig.type==="recaptcha_v3"){const res=await fetch(`https://www.google.com/recaptcha/api/siteverify?secret=${captchaConfig.secretKey}&response=${token}&remoteip=${userIp}`,{method:"POST"});const data=await res.json();return{success:data.success,score:data.score??0,"error-codes":data["error-codes"]}}return{success:false,score:0}};export const captchaMiddleware=()=>{return async(c,next)=>{const token=c.req.header("x-vitnode-captcha-token");const captchaConfig=c.get("core").captcha;if(!captchaConfig){await next();return}if(!token){throw new HTTPException(400,{message:"Captcha token is required"})}const res=await getResFromReCaptcha({token,userIp:c.get("ipAddress"),captchaConfig});if(!res.success||res.score<.5){throw new HTTPException(400,{message:"Captcha validation failed"})}await next()}};
@@ -1,19 +1 @@
1
- import { HTTPException } from "hono/http-exception";
2
- export const cronAuthMiddleware = ()=>{
3
- return async (c, next)=>{
4
- const cronSecret = c.get("core").cronSecret;
5
- if (!cronSecret) {
6
- throw new HTTPException(403, {
7
- message: "Cron access not configured"
8
- });
9
- }
10
- const authHeader = c.req.header("authorization");
11
- const providedSecret = authHeader?.replace("Bearer ", "");
12
- if (providedSecret !== cronSecret) {
13
- throw new HTTPException(403, {
14
- message: "Invalid cron authorization"
15
- });
16
- }
17
- await next();
18
- };
19
- };
1
+ import{HTTPException}from"hono/http-exception";export const cronAuthMiddleware=()=>{return async(c,next)=>{const cronSecret=c.get("core").cronSecret;if(!cronSecret){throw new HTTPException(403,{message:"Cron access not configured"})}const authHeader=c.req.header("authorization");const providedSecret=authHeader?.replace("Bearer ","");if(providedSecret!==cronSecret){throw new HTTPException(403,{message:"Invalid cron authorization"})}await next()}};
@@ -1,93 +1 @@
1
- import { HTTPException } from "hono/http-exception";
2
- import { EmailModel } from "../models/email.js";
3
- import { SessionModel } from "../models/session.js";
4
- import { SessionAdminModel } from "../models/session-admin.js";
5
- import { CONFIG } from "../../lib/config.js";
6
- import { loggerMiddleware } from "../lib/logger-middleware.js";
7
- export const globalMiddleware = ({ authorization, metadata, email, dbProvider, captcha, plugins, pathToMessages })=>{
8
- const pluginsMetadata = plugins.map((plugin)=>({
9
- id: plugin.pluginId
10
- }));
11
- const cronMetadata = plugins.flatMap((plugin)=>plugin.cronJobs.map((cronJob)=>({
12
- pluginId: plugin.pluginId,
13
- module: cronJob.module,
14
- name: cronJob.name,
15
- schedule: cronJob.schedule,
16
- handler: cronJob.handler,
17
- description: cronJob.description
18
- })));
19
- const ipHeaderKeys = [
20
- "x-forwarded-for",
21
- "x-real-ip",
22
- "cf-connecting-ip",
23
- "x-client-ip",
24
- "x-forwarded",
25
- "x-cluster-client-ip",
26
- "forwarded-for",
27
- "forwarded",
28
- "via",
29
- "remote-addr",
30
- "client-ip",
31
- "ip",
32
- "x-ip",
33
- "true-client-ip",
34
- "fastly-client-ip",
35
- "x-fastly-client-ip"
36
- ];
37
- return async (c, next)=>{
38
- let ipAddress;
39
- // Optimize: Try both header methods in a single loop
40
- for (const key of ipHeaderKeys){
41
- ipAddress = c.req.header(key);
42
- if (ipAddress) break;
43
- ipAddress = c.req.raw.headers.get(key) ?? undefined;
44
- if (ipAddress) break;
45
- }
46
- // Fallback to localhost if nothing found
47
- c.set("ipAddress", ipAddress ?? "127.0.0.1");
48
- c.set("db", dbProvider);
49
- c.set("email", new EmailModel(c));
50
- c.set("core", {
51
- pathToMessages,
52
- metadata,
53
- email,
54
- authorization: {
55
- cookieName: authorization?.cookieName ?? "vitnode_auth",
56
- cookie_expires: authorization?.cookieExpires ?? 1000 * 60 * 60 * 24 * 90,
57
- ssoAdapters: authorization?.ssoAdapters ?? [],
58
- deviceCookieName: authorization?.deviceCookieName ?? "vitnode_device",
59
- deviceCookieExpires: authorization?.deviceCookieExpires ?? 1000 * 60 * 60 * 24 * 365,
60
- adminCookieName: authorization?.adminCookieName ?? "vitnode_auth_admin",
61
- adminCookieExpires: authorization?.adminCookieExpires ?? 1000 * 60 * 60 * 24 * 1,
62
- cookieSecure: authorization?.cookieSecure ?? true
63
- },
64
- captcha,
65
- cronSecret: CONFIG.cronJobSecret,
66
- plugins: pluginsMetadata,
67
- cron: cronMetadata
68
- });
69
- const user = await new SessionModel(c).getUser();
70
- c.set("user", user);
71
- c.set("admin", null);
72
- c.set("log", loggerMiddleware(c));
73
- await next();
74
- };
75
- };
76
- export const pluginMiddleware = (pluginId)=>{
77
- return async (c, next)=>{
78
- c.set("plugin", {
79
- id: pluginId
80
- });
81
- await next();
82
- };
83
- };
84
- export const globalAdminMiddleware = ()=>{
85
- return async (c, next)=>{
86
- const user = await new SessionAdminModel(c).getUser();
87
- if (!user) throw new HTTPException(403);
88
- c.set("admin", {
89
- user
90
- });
91
- await next();
92
- };
93
- };
1
+ import{HTTPException}from"hono/http-exception";import{EmailModel}from"../models/email.js";import{SessionModel}from"../models/session.js";import{SessionAdminModel}from"../models/session-admin.js";import{CONFIG}from"../../lib/config.js";import{loggerMiddleware}from"../lib/logger-middleware.js";export const globalMiddleware=({authorization,metadata,email,dbProvider,captcha,plugins,pathToMessages})=>{const pluginsMetadata=plugins.map(plugin=>({id:plugin.pluginId}));const cronMetadata=plugins.flatMap(plugin=>plugin.cronJobs.map(cronJob=>({pluginId:plugin.pluginId,module:cronJob.module,name:cronJob.name,schedule:cronJob.schedule,handler:cronJob.handler,description:cronJob.description})));const ipHeaderKeys=["x-forwarded-for","x-real-ip","cf-connecting-ip","x-client-ip","x-forwarded","x-cluster-client-ip","forwarded-for","forwarded","via","remote-addr","client-ip","ip","x-ip","true-client-ip","fastly-client-ip","x-fastly-client-ip"];return async(c,next)=>{let ipAddress;for(const key of ipHeaderKeys){ipAddress=c.req.header(key);if(ipAddress)break;ipAddress=c.req.raw.headers.get(key)??undefined;if(ipAddress)break}c.set("ipAddress",ipAddress??"127.0.0.1");c.set("db",dbProvider);c.set("email",new EmailModel(c));c.set("core",{pathToMessages,metadata,email,authorization:{cookieName:authorization?.cookieName??"vitnode_auth",cookie_expires:authorization?.cookieExpires??1e3*60*60*24*90,ssoAdapters:authorization?.ssoAdapters??[],deviceCookieName:authorization?.deviceCookieName??"vitnode_device",deviceCookieExpires:authorization?.deviceCookieExpires??1e3*60*60*24*365,adminCookieName:authorization?.adminCookieName??"vitnode_auth_admin",adminCookieExpires:authorization?.adminCookieExpires??1e3*60*60*24*1,cookieSecure:authorization?.cookieSecure??true},captcha,cronSecret:CONFIG.cronJobSecret,plugins:pluginsMetadata,cron:cronMetadata});const user=await new SessionModel(c).getUser();c.set("user",user);c.set("admin",null);c.set("log",loggerMiddleware(c));await next()}};export const pluginMiddleware=pluginId=>{return async(c,next)=>{c.set("plugin",{id:pluginId});await next()}};export const globalAdminMiddleware=()=>{return async(c,next)=>{const user=await new SessionAdminModel(c).getUser();if(!user)throw new HTTPException(403);c.set("admin",{user});await next()}};
@@ -1,32 +1 @@
1
- import { RateLimiterMemory } from "rate-limiter-flexible";
2
- import { CONFIG } from "../../lib/config.js";
3
- const createRateLimiter = ({ keyPrefix, ...options })=>{
4
- // TODO: Add support for Redis or other storage options
5
- return new RateLimiterMemory({
6
- keyPrefix,
7
- points: options?.points ?? 80,
8
- duration: options?.duration ?? 60,
9
- ...options
10
- });
11
- };
12
- export const rateLimiterMiddleware = (options)=>{
13
- if (CONFIG.node_development) {
14
- // In development, we disable the rate limiter for easier testing
15
- return async (_c, next)=>{
16
- await next();
17
- };
18
- }
19
- const rateLimiter = createRateLimiter({
20
- ...options,
21
- keyPrefix: "vitnode-api-rate-limiter"
22
- });
23
- return async (c, next)=>{
24
- const key = c.get("ipAddress");
25
- try {
26
- await rateLimiter.consume(key);
27
- } catch {
28
- return c.text("Too Many Requests", 429);
29
- }
30
- await next();
31
- };
32
- };
1
+ import{RateLimiterMemory}from"rate-limiter-flexible";import{CONFIG}from"../../lib/config.js";const createRateLimiter=({keyPrefix,...options})=>{return new RateLimiterMemory({keyPrefix,points:options?.points??80,duration:options?.duration??60,...options})};export const rateLimiterMiddleware=options=>{if(CONFIG.node_development){return async(_c,next)=>{await next()}}const rateLimiter=createRateLimiter({...options,keyPrefix:"vitnode-api-rate-limiter"});return async(c,next)=>{const key=c.get("ipAddress");try{await rateLimiter.consume(key)}catch{return c.text("Too Many Requests",429)}await next()}};
@@ -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()}}