@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.
- package/dist/scripts/get-config.js +15 -0
- package/dist/scripts/plugin.js +188 -0
- package/dist/scripts/prepare-database.js +177 -0
- package/dist/scripts/prepare-plugins-files.js +134 -0
- package/dist/scripts/run-interactive-shell-command.js +21 -0
- package/dist/scripts/scripts.js +39 -13
- package/dist/scripts/shared/file-utils.js +203 -0
- package/dist/src/api/adapters/sso/discord.js +1 -77
- package/dist/src/api/adapters/sso/facebook.js +1 -76
- package/dist/src/api/adapters/sso/google.js +1 -87
- package/dist/src/api/config.js +1 -67
- package/dist/src/api/lib/check-plugin-id.js +1 -43
- package/dist/src/api/lib/cron.js +1 -22
- package/dist/src/api/lib/logger-middleware.js +1 -35
- package/dist/src/api/lib/module.js +1 -22
- package/dist/src/api/lib/plugin.js +1 -22
- package/dist/src/api/lib/route.js +1 -27
- package/dist/src/api/lib/with-pagination.js +1 -83
- package/dist/src/api/middlewares/captcha.middleware.js +1 -63
- package/dist/src/api/middlewares/cron-auth.middleware.js +1 -19
- package/dist/src/api/middlewares/global.middleware.d.ts.map +1 -1
- package/dist/src/api/middlewares/global.middleware.js +1 -104
- package/dist/src/api/middlewares/rate-limiter.middleware.js +1 -32
- package/dist/src/api/models/device.js +1 -61
- package/dist/src/api/models/email.d.ts.map +1 -1
- package/dist/src/api/models/email.js +1 -77
- package/dist/src/api/models/password.js +1 -36
- package/dist/src/api/models/session-admin.d.ts.map +1 -1
- package/dist/src/api/models/session-admin.js +1 -97
- package/dist/src/api/models/session.d.ts.map +1 -1
- package/dist/src/api/models/session.js +1 -76
- package/dist/src/api/models/sso.js +1 -120
- package/dist/src/api/models/user/get-user-by-id.js +1 -19
- package/dist/src/api/models/user/sign-in-with-passwords.js +1 -22
- package/dist/src/api/models/user/sign-up.js +1 -73
- package/dist/src/api/models/user.js +1 -8
- package/dist/src/api/modules/admin/admin.module.js +1 -19
- package/dist/src/api/modules/admin/advanced/advanced.admin.module.js +1 -11
- package/dist/src/api/modules/admin/advanced/cron/cron.admin.module.js +1 -12
- package/dist/src/api/modules/admin/advanced/cron/routes/get.route.js +1 -66
- package/dist/src/api/modules/admin/advanced/cron/routes/run.route.js +1 -95
- package/dist/src/api/modules/admin/debug/debug.admin.module.js +1 -10
- package/dist/src/api/modules/admin/debug/routes/logs.route.js +1 -94
- package/dist/src/api/modules/admin/routes/session.route.js +1 -47
- package/dist/src/api/modules/admin/users/routes/list.route.js +1 -79
- package/dist/src/api/modules/admin/users/routes/users.route.js +1 -84
- package/dist/src/api/modules/admin/users/users.admin.module.js +1 -10
- package/dist/src/api/modules/cron/cron/clean.cron.js +1 -24
- package/dist/src/api/modules/cron/cron.module.js +1 -14
- package/dist/src/api/modules/cron/helpers/process-cron-jobs.d.ts.map +1 -1
- package/dist/src/api/modules/cron/helpers/process-cron-jobs.js +1 -72
- package/dist/src/api/modules/cron/routes/cron.route.d.ts.map +1 -1
- package/dist/src/api/modules/cron/routes/cron.route.js +1 -95
- package/dist/src/api/modules/middleware/middleware.module.js +1 -10
- package/dist/src/api/modules/middleware/route.js +1 -49
- package/dist/src/api/modules/users/avatar-color.js +1 -43
- package/dist/src/api/modules/users/routes/change-password.route.js +1 -58
- package/dist/src/api/modules/users/routes/reset-passowrd.route.js +1 -91
- package/dist/src/api/modules/users/routes/session.route.js +1 -46
- package/dist/src/api/modules/users/routes/sign-in.route.js +1 -71
- package/dist/src/api/modules/users/routes/sign-out.route.js +1 -43
- package/dist/src/api/modules/users/routes/sign-up.route.js +1 -77
- package/dist/src/api/modules/users/routes/test.route.js +1 -33
- package/dist/src/api/modules/users/sso/routes/callback.route.js +1 -52
- package/dist/src/api/modules/users/sso/routes/create-url.route.js +1 -36
- package/dist/src/api/modules/users/sso/sso.module.js +1 -12
- package/dist/src/api/modules/users/users.module.js +1 -26
- package/dist/src/api/plugin.js +1 -15
- package/dist/src/app/login/page.js +1 -16
- package/dist/src/app/login/reset-password/page.js +1 -18
- package/dist/src/app/login/sso/[providerId]/page.js +1 -10
- package/dist/src/app/register/page.js +1 -16
- package/dist/src/app_admin/core/advanced/cron/page.js +1 -42
- package/dist/src/app_admin/core/debug/page.js +1 -45
- package/dist/src/app_admin/core/page.js +1 -5
- package/dist/src/app_admin/core/test/page.js +1 -5
- package/dist/src/app_admin/core/users/page.js +1 -38
- package/dist/src/components/avatar.js +1 -14
- package/dist/src/components/confirm-action/confirm-action-alert-dialog.js +1 -42
- package/dist/src/components/confirm-action/content.js +1 -31
- package/dist/src/components/date-format.js +1 -41
- package/dist/src/components/form/auto-form.js +1 -103
- package/dist/src/components/form/common/desc.js +1 -9
- package/dist/src/components/form/common/label.js +1 -18
- package/dist/src/components/form/fields/checkbox.js +1 -37
- package/dist/src/components/form/fields/combobox-async.js +1 -123
- package/dist/src/components/form/fields/combobox.js +1 -87
- package/dist/src/components/form/fields/input.js +1 -39
- package/dist/src/components/form/fields/radio-group.js +1 -50
- package/dist/src/components/form/fields/select.js +1 -58
- package/dist/src/components/form/fields/switch.js +1 -36
- package/dist/src/components/form/fields/textarea.js +1 -36
- package/dist/src/components/i18n-provider.js +1 -43
- package/dist/src/components/logo-vitnode.js +1 -119
- package/dist/src/components/switchers/langs/language-switcher.js +1 -47
- package/dist/src/components/switchers/themes/theme-switcher.js +1 -28
- package/dist/src/components/table/content.js +1 -81
- package/dist/src/components/table/data-table.js +1 -68
- package/dist/src/components/table/order-table-head.js +1 -50
- package/dist/src/components/table/pagination.js +1 -122
- package/dist/src/components/tiptap/extension.js +1 -33
- package/dist/src/components/tiptap/toolbar/actions/alignment-action.js +1 -91
- package/dist/src/components/tiptap/toolbar/actions/bold-action.js +1 -39
- package/dist/src/components/tiptap/toolbar/actions/headings-action.js +1 -93
- package/dist/src/components/tiptap/toolbar/actions/italic-action.js +1 -38
- package/dist/src/components/tiptap/toolbar/actions/list-action.js +1 -61
- package/dist/src/components/tiptap/toolbar/actions/text-format-more/text-format-more.js +1 -59
- package/dist/src/components/tiptap/toolbar/actions/underline-action.js +1 -38
- package/dist/src/components/tiptap/toolbar/actions/undo-redo-actions.js +1 -63
- package/dist/src/components/tiptap/toolbar/actions/utils/tooltip-shortcut.js +1 -11
- package/dist/src/components/tiptap/toolbar/tiptap-toolbar.js +1 -55
- package/dist/src/components/tiptap/toolbar/use-toolbar-editor.js +1 -5
- package/dist/src/components/ui/accordion.js +1 -46
- package/dist/src/components/ui/alert-dialog.js +1 -123
- package/dist/src/components/ui/alert.js +1 -39
- package/dist/src/components/ui/badge.js +1 -28
- package/dist/src/components/ui/button-client.js +1 -57
- package/dist/src/components/ui/button.js +1 -32
- package/dist/src/components/ui/card.js +1 -52
- package/dist/src/components/ui/checkbox.js +1 -20
- package/dist/src/components/ui/collapsible.js +1 -22
- package/dist/src/components/ui/command.js +1 -98
- package/dist/src/components/ui/context-menu.js +1 -140
- package/dist/src/components/ui/dialog.js +1 -189
- package/dist/src/components/ui/drawer.js +1 -83
- package/dist/src/components/ui/dropdown-menu.js +1 -141
- package/dist/src/components/ui/editor-content.js +1 -10
- package/dist/src/components/ui/editor.js +1 -35
- package/dist/src/components/ui/form.js +1 -148
- package/dist/src/components/ui/header-content.js +1 -30
- package/dist/src/components/ui/hover-card.js +1 -29
- package/dist/src/components/ui/input-otp.js +1 -48
- package/dist/src/components/ui/input.js +1 -11
- package/dist/src/components/ui/label.js +1 -12
- package/dist/src/components/ui/loader.js +1 -16
- package/dist/src/components/ui/menubar.js +1 -151
- package/dist/src/components/ui/navigation-menu.js +1 -82
- package/dist/src/components/ui/popover.js +1 -35
- package/dist/src/components/ui/progress.js +1 -19
- package/dist/src/components/ui/radio-group.js +1 -27
- package/dist/src/components/ui/scroll-area.js +1 -33
- package/dist/src/components/ui/select.js +1 -113
- package/dist/src/components/ui/separator.js +1 -14
- package/dist/src/components/ui/sheet.js +1 -92
- package/dist/src/components/ui/sidebar.js +1 -433
- package/dist/src/components/ui/skeleton.js +1 -10
- package/dist/src/components/ui/slider.js +1 -50
- package/dist/src/components/ui/sonner.js +1 -26
- package/dist/src/components/ui/switch.js +1 -16
- package/dist/src/components/ui/table.js +1 -63
- package/dist/src/components/ui/tabs.js +1 -33
- package/dist/src/components/ui/textarea.js +1 -10
- package/dist/src/components/ui/toggle-group.js +1 -45
- package/dist/src/components/ui/toggle.js +1 -34
- package/dist/src/components/ui/tooltip.js +1 -57
- package/dist/src/config.js +1 -4
- package/dist/src/database/admins.js +1 -74
- package/dist/src/database/cron.js +1 -22
- package/dist/src/database/languages.js +1 -52
- package/dist/src/database/logs.js +1 -34
- package/dist/src/database/moderators.js +1 -37
- package/dist/src/database/roles.js +1 -13
- package/dist/src/database/sessions.js +1 -60
- package/dist/src/database/users.js +1 -145
- package/dist/src/drizzle.config.js +1 -67
- package/dist/src/emails/default-template.js +1 -99
- package/dist/src/emails/reset-password.js +1 -132
- package/dist/src/emails/ui/button.js +1 -36
- package/dist/src/emails/ui/card.js +1 -39
- package/dist/src/hooks/use-before-unload.js +1 -30
- package/dist/src/hooks/use-captcha.js +1 -122
- package/dist/src/hooks/use-mobile.js +1 -16
- package/dist/src/lib/api/get-middleware-api.js +1 -13
- package/dist/src/lib/api/get-next-cron-run-date.js +1 -14
- package/dist/src/lib/api/get-next-cron-run-date.test.js +1 -55
- package/dist/src/lib/api/get-session-admin-api.js +1 -19
- package/dist/src/lib/api/get-session-api.js +1 -14
- package/dist/src/lib/api/should-cron-job-run.js +1 -16
- package/dist/src/lib/api/should-cron-job-run.test.js +1 -213
- package/dist/src/lib/api/validate-cron-schedule.d.ts +2 -0
- package/dist/src/lib/api/validate-cron-schedule.d.ts.map +1 -0
- package/dist/src/lib/api/validate-cron-schedule.js +1 -0
- package/dist/src/lib/api/validate-cron-schedule.test.d.ts +2 -0
- package/dist/src/lib/api/validate-cron-schedule.test.d.ts.map +1 -0
- package/dist/src/lib/api/validate-cron-schedule.test.js +1 -0
- package/dist/src/lib/colors.js +1 -140
- package/dist/src/lib/colors.test.js +1 -173
- package/dist/src/lib/config.js +1 -14
- package/dist/src/lib/ctrl-or-command-character.js +1 -5
- package/dist/src/lib/fetcher/cookie-from-string-to-object.js +1 -12
- package/dist/src/lib/fetcher/cookie-from-string-to-object.test.js +1 -78
- package/dist/src/lib/fetcher/core.js +3 -48
- package/dist/src/lib/fetcher/helpers-server.js +1 -18
- package/dist/src/lib/fetcher/helpers.js +1 -16
- package/dist/src/lib/fetcher/helpers.test.js +1 -37
- package/dist/src/lib/fetcher/types.js +1 -1
- package/dist/src/lib/fetcher-client.js +1 -17
- package/dist/src/lib/fetcher.js +1 -33
- package/dist/src/lib/helpers/auto-form.js +1 -98
- package/dist/src/lib/helpers/auto-form.test.js +1 -336
- package/dist/src/lib/navigation.d.ts +15 -13
- package/dist/src/lib/navigation.d.ts.map +1 -1
- package/dist/src/lib/navigation.js +1 -11
- package/dist/src/lib/plugin.js +1 -3
- package/dist/src/lib/special-characters.js +1 -4
- package/dist/src/lib/special-characters.test.js +1 -56
- package/dist/src/lib/utils.js +1 -5
- package/dist/src/locales/en.json +308 -0
- package/dist/src/tests/setup.js +1 -6
- package/dist/src/views/admin/layouts/admin-layout.js +1 -73
- package/dist/src/views/admin/layouts/sidebar/nav/item.js +1 -98
- package/dist/src/views/admin/layouts/sidebar/nav/nav.js +1 -60
- package/dist/src/views/admin/layouts/sidebar/sidebar.js +1 -28
- package/dist/src/views/admin/layouts/sidebar/sign-out.js +1 -14
- package/dist/src/views/admin/layouts/user-bar/client.js +1 -69
- package/dist/src/views/admin/layouts/user-bar/user-bar.js +1 -32
- package/dist/src/views/admin/sign-in/sign-in-admin-view.js +1 -27
- package/dist/src/views/admin/views/core/advanced/cron/cron-table-view.js +1 -97
- package/dist/src/views/admin/views/core/advanced/cron/run-action/mutation-api.js +1 -23
- package/dist/src/views/admin/views/core/advanced/cron/run-action/run-action.js +1 -38
- package/dist/src/views/admin/views/core/dashboard/dashboard-admin-view.js +1 -44
- package/dist/src/views/admin/views/core/debug/actions/clear-cache/clear-cache.js +1 -35
- package/dist/src/views/admin/views/core/debug/actions/clear-cache/mutation-api.js +1 -5
- package/dist/src/views/admin/views/core/debug/system-logs/actions/more/content.js +1 -281
- package/dist/src/views/admin/views/core/debug/system-logs/actions/more/more.js +1 -67
- package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-status.js +1 -14
- package/dist/src/views/admin/views/core/debug/system-logs/badges/badge-type-log.js +1 -30
- package/dist/src/views/admin/views/core/debug/system-logs/system-logs-view.js +1 -96
- package/dist/src/views/admin/views/core/test.js +1 -161
- package/dist/src/views/admin/views/core/users/users-admin-view.js +1 -89
- package/dist/src/views/auth/password-reset/change-password-form/form.js +1 -48
- package/dist/src/views/auth/password-reset/change-password-form/mutation-api.js +1 -22
- package/dist/src/views/auth/password-reset/change-password-form/use-form.js +1 -36
- package/dist/src/views/auth/password-reset/form/form.js +1 -104
- package/dist/src/views/auth/password-reset/form/mutation-api.js +1 -21
- package/dist/src/views/auth/password-reset/form/use-form.js +1 -33
- package/dist/src/views/auth/password-reset/password-reset-view.js +1 -37
- package/dist/src/views/auth/sign-in/form/form.js +1 -64
- package/dist/src/views/auth/sign-in/form/mutation-api.js +1 -33
- package/dist/src/views/auth/sign-in/form/use-form.d.ts.map +1 -1
- package/dist/src/views/auth/sign-in/form/use-form.js +1 -38
- package/dist/src/views/auth/sign-in/sign-in-view.js +1 -61
- package/dist/src/views/auth/sign-up/components/password-input.js +1 -96
- package/dist/src/views/auth/sign-up/email-confirmation-view.js +1 -55
- package/dist/src/views/auth/sign-up/form/form.js +1 -89
- package/dist/src/views/auth/sign-up/form/mutation-api.js +1 -30
- package/dist/src/views/auth/sign-up/form/use-form.js +1 -70
- package/dist/src/views/auth/sign-up/sign-up-view.js +1 -65
- package/dist/src/views/auth/sign-up/wrapper.js +1 -22
- package/dist/src/views/auth/sso/buttons/client.js +1 -22
- package/dist/src/views/auth/sso/buttons/mutation-api.js +1 -24
- package/dist/src/views/auth/sso/buttons/sso-buttons.js +1 -56
- package/dist/src/views/auth/sso/callback/callback-sso-view.js +1 -32
- package/dist/src/views/auth/sso/callback/client/client.js +1 -68
- package/dist/src/views/auth/sso/callback/client/mutation-api.js +1 -32
- package/dist/src/views/error/back-button.js +1 -14
- package/dist/src/views/error/error-view.js +1 -58
- package/dist/src/views/error/global-error-view.js +1 -50
- package/dist/src/views/layouts/provider.js +1 -44
- package/dist/src/views/layouts/root-layout.js +1 -25
- package/dist/src/views/layouts/theme/header/header.js +1 -38
- package/dist/src/views/layouts/theme/header/user/auth/auth.js +1 -33
- package/dist/src/views/layouts/theme/header/user/auth/client.js +1 -47
- package/dist/src/views/layouts/theme/header/user/auth/log-out-mutation-api.js +1 -27
- package/dist/src/views/layouts/theme/header/user/user.js +1 -32
- package/dist/src/views/layouts/theme/layout.js +1 -16
- package/dist/src/vitnode.config.d.ts.map +1 -1
- package/dist/src/vitnode.config.js +1 -42
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsup.config.js +18 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +49 -0
- package/eslint.config.mjs +2 -0
- package/package.json +40 -44
- package/dist/src/api/adapters/cron/node-cron.adapter.d.ts +0 -3
- package/dist/src/api/adapters/cron/node-cron.adapter.d.ts.map +0 -1
- package/dist/src/api/adapters/cron/node-cron.adapter.js +0 -11
- package/dist/src/api/adapters/email/nodemailer.d.ts +0 -10
- package/dist/src/api/adapters/email/nodemailer.d.ts.map +0 -1
- package/dist/src/api/adapters/email/nodemailer.js +0 -30
- package/dist/src/api/adapters/email/resend.d.ts +0 -6
- package/dist/src/api/adapters/email/resend.d.ts.map +0 -1
- package/dist/src/api/adapters/email/resend.js +0 -21
|
@@ -1,122 +1 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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 @@
|
|
|
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 @@
|
|
|
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)})})});
|