@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,122 +1 @@
1
- import { useLocale, useTranslations } from "next-intl";
2
- import { useTheme } from "next-themes";
3
- import React from "react";
4
- import { toast } from "sonner";
5
- import { usePathname } from "../lib/navigation.js";
6
- export const useCaptcha = (captcha)=>{
7
- const t = useTranslations("core.global.errors");
8
- const locale = useLocale();
9
- const { resolvedTheme } = useTheme();
10
- const [isReady, setIsReady] = React.useState(false);
11
- const [token, setToken] = React.useState("");
12
- const pathname = usePathname();
13
- const handleLoaded = ()=>{
14
- if (!captcha) return;
15
- const elementId = "vitnode_captcha";
16
- if (captcha.type === "cloudflare_turnstile" && window.turnstile) {
17
- window.turnstile.render(`#${elementId}`, {
18
- sitekey: captcha.siteKey,
19
- theme: resolvedTheme,
20
- language: locale,
21
- callback: (token)=>{
22
- setToken(token);
23
- // ✅ Set isReady to true ONLY when the token is received
24
- setIsReady(true);
25
- },
26
- // Optional: Add more callbacks for a better user experience
27
- "expired-callback": ()=>{
28
- onReset();
29
- }
30
- });
31
- } else {
32
- // For reCAPTCHA, the script being loaded means it's ready.
33
- setIsReady(true);
34
- }
35
- };
36
- const handleError = ()=>{
37
- toast.error(t("title"), {
38
- description: t("captcha_internal_error")
39
- });
40
- };
41
- React.useEffect(()=>{
42
- if (!captcha) {
43
- // If no captcha is required, consider it "ready"
44
- // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
45
- setIsReady(true);
46
- return;
47
- }
48
- // Reset state on captcha type change
49
- // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
50
- setIsReady(false);
51
- // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
52
- setToken("");
53
- const googleCaptchaDomain = `https://www.google.com/recaptcha/api.js?hl=${locale}`;
54
- const script = document.createElement("script");
55
- if (captcha.type === "cloudflare_turnstile") {
56
- script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
57
- } else if (captcha.type === "recaptcha_v3") {
58
- script.src = `${googleCaptchaDomain}&render=${captcha.siteKey}`;
59
- }
60
- if (!script.src) return;
61
- script.async = true;
62
- script.defer = true;
63
- document.body.appendChild(script);
64
- script.addEventListener("load", handleLoaded);
65
- script.addEventListener("error", handleError);
66
- return ()=>{
67
- script.removeEventListener("load", handleLoaded);
68
- script.removeEventListener("error", handleError);
69
- if (document.body.contains(script)) {
70
- document.body.removeChild(script);
71
- }
72
- // Clean up the captcha widget if it exists
73
- const widget = document.getElementById("vitnode_captcha");
74
- if (widget) {
75
- widget.innerHTML = "";
76
- }
77
- };
78
- }, [
79
- pathname,
80
- locale,
81
- captcha?.type,
82
- captcha?.siteKey
83
- ]);
84
- const onReset = ()=>{
85
- if (!captcha) return;
86
- if (captcha.type === "cloudflare_turnstile" && window.turnstile) {
87
- window.turnstile.reset();
88
- }
89
- setToken("");
90
- setIsReady(false);
91
- };
92
- const getToken = async ()=>{
93
- if (!captcha) return "";
94
- if (captcha.type === "recaptcha_v3") {
95
- return await new Promise((resolve)=>{
96
- const grecaptcha = window.grecaptcha;
97
- if (grecaptcha) {
98
- grecaptcha.ready(async ()=>{
99
- try {
100
- const token = await grecaptcha.execute(captcha.siteKey, {
101
- action: "submit"
102
- });
103
- resolve(token);
104
- } catch (error) {
105
- // eslint-disable-next-line no-console
106
- console.error("Captcha error", error);
107
- resolve("");
108
- }
109
- });
110
- } else {
111
- resolve("");
112
- }
113
- });
114
- }
115
- return token;
116
- };
117
- return {
118
- isReady,
119
- onReset,
120
- getToken
121
- };
122
- };
1
+ import{useLocale,useTranslations}from"next-intl";import{useTheme}from"next-themes";import React from"react";import{toast}from"sonner";import{usePathname}from"../lib/navigation.js";export const useCaptcha=captcha=>{const t=useTranslations("core.global.errors");const locale=useLocale();const{resolvedTheme}=useTheme();const[isReady,setIsReady]=React.useState(false);const[token,setToken]=React.useState("");const pathname=usePathname();const handleLoaded=()=>{if(!captcha)return;const elementId="vitnode_captcha";if(captcha.type==="cloudflare_turnstile"&&window.turnstile){window.turnstile.render(`#${elementId}`,{sitekey:captcha.siteKey,theme:resolvedTheme,language:locale,callback:token=>{setToken(token);setIsReady(true)},"expired-callback":()=>{onReset()}})}else{setIsReady(true)}};const handleError=()=>{toast.error(t("title"),{description:t("captcha_internal_error")})};React.useEffect(()=>{if(!captcha){setIsReady(true);return}setIsReady(false);setToken("");const googleCaptchaDomain=`https://www.google.com/recaptcha/api.js?hl=${locale}`;const script=document.createElement("script");if(captcha.type==="cloudflare_turnstile"){script.src="https://challenges.cloudflare.com/turnstile/v0/api.js"}else if(captcha.type==="recaptcha_v3"){script.src=`${googleCaptchaDomain}&render=${captcha.siteKey}`}if(!script.src)return;script.async=true;script.defer=true;document.body.appendChild(script);script.addEventListener("load",handleLoaded);script.addEventListener("error",handleError);return()=>{script.removeEventListener("load",handleLoaded);script.removeEventListener("error",handleError);if(document.body.contains(script)){document.body.removeChild(script)}const widget=document.getElementById("vitnode_captcha");if(widget){widget.innerHTML=""}}},[pathname,locale,captcha?.type,captcha?.siteKey]);const onReset=()=>{if(!captcha)return;if(captcha.type==="cloudflare_turnstile"&&window.turnstile){window.turnstile.reset()}setToken("");setIsReady(false)};const getToken=async()=>{if(!captcha)return"";if(captcha.type==="recaptcha_v3"){return await new Promise(resolve=>{const grecaptcha=window.grecaptcha;if(grecaptcha){grecaptcha.ready(async()=>{try{const token=await grecaptcha.execute(captcha.siteKey,{action:"submit"});resolve(token)}catch(error){console.error("Captcha error",error);resolve("")}})}else{resolve("")}})}return token};return{isReady,onReset,getToken}};
@@ -1,16 +1 @@
1
- import React from "react";
2
- const MOBILE_BREAKPOINT = 768;
3
- export function useIsMobile() {
4
- const [isMobile, setIsMobile] = React.useState(undefined);
5
- React.useEffect(()=>{
6
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
7
- const onChange = ()=>{
8
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
9
- };
10
- mql.addEventListener("change", onChange);
11
- // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
12
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
13
- return ()=>mql.removeEventListener("change", onChange);
14
- }, []);
15
- return !!isMobile;
16
- }
1
+ import React from"react";const MOBILE_BREAKPOINT=768;export function useIsMobile(){const[isMobile,setIsMobile]=React.useState(undefined);React.useEffect(()=>{const mql=window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT-1}px)`);const onChange=()=>{setIsMobile(window.innerWidth<MOBILE_BREAKPOINT)};mql.addEventListener("change",onChange);setIsMobile(window.innerWidth<MOBILE_BREAKPOINT);return()=>mql.removeEventListener("change",onChange)},[]);return!!isMobile}
@@ -1,13 +1 @@
1
- import { middlewareModule } from "../../api/modules/middleware/middleware.module.js";
2
- import { fetcher } from "../fetcher.js";
3
- export const getMiddlewareApi = async ()=>{
4
- const res = await fetcher(middlewareModule, {
5
- path: "/",
6
- method: "get",
7
- module: "middleware",
8
- options: {
9
- cache: "force-cache"
10
- }
11
- });
12
- return await res.json();
13
- };
1
+ import{middlewareModule}from"../../api/modules/middleware/middleware.module.js";import{fetcher}from"../fetcher.js";export const getMiddlewareApi=async()=>{const res=await fetcher(middlewareModule,{path:"/",method:"get",module:"middleware",options:{cache:"force-cache"}});return await res.json()};
@@ -1,14 +1 @@
1
- import cronParser from "cron-parser";
2
- export const getNextCronRunDate = (schedule, lastRun)=>{
3
- try {
4
- const options = {
5
- currentDate: lastRun ?? new Date(0)
6
- };
7
- const interval = cronParser.parse(schedule, options);
8
- return interval.next().toDate();
9
- } catch (err) {
10
- // eslint-disable-next-line no-console
11
- console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule for nextRun\x1b[0m: ${schedule}`, err);
12
- return null;
13
- }
14
- };
1
+ import cronParser from"cron-parser";export const getNextCronRunDate=(schedule,lastRun)=>{try{const options={currentDate:lastRun??new Date(0)};const interval=cronParser.parse(schedule,options);return interval.next().toDate()}catch(err){console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule for nextRun\x1b[0m: ${schedule}`,err);return null}};
@@ -1,55 +1 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { getNextCronRunDate } from "./get-next-cron-run-date.js";
3
- describe("getNextCronRunDate", ()=>{
4
- it("should return the next run date for a valid cron schedule with lastRun null", ()=>{
5
- const schedule = "0 0 * * *"; // Daily at midnight
6
- const lastRun = null;
7
- const result = getNextCronRunDate(schedule, lastRun);
8
- expect(result).toBeInstanceOf(Date);
9
- if (result) {
10
- expect(result.getHours()).toBe(0);
11
- expect(result.getMinutes()).toBe(0);
12
- }
13
- });
14
- it("should return the next run date for a valid cron schedule with lastRun provided", ()=>{
15
- const schedule = "0 0 * * *"; // Daily at midnight
16
- const lastRun = new Date("2023-01-01T12:00:00Z");
17
- const result = getNextCronRunDate(schedule, lastRun);
18
- expect(result).toBeInstanceOf(Date);
19
- if (result) {
20
- expect(result.getHours()).toBe(0);
21
- expect(result.getMinutes()).toBe(0);
22
- expect(result.getTime()).toBeGreaterThan(lastRun.getTime());
23
- }
24
- });
25
- it("should return null for an invalid cron schedule", ()=>{
26
- const schedule = "invalid cron";
27
- const lastRun = null;
28
- const consoleSpy = vi.spyOn(console, "error").mockImplementation(()=>{});
29
- const result = getNextCronRunDate(schedule, lastRun);
30
- expect(result).toBeNull();
31
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("[VitNode]"), expect.any(Error));
32
- consoleSpy.mockRestore();
33
- });
34
- it("should handle cron schedules with specific times", ()=>{
35
- const schedule = "30 14 * * 1"; // Every Monday at 14:30
36
- const lastRun = new Date("2023-01-01T10:00:00Z"); // Sunday
37
- const result = getNextCronRunDate(schedule, lastRun);
38
- expect(result).toBeInstanceOf(Date);
39
- if (result) {
40
- expect(result.getDay()).toBe(1); // Monday
41
- expect(result.getHours()).toBe(14);
42
- expect(result.getMinutes()).toBe(30);
43
- }
44
- });
45
- it("should return null when cron parsing throws an error", ()=>{
46
- // This test assumes cron-parser can throw for certain invalid inputs
47
- const schedule = "99 99 99 99 99"; // Invalid cron with out-of-range values
48
- const lastRun = null;
49
- const consoleSpy = vi.spyOn(console, "error").mockImplementation(()=>{});
50
- const result = getNextCronRunDate(schedule, lastRun);
51
- expect(result).toBeNull();
52
- expect(consoleSpy).toHaveBeenCalled();
53
- consoleSpy.mockRestore();
54
- });
55
- });
1
+ import{describe,expect,it,vi}from"vitest";import{getNextCronRunDate}from"./get-next-cron-run-date.js";describe("getNextCronRunDate",()=>{it("should return the next run date for a valid cron schedule with lastRun null",()=>{const schedule="0 0 * * *";const lastRun=null;const result=getNextCronRunDate(schedule,lastRun);expect(result).toBeInstanceOf(Date);if(result){expect(result.getHours()).toBe(0);expect(result.getMinutes()).toBe(0)}});it("should return the next run date for a valid cron schedule with lastRun provided",()=>{const schedule="0 0 * * *";const lastRun=new Date("2023-01-01T12:00:00Z");const result=getNextCronRunDate(schedule,lastRun);expect(result).toBeInstanceOf(Date);if(result){expect(result.getHours()).toBe(0);expect(result.getMinutes()).toBe(0);expect(result.getTime()).toBeGreaterThan(lastRun.getTime())}});it("should return null for an invalid cron schedule",()=>{const schedule="invalid cron";const lastRun=null;const consoleSpy=vi.spyOn(console,"error").mockImplementation(()=>{});const result=getNextCronRunDate(schedule,lastRun);expect(result).toBeNull();expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("[VitNode]"),expect.any(Error));consoleSpy.mockRestore()});it("should handle cron schedules with specific times",()=>{const schedule="30 14 * * 1";const lastRun=new Date("2023-01-01T10:00:00Z");const result=getNextCronRunDate(schedule,lastRun);expect(result).toBeInstanceOf(Date);if(result){expect(result.getDay()).toBe(1);expect(result.getHours()).toBe(14);expect(result.getMinutes()).toBe(30)}});it("should return null when cron parsing throws an error",()=>{const schedule="99 99 99 99 99";const lastRun=null;const consoleSpy=vi.spyOn(console,"error").mockImplementation(()=>{});const result=getNextCronRunDate(schedule,lastRun);expect(result).toBeNull();expect(consoleSpy).toHaveBeenCalled();consoleSpy.mockRestore()})});
@@ -1,19 +1 @@
1
- import { adminModule } from "../../api/modules/admin/admin.module.js";
2
- import { fetcher } from "../fetcher.js";
3
- import { redirect } from "../navigation.js";
4
- export const getSessionAdminApi = async ()=>{
5
- const res = await fetcher(adminModule, {
6
- path: "/session",
7
- method: "get",
8
- module: "admin",
9
- options: {
10
- cache: "force-cache"
11
- }
12
- });
13
- if (res.status !== 200) {
14
- await redirect("/admin");
15
- return;
16
- }
17
- const data = await res.json();
18
- return data;
19
- };
1
+ import{adminModule}from"../../api/modules/admin/admin.module.js";import{fetcher}from"../fetcher.js";import{redirect}from"../navigation.js";export const getSessionAdminApi=async()=>{const res=await fetcher(adminModule,{path:"/session",method:"get",module:"admin",options:{cache:"force-cache"}});if(res.status!==200){await redirect("/admin");return}const data=await res.json();return data};
@@ -1,14 +1 @@
1
- import { usersModule } from "../../api/modules/users/users.module.js";
2
- import { fetcher } from "../fetcher.js";
3
- export const getSessionApi = async ()=>{
4
- const res = await fetcher(usersModule, {
5
- path: "/session",
6
- method: "get",
7
- module: "users",
8
- options: {
9
- cache: "force-cache"
10
- }
11
- });
12
- const data = await res.json();
13
- return data;
14
- };
1
+ import{usersModule}from"../../api/modules/users/users.module.js";import{fetcher}from"../fetcher.js";export const getSessionApi=async()=>{const res=await fetcher(usersModule,{path:"/session",method:"get",module:"users",options:{cache:"force-cache"}});const data=await res.json();return data};
@@ -1,16 +1 @@
1
- import cronParser from "cron-parser";
2
- export const shouldCronJobRun = (schedule, lastRun)=>{
3
- try {
4
- const now = new Date();
5
- const options = {
6
- currentDate: lastRun ?? new Date(0)
7
- };
8
- const interval = cronParser.parse(schedule, options);
9
- const nextScheduledRun = interval.next().toDate();
10
- return nextScheduledRun <= now;
11
- } catch (err) {
12
- // eslint-disable-next-line no-console
13
- console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule\x1b[0m: ${schedule}`, err);
14
- return false;
15
- }
16
- };
1
+ import cronParser from"cron-parser";export const shouldCronJobRun=(schedule,lastRun)=>{try{const now=new Date;const options={currentDate:lastRun??new Date(0)};const interval=cronParser.parse(schedule,options);const nextScheduledRun=interval.next().toDate();return nextScheduledRun<=now}catch(err){console.error(`\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule\x1b[0m: ${schedule}`,err);return false}};
@@ -1,213 +1 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { shouldCronJobRun } from "./should-cron-job-run.js";
3
- describe("shouldCronJobRun", ()=>{
4
- beforeEach(()=>{
5
- // Reset any mocked console methods
6
- vi.clearAllMocks();
7
- });
8
- describe("valid cron schedules", ()=>{
9
- it("should return true when job has never run (lastRun is null)", ()=>{
10
- const schedule = "0 * * * *"; // Every hour
11
- const lastRun = null;
12
- const result = shouldCronJobRun(schedule, lastRun);
13
- expect(result).toBe(true);
14
- });
15
- it("should return true when enough time has passed since last run", ()=>{
16
- const schedule = "0 * * * *"; // Every hour
17
- const lastRun = new Date("2024-01-01T10:00:00Z");
18
- // Mock current time to be 2 hours later
19
- vi.useFakeTimers();
20
- vi.setSystemTime(new Date("2024-01-01T12:00:00Z"));
21
- const result = shouldCronJobRun(schedule, lastRun);
22
- expect(result).toBe(true);
23
- vi.useRealTimers();
24
- });
25
- it("should return false when not enough time has passed since last run", ()=>{
26
- const schedule = "0 * * * *"; // Every hour
27
- const lastRun = new Date("2024-01-01T11:30:00Z");
28
- // Mock current time to be 15 minutes later (not enough for hourly job)
29
- vi.useFakeTimers();
30
- vi.setSystemTime(new Date("2024-01-01T11:45:00Z"));
31
- const result = shouldCronJobRun(schedule, lastRun);
32
- expect(result).toBe(false);
33
- vi.useRealTimers();
34
- });
35
- it("should handle daily job correctly", ()=>{
36
- const schedule = "0 9 * * *"; // Every day at 9 AM
37
- const lastRun = new Date("2024-01-01T09:00:00Z");
38
- // Mock current time to be next day at 10 AM
39
- vi.useFakeTimers();
40
- vi.setSystemTime(new Date("2024-01-02T10:00:00Z"));
41
- const result = shouldCronJobRun(schedule, lastRun);
42
- expect(result).toBe(true);
43
- vi.useRealTimers();
44
- });
45
- it("should handle weekly job correctly", ()=>{
46
- const schedule = "0 9 * * 1"; // Every Monday at 9 AM
47
- const lastRun = new Date("2024-01-01T09:00:00Z"); // Monday
48
- // Mock current time to be next Monday
49
- vi.useFakeTimers();
50
- vi.setSystemTime(new Date("2024-01-08T10:00:00Z")); // Next Monday
51
- const result = shouldCronJobRun(schedule, lastRun);
52
- expect(result).toBe(true);
53
- vi.useRealTimers();
54
- });
55
- it("should handle monthly job correctly", ()=>{
56
- const schedule = "0 9 1 * *"; // First day of every month at 9 AM
57
- const lastRun = new Date("2024-01-01T09:00:00Z");
58
- // Mock current time to be next month
59
- vi.useFakeTimers();
60
- vi.setSystemTime(new Date("2024-02-01T10:00:00Z"));
61
- const result = shouldCronJobRun(schedule, lastRun);
62
- expect(result).toBe(true);
63
- vi.useRealTimers();
64
- });
65
- it("should handle every minute job correctly", ()=>{
66
- const schedule = "* * * * *"; // Every minute
67
- const lastRun = new Date("2024-01-01T10:00:00Z");
68
- // Mock current time to be 1 minute later
69
- vi.useFakeTimers();
70
- vi.setSystemTime(new Date("2024-01-01T10:01:00Z"));
71
- const result = shouldCronJobRun(schedule, lastRun);
72
- expect(result).toBe(true);
73
- vi.useRealTimers();
74
- });
75
- it("should handle every 5 minutes job correctly", ()=>{
76
- const schedule = "*/5 * * * *"; // Every 5 minutes
77
- const lastRun = new Date("2024-01-01T10:00:00Z");
78
- // Mock current time to be 3 minutes later (not enough)
79
- vi.useFakeTimers();
80
- vi.setSystemTime(new Date("2024-01-01T10:03:00Z"));
81
- const result = shouldCronJobRun(schedule, lastRun);
82
- expect(result).toBe(false);
83
- // Mock current time to be 5 minutes later (enough)
84
- vi.setSystemTime(new Date("2024-01-01T10:05:00Z"));
85
- const result2 = shouldCronJobRun(schedule, lastRun);
86
- expect(result2).toBe(true);
87
- vi.useRealTimers();
88
- });
89
- it("should handle complex cron expressions", ()=>{
90
- const schedule = "0 9-17 * * 1-5"; // 9 AM to 5 PM on weekdays
91
- const lastRun = new Date("2024-01-01T09:00:00Z"); // Monday 9 AM
92
- // Mock current time to be same day at 10 AM
93
- vi.useFakeTimers();
94
- vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));
95
- const result = shouldCronJobRun(schedule, lastRun);
96
- expect(result).toBe(true);
97
- vi.useRealTimers();
98
- });
99
- });
100
- describe("invalid cron schedules", ()=>{
101
- it("should return false and log error for invalid cron schedule", ()=>{
102
- const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(()=>{});
103
- const schedule = "invalid cron"; // Invalid schedule
104
- const lastRun = null;
105
- const result = shouldCronJobRun(schedule, lastRun);
106
- expect(result).toBe(false);
107
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("[VitNode]") && expect.stringContaining("Error parsing schedule") && expect.stringContaining("invalid cron"), expect.any(Error));
108
- consoleErrorSpy.mockRestore();
109
- });
110
- it("should return false for malformed cron expression", ()=>{
111
- const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(()=>{});
112
- const schedule = "60 * * * *"; // Invalid minute (should be 0-59)
113
- const lastRun = null;
114
- const result = shouldCronJobRun(schedule, lastRun);
115
- expect(result).toBe(false);
116
- expect(consoleErrorSpy).toHaveBeenCalled();
117
- consoleErrorSpy.mockRestore();
118
- });
119
- it("should return true for empty cron schedule (parsed as valid)", ()=>{
120
- // Note: cron-parser actually parses empty string as a valid cron expression
121
- const schedule = ""; // Empty schedule
122
- const lastRun = null;
123
- const result = shouldCronJobRun(schedule, lastRun);
124
- expect(result).toBe(true);
125
- });
126
- });
127
- describe("edge cases", ()=>{
128
- it("should handle lastRun being exactly at the next scheduled time", ()=>{
129
- const schedule = "0 * * * *"; // Every hour
130
- const lastRun = new Date("2024-01-01T10:00:00Z");
131
- // Mock current time to be exactly at the next scheduled time
132
- vi.useFakeTimers();
133
- vi.setSystemTime(new Date("2024-01-01T11:00:00Z"));
134
- const result = shouldCronJobRun(schedule, lastRun);
135
- expect(result).toBe(true);
136
- vi.useRealTimers();
137
- });
138
- it("should handle lastRun being in the future (clock skew)", ()=>{
139
- const schedule = "0 * * * *"; // Every hour
140
- const lastRun = new Date("2024-01-01T12:00:00Z"); // Future time
141
- // Mock current time to be earlier
142
- vi.useFakeTimers();
143
- vi.setSystemTime(new Date("2024-01-01T11:00:00Z"));
144
- const result = shouldCronJobRun(schedule, lastRun);
145
- expect(result).toBe(false);
146
- vi.useRealTimers();
147
- });
148
- it("should handle lastRun being epoch (new Date(0))", ()=>{
149
- const schedule = "0 * * * *"; // Every hour
150
- const lastRun = new Date(0); // Epoch time
151
- // Mock current time to be much later
152
- vi.useFakeTimers();
153
- vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));
154
- const result = shouldCronJobRun(schedule, lastRun);
155
- expect(result).toBe(true);
156
- vi.useRealTimers();
157
- });
158
- it("should handle yearly cron job", ()=>{
159
- const schedule = "0 0 1 1 *"; // January 1st at midnight
160
- const lastRun = new Date("2023-01-01T00:00:00Z");
161
- // Mock current time to be next year
162
- vi.useFakeTimers();
163
- vi.setSystemTime(new Date("2024-01-01T01:00:00Z"));
164
- const result = shouldCronJobRun(schedule, lastRun);
165
- expect(result).toBe(true);
166
- vi.useRealTimers();
167
- });
168
- it("should handle specific day of month with month", ()=>{
169
- const schedule = "0 12 15 6 *"; // June 15th at noon
170
- const lastRun = new Date("2023-06-15T12:00:00Z");
171
- // Mock current time to be next year same date
172
- vi.useFakeTimers();
173
- vi.setSystemTime(new Date("2024-06-15T13:00:00Z"));
174
- const result = shouldCronJobRun(schedule, lastRun);
175
- expect(result).toBe(true);
176
- vi.useRealTimers();
177
- });
178
- });
179
- describe("timezone handling", ()=>{
180
- it("should work with different timezone dates", ()=>{
181
- const schedule = "0 9 * * *"; // Every day at 9 AM
182
- const lastRun = new Date("2024-01-01T09:00:00+05:00"); // 9 AM in +5 timezone
183
- // Mock current time to be next day in UTC
184
- vi.useFakeTimers();
185
- vi.setSystemTime(new Date("2024-01-02T04:00:00Z")); // 9 AM in +5 timezone
186
- const result = shouldCronJobRun(schedule, lastRun);
187
- expect(result).toBe(true);
188
- vi.useRealTimers();
189
- });
190
- });
191
- describe("performance and boundary conditions", ()=>{
192
- it("should handle very old lastRun dates", ()=>{
193
- const schedule = "0 * * * *"; // Every hour
194
- const lastRun = new Date("1970-01-01T00:00:00Z"); // Very old date
195
- // Current time
196
- vi.useFakeTimers();
197
- vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));
198
- const result = shouldCronJobRun(schedule, lastRun);
199
- expect(result).toBe(true);
200
- vi.useRealTimers();
201
- });
202
- it("should handle leap year considerations", ()=>{
203
- const schedule = "0 0 29 2 *"; // February 29th (leap day)
204
- const lastRun = new Date("2020-02-29T00:00:00Z"); // Last leap year
205
- // Mock current time to be next leap year
206
- vi.useFakeTimers();
207
- vi.setSystemTime(new Date("2024-02-29T01:00:00Z"));
208
- const result = shouldCronJobRun(schedule, lastRun);
209
- expect(result).toBe(true);
210
- vi.useRealTimers();
211
- });
212
- });
213
- });
1
+ import{beforeEach,describe,expect,it,vi}from"vitest";import{shouldCronJobRun}from"./should-cron-job-run.js";describe("shouldCronJobRun",()=>{beforeEach(()=>{vi.clearAllMocks()});describe("valid cron schedules",()=>{it("should return true when job has never run (lastRun is null)",()=>{const schedule="0 * * * *";const lastRun=null;const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true)});it("should return true when enough time has passed since last run",()=>{const schedule="0 * * * *";const lastRun=new Date("2024-01-01T10:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T12:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should return false when not enough time has passed since last run",()=>{const schedule="0 * * * *";const lastRun=new Date("2024-01-01T11:30:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T11:45:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(false);vi.useRealTimers()});it("should handle daily job correctly",()=>{const schedule="0 9 * * *";const lastRun=new Date("2024-01-01T09:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-02T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle weekly job correctly",()=>{const schedule="0 9 * * 1";const lastRun=new Date("2024-01-01T09:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-08T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle monthly job correctly",()=>{const schedule="0 9 1 * *";const lastRun=new Date("2024-01-01T09:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-02-01T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle every minute job correctly",()=>{const schedule="* * * * *";const lastRun=new Date("2024-01-01T10:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T10:01:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle every 5 minutes job correctly",()=>{const schedule="*/5 * * * *";const lastRun=new Date("2024-01-01T10:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T10:03:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(false);vi.setSystemTime(new Date("2024-01-01T10:05:00Z"));const result2=shouldCronJobRun(schedule,lastRun);expect(result2).toBe(true);vi.useRealTimers()});it("should handle complex cron expressions",()=>{const schedule="0 9-17 * * 1-5";const lastRun=new Date("2024-01-01T09:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()})});describe("invalid cron schedules",()=>{it("should return false and log error for invalid cron schedule",()=>{const consoleErrorSpy=vi.spyOn(console,"error").mockImplementation(()=>{});const schedule="invalid cron";const lastRun=null;const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(false);expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("[VitNode]")&&expect.stringContaining("Error parsing schedule")&&expect.stringContaining("invalid cron"),expect.any(Error));consoleErrorSpy.mockRestore()});it("should return false for malformed cron expression",()=>{const consoleErrorSpy=vi.spyOn(console,"error").mockImplementation(()=>{});const schedule="60 * * * *";const lastRun=null;const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(false);expect(consoleErrorSpy).toHaveBeenCalled();consoleErrorSpy.mockRestore()});it("should return true for empty cron schedule (parsed as valid)",()=>{const schedule="";const lastRun=null;const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true)})});describe("edge cases",()=>{it("should handle lastRun being exactly at the next scheduled time",()=>{const schedule="0 * * * *";const lastRun=new Date("2024-01-01T10:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T11:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle lastRun being in the future (clock skew)",()=>{const schedule="0 * * * *";const lastRun=new Date("2024-01-01T12:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T11:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(false);vi.useRealTimers()});it("should handle lastRun being epoch (new Date(0))",()=>{const schedule="0 * * * *";const lastRun=new Date(0);vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle yearly cron job",()=>{const schedule="0 0 1 1 *";const lastRun=new Date("2023-01-01T00:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T01:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle specific day of month with month",()=>{const schedule="0 12 15 6 *";const lastRun=new Date("2023-06-15T12:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-06-15T13:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()})});describe("timezone handling",()=>{it("should work with different timezone dates",()=>{const schedule="0 9 * * *";const lastRun=new Date("2024-01-01T09:00:00+05:00");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-02T04:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()})});describe("performance and boundary conditions",()=>{it("should handle very old lastRun dates",()=>{const schedule="0 * * * *";const lastRun=new Date("1970-01-01T00:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-01-01T10:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()});it("should handle leap year considerations",()=>{const schedule="0 0 29 2 *";const lastRun=new Date("2020-02-29T00:00:00Z");vi.useFakeTimers();vi.setSystemTime(new Date("2024-02-29T01:00:00Z"));const result=shouldCronJobRun(schedule,lastRun);expect(result).toBe(true);vi.useRealTimers()})})});
@@ -0,0 +1,2 @@
1
+ export declare function validateCronSchedule(schedule: string): boolean;
2
+ //# sourceMappingURL=validate-cron-schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-cron-schedule.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/validate-cron-schedule.ts"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA+C9D"}
@@ -0,0 +1 @@
1
+ export function validateCronSchedule(schedule){if(!schedule||typeof schedule!=="string"){return false}const trimmedSchedule=schedule.trim();if(!trimmedSchedule){return false}const parts=trimmedSchedule.split(/\s+/);if(parts.length!==5&&parts.length!==6){return false}const fieldConfigs=parts.length===6?[{name:"second",min:0,max:59},{name:"minute",min:0,max:59},{name:"hour",min:0,max:23},{name:"day",min:1,max:31},{name:"month",min:1,max:12},{name:"weekday",min:0,max:7}]:[{name:"minute",min:0,max:59},{name:"hour",min:0,max:23},{name:"day",min:1,max:31},{name:"month",min:1,max:12},{name:"weekday",min:0,max:7}];for(let i=0;i<parts.length;i++){const part=parts[i];const config=fieldConfigs[i];if(!validateCronField(part,config.min,config.max)){return false}}return true}function validateCronField(field,min,max){if(field==="*"){return true}if(field.includes(",")){const listItems=field.split(",");return listItems.every(item=>validateCronField(item.trim(),min,max))}if(field.includes("/")){const[range,step]=field.split("/");const stepNum=parseInt(step,10);if(isNaN(stepNum)||stepNum<=0||stepNum>max){return false}if(range==="*"){return true}return validateCronField(range,min,max)}if(field.includes("-")){const[start,end]=field.split("-");const startNum=parseInt(start,10);const endNum=parseInt(end,10);if(isNaN(startNum)||isNaN(endNum)||startNum<min||startNum>max||endNum<min||endNum>max||startNum>endNum){return false}return true}const num=parseInt(field,10);if(isNaN(num)||num<min||num>max){return false}return true}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validate-cron-schedule.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-cron-schedule.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/validate-cron-schedule.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ import{describe,expect,it}from"vitest";import{validateCronSchedule}from"./validate-cron-schedule.js";describe("validateCronSchedule",()=>{describe("valid 5-field cron expressions",()=>{it("should validate wildcard expression",()=>{expect(validateCronSchedule("* * * * *")).toBe(true)});it("should validate specific time expressions",()=>{expect(validateCronSchedule("0 0 * * *")).toBe(true);expect(validateCronSchedule("30 14 * * 1")).toBe(true);expect(validateCronSchedule("0 0 1 * *")).toBe(true);expect(validateCronSchedule("0 0 1 1 *")).toBe(true);expect(validateCronSchedule("15 10 * * 5")).toBe(true)});it("should validate step expressions",()=>{expect(validateCronSchedule("*/5 * * * *")).toBe(true);expect(validateCronSchedule("*/15 * * * *")).toBe(true);expect(validateCronSchedule("0 */2 * * *")).toBe(true);expect(validateCronSchedule("0 0 */3 * *")).toBe(true);expect(validateCronSchedule("0-30/5 * * * *")).toBe(true)});it("should validate range expressions",()=>{expect(validateCronSchedule("0 9-17 * * *")).toBe(true);expect(validateCronSchedule("0 0 1-15 * *")).toBe(true);expect(validateCronSchedule("0 0 * 1-6 *")).toBe(true);expect(validateCronSchedule("0 0 * * 1-5")).toBe(true)});it("should validate list expressions",()=>{expect(validateCronSchedule("0 0,12 * * *")).toBe(true);expect(validateCronSchedule("0 0 1,15 * *")).toBe(true);expect(validateCronSchedule("0 0 * * 1,3,5")).toBe(true);expect(validateCronSchedule("0 9,12,15 * * *")).toBe(true)});it("should validate complex expressions",()=>{expect(validateCronSchedule("0-30/5 9-17 * * 1-5")).toBe(true);expect(validateCronSchedule("0 0,12 1,15 * *")).toBe(true);expect(validateCronSchedule("*/10 */2 * * *")).toBe(true)});it("should validate weekday 0 and 7 (both Sunday)",()=>{expect(validateCronSchedule("0 0 * * 0")).toBe(true);expect(validateCronSchedule("0 0 * * 7")).toBe(true)});it("should validate edge cases",()=>{expect(validateCronSchedule("59 23 31 12 7")).toBe(true);expect(validateCronSchedule("0 0 1 1 0")).toBe(true)})});describe("valid 6-field cron expressions (with seconds)",()=>{it("should validate wildcard expression with seconds",()=>{expect(validateCronSchedule("* * * * * *")).toBe(true)});it("should validate specific time expressions with seconds",()=>{expect(validateCronSchedule("0 0 0 * * *")).toBe(true);expect(validateCronSchedule("30 30 14 * * 1")).toBe(true);expect(validateCronSchedule("0 0 0 1 * *")).toBe(true)});it("should validate step expressions with seconds",()=>{expect(validateCronSchedule("*/5 * * * * *")).toBe(true);expect(validateCronSchedule("0 */5 * * * *")).toBe(true);expect(validateCronSchedule("*/30 0 * * * *")).toBe(true)});it("should validate range expressions with seconds",()=>{expect(validateCronSchedule("0-30 0 9-17 * * *")).toBe(true)});it("should validate list expressions with seconds",()=>{expect(validateCronSchedule("0,30 0 0,12 * * *")).toBe(true)})});describe("invalid cron expressions",()=>{it("should reject empty or non-string input",()=>{expect(validateCronSchedule("")).toBe(false);expect(validateCronSchedule(" ")).toBe(false);expect(validateCronSchedule(null)).toBe(false);expect(validateCronSchedule(undefined)).toBe(false);expect(validateCronSchedule(123)).toBe(false)});it("should reject wrong number of fields",()=>{expect(validateCronSchedule("* * *")).toBe(false);expect(validateCronSchedule("* * * *")).toBe(false);expect(validateCronSchedule("* * * * * * *")).toBe(false);expect(validateCronSchedule("* * * * * * * *")).toBe(false)});it("should reject invalid characters",()=>{expect(validateCronSchedule("a * * * *")).toBe(false);expect(validateCronSchedule("* b * * *")).toBe(false);expect(validateCronSchedule("* * c * *")).toBe(false);expect(validateCronSchedule("@ # $ % ^")).toBe(false);expect(validateCronSchedule("invalid cron")).toBe(false)});it("should reject out-of-range values",()=>{expect(validateCronSchedule("60 * * * *")).toBe(false);expect(validateCronSchedule("* 24 * * *")).toBe(false);expect(validateCronSchedule("* * 32 * *")).toBe(false);expect(validateCronSchedule("* * 0 * *")).toBe(false);expect(validateCronSchedule("* * * 13 *")).toBe(false);expect(validateCronSchedule("* * * 0 *")).toBe(false);expect(validateCronSchedule("* * * * 8")).toBe(false);expect(validateCronSchedule("-1 * * * *")).toBe(false)});it("should reject out-of-range values in 6-field format",()=>{expect(validateCronSchedule("60 * * * * *")).toBe(false);expect(validateCronSchedule("-1 * * * * *")).toBe(false)});it("should reject invalid ranges",()=>{expect(validateCronSchedule("10-5 * * * *")).toBe(false);expect(validateCronSchedule("* 20-10 * * *")).toBe(false);expect(validateCronSchedule("* * 60-70 * *")).toBe(false);expect(validateCronSchedule("0-60 * * * *")).toBe(false)});it("should reject invalid steps",()=>{expect(validateCronSchedule("*/0 * * * *")).toBe(false);expect(validateCronSchedule("*/-1 * * * *")).toBe(false);expect(validateCronSchedule("*/60 * * * *")).toBe(false);expect(validateCronSchedule("*/abc * * * *")).toBe(false);expect(validateCronSchedule("* */25 * * *")).toBe(false)});it("should reject invalid lists",()=>{expect(validateCronSchedule("0,60 * * * *")).toBe(false);expect(validateCronSchedule("* 0,24 * * *")).toBe(false);expect(validateCronSchedule("a,b,c * * * *")).toBe(false);expect(validateCronSchedule(",, * * * *")).toBe(false)});it("should reject malformed expressions",()=>{expect(validateCronSchedule("1--5 * * * *")).toBe(false);expect(validateCronSchedule("1//5 * * * *")).toBe(false);expect(validateCronSchedule("1- * * * *")).toBe(false);expect(validateCronSchedule("-5 * * * *")).toBe(false);expect(validateCronSchedule("1/ * * * *")).toBe(false);expect(validateCronSchedule("/5 * * * *")).toBe(false)})});describe("edge cases",()=>{it("should handle extra whitespace",()=>{expect(validateCronSchedule(" * * * * * ")).toBe(true);expect(validateCronSchedule("0 0 * * *")).toBe(true)});it("should reject mixed valid and invalid fields",()=>{expect(validateCronSchedule("0 0 * * invalid")).toBe(false);expect(validateCronSchedule("0 25 * * *")).toBe(false);expect(validateCronSchedule("* * * * * 60")).toBe(false)})})});