├── .nvmrc ├── .github ├── FUNDING.yml ├── pull_request_template.md ├── release.yaml ├── workflows │ ├── conventional-label.yaml │ └── build-lint-test.yml └── ISSUE_TEMPLATE │ └── feature_request.yml ├── packages ├── config │ ├── .npmignore │ ├── prettierrc.mjs │ └── tsconfig.json ├── vitnode │ ├── src │ │ ├── lib │ │ │ ├── tiptap │ │ │ │ └── toolbar │ │ │ │ │ └── actions │ │ │ │ │ └── bold-action.tsx │ │ │ ├── utils.ts │ │ │ ├── ctrl-or-command-character.tsx │ │ │ ├── api │ │ │ │ ├── get-middleware-api.ts │ │ │ │ ├── get-session-api.ts │ │ │ │ ├── get-session-admin-api.ts │ │ │ │ ├── get-next-cron-run-date.ts │ │ │ │ └── should-cron-job-run.ts │ │ │ ├── fetcher │ │ │ │ ├── helpers.ts │ │ │ │ ├── cookie-from-string-to-object.ts │ │ │ │ └── helpers-server.ts │ │ │ ├── config.ts │ │ │ ├── special-characters.ts │ │ │ ├── plugin.ts │ │ │ └── navigation.ts │ │ ├── config.ts │ │ ├── app_admin │ │ │ └── core │ │ │ │ ├── test │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ ├── views │ │ │ ├── admin │ │ │ │ ├── views │ │ │ │ │ └── core │ │ │ │ │ │ ├── debug │ │ │ │ │ │ ├── actions │ │ │ │ │ │ │ └── clear-cache │ │ │ │ │ │ │ │ └── mutation-api.ts │ │ │ │ │ │ └── system-logs │ │ │ │ │ │ │ └── badges │ │ │ │ │ │ │ ├── badge-status.tsx │ │ │ │ │ │ │ └── badge-type-log.tsx │ │ │ │ │ │ └── advanced │ │ │ │ │ │ └── cron │ │ │ │ │ │ └── run-action │ │ │ │ │ │ └── mutation-api.ts │ │ │ │ ├── layouts │ │ │ │ │ ├── sidebar │ │ │ │ │ │ ├── sign-out.tsx │ │ │ │ │ │ └── sidebar.tsx │ │ │ │ │ └── user-bar │ │ │ │ │ │ └── user-bar.tsx │ │ │ │ └── sign-in │ │ │ │ │ └── sign-in-admin-view.tsx │ │ │ ├── error │ │ │ │ └── back-button.tsx │ │ │ ├── layouts │ │ │ │ └── theme │ │ │ │ │ ├── layout.tsx │ │ │ │ │ └── header │ │ │ │ │ └── user │ │ │ │ │ ├── auth │ │ │ │ │ ├── log-out-mutation-api.ts │ │ │ │ │ └── auth.tsx │ │ │ │ │ └── user.tsx │ │ │ └── auth │ │ │ │ ├── password-reset │ │ │ │ ├── form │ │ │ │ │ ├── mutation-api.ts │ │ │ │ │ └── use-form.ts │ │ │ │ └── change-password-form │ │ │ │ │ └── mutation-api.ts │ │ │ │ ├── sso │ │ │ │ ├── buttons │ │ │ │ │ ├── mutation-api.ts │ │ │ │ │ └── client.tsx │ │ │ │ └── callback │ │ │ │ │ └── client │ │ │ │ │ └── mutation-api.ts │ │ │ │ └── sign-up │ │ │ │ ├── wrapper.tsx │ │ │ │ └── form │ │ │ │ └── mutation-api.ts │ │ ├── components │ │ │ ├── ui │ │ │ │ ├── editor-content.tsx │ │ │ │ ├── skeleton.tsx │ │ │ │ ├── loader.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── separator.tsx │ │ │ │ ├── textarea.tsx │ │ │ │ ├── progress.tsx │ │ │ │ ├── collapsible.tsx │ │ │ │ ├── sonner.tsx │ │ │ │ └── input.tsx │ │ │ ├── form │ │ │ │ └── common │ │ │ │ │ ├── desc.tsx │ │ │ │ │ └── label.tsx │ │ │ ├── tiptap │ │ │ │ ├── toolbar │ │ │ │ │ ├── use-toolbar-editor.ts │ │ │ │ │ └── actions │ │ │ │ │ │ └── utils │ │ │ │ │ │ └── tooltip-shortcut.tsx │ │ │ │ └── extension.ts │ │ │ ├── switchers │ │ │ │ └── themes │ │ │ │ │ └── theme-switcher.tsx │ │ │ ├── avatar.tsx │ │ │ └── confirm-action │ │ │ │ └── content.tsx │ │ ├── tests │ │ │ └── setup.ts │ │ ├── api │ │ │ ├── models │ │ │ │ ├── user.ts │ │ │ │ └── user │ │ │ │ │ ├── get-user-by-id.ts │ │ │ │ │ └── sign-in-with-passwords.ts │ │ │ ├── modules │ │ │ │ ├── middleware │ │ │ │ │ └── middleware.module.ts │ │ │ │ ├── admin │ │ │ │ │ ├── users │ │ │ │ │ │ └── users.admin.module.ts │ │ │ │ │ ├── debug │ │ │ │ │ │ └── debug.admin.module.ts │ │ │ │ │ ├── advanced │ │ │ │ │ │ ├── advanced.admin.module.ts │ │ │ │ │ │ └── cron │ │ │ │ │ │ │ └── cron.admin.module.ts │ │ │ │ │ └── admin.module.ts │ │ │ │ ├── cron │ │ │ │ │ └── cron.module.ts │ │ │ │ └── users │ │ │ │ │ ├── sso │ │ │ │ │ ├── sso.module.ts │ │ │ │ │ └── routes │ │ │ │ │ │ └── create-url.route.ts │ │ │ │ │ ├── routes │ │ │ │ │ └── test.route.ts │ │ │ │ │ ├── users.module.ts │ │ │ │ │ └── avatar-color.ts │ │ │ ├── plugin.ts │ │ │ ├── middlewares │ │ │ │ └── cron-auth.middleware.ts │ │ │ └── lib │ │ │ │ ├── plugin.ts │ │ │ │ └── cron.ts │ │ ├── tiptap.css │ │ ├── database │ │ │ ├── cron.ts │ │ │ ├── roles.ts │ │ │ └── logs.ts │ │ ├── app │ │ │ ├── login │ │ │ │ ├── sso │ │ │ │ │ └── [providerId] │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── reset-password │ │ │ │ │ └── page.tsx │ │ │ └── register │ │ │ │ └── page.tsx │ │ └── hooks │ │ │ ├── use-mobile.ts │ │ │ └── use-before-unload.ts │ ├── global.d.ts │ ├── config │ │ └── next.config.ts │ ├── .npmignore │ ├── tsup.config.ts │ ├── .swcrc │ ├── components.json │ ├── eslint.config.mjs │ ├── scripts │ │ └── run-interactive-shell-command.ts │ ├── README.md │ └── tsconfig.json ├── create-vitnode-app │ ├── .npmignore │ ├── copy-of-vitnode-app │ │ ├── monorepo │ │ │ ├── apps │ │ │ │ ├── web │ │ │ │ │ └── .env.example │ │ │ │ └── api │ │ │ │ │ └── .env.example │ │ │ └── .gitignore_template │ │ ├── api-single-app │ │ │ ├── .env.example │ │ │ └── src │ │ │ │ └── vitnode.api.config.ts │ │ ├── root │ │ │ ├── global.d.ts │ │ │ ├── next.config.ts │ │ │ ├── .env.example │ │ │ ├── src │ │ │ │ ├── app │ │ │ │ │ └── [locale] │ │ │ │ │ │ └── (main) │ │ │ │ │ │ └── layout.tsx │ │ │ │ ├── proxy.ts │ │ │ │ └── vitnode.config.ts │ │ │ └── tsconfig.json │ │ ├── eslint │ │ │ ├── .prettierrc.mjs │ │ │ └── eslint.config.mjs │ │ ├── api-bun │ │ │ └── src │ │ │ │ └── index.ts │ │ ├── docker │ │ │ └── docker-compose.yml │ │ ├── eslint-react │ │ │ └── eslint.config.mjs │ │ ├── api │ │ │ └── src │ │ │ │ ├── vitnode.api.config.ts │ │ │ │ └── index.ts │ │ └── .vscode │ │ │ └── settings.json │ ├── src │ │ ├── helpers │ │ │ ├── with-If.ts │ │ │ ├── get-package-json.ts │ │ │ ├── is-writeable.ts │ │ │ ├── packages-json.ts │ │ │ ├── get-vitnode-package-version.ts │ │ │ ├── validate-pkg.ts │ │ │ ├── get-package-manager-from-root.ts │ │ │ ├── get-available-package-managers.ts │ │ │ └── init-vitnode.ts │ │ ├── plugin │ │ │ └── questions.ts │ │ ├── prepare │ │ │ └── prepare.ts │ │ └── create │ │ │ └── package-versions.ts │ ├── copy-of-vitnode-plugin │ │ └── root │ │ │ ├── global.d.ts │ │ │ ├── npmignore.template │ │ │ ├── .swcrc │ │ │ └── tsconfig.json │ ├── tsconfig.json │ └── eslint.config.mjs ├── node-cron │ ├── .npmignore │ ├── src │ │ └── index.ts │ ├── eslint.config.mjs │ ├── .swcrc │ └── tsconfig.json ├── nodemailer │ ├── .npmignore │ ├── eslint.config.mjs │ ├── .swcrc │ └── tsconfig.json └── resend │ ├── .npmignore │ ├── eslint.config.mjs │ ├── .swcrc │ ├── tsconfig.json │ └── src │ └── index.ts ├── apps ├── docs │ ├── migrations │ │ ├── 0001_common_spirit.sql │ │ ├── 0002_public_millenium_guard.sql │ │ └── meta │ │ │ └── _journal.json │ ├── content │ │ └── docs │ │ │ ├── dev │ │ │ ├── advanced │ │ │ │ └── meta.json │ │ │ ├── deployments │ │ │ │ ├── cloud │ │ │ │ │ └── meta.json │ │ │ │ ├── meta.json │ │ │ │ └── self-hosted.mdx │ │ │ ├── captcha │ │ │ │ ├── meta.json │ │ │ │ ├── recaptcha.png │ │ │ │ ├── cloudflare.png │ │ │ │ └── captcha_preview.png │ │ │ ├── sso │ │ │ │ ├── meta.json │ │ │ │ ├── facebook │ │ │ │ │ ├── 1.png │ │ │ │ │ ├── 2.png │ │ │ │ │ └── 3.png │ │ │ │ └── index.mdx │ │ │ ├── cron │ │ │ │ └── meta.json │ │ │ ├── api │ │ │ │ └── meta.json │ │ │ ├── i18n │ │ │ │ ├── meta.json │ │ │ │ └── index.mdx │ │ │ ├── email │ │ │ │ ├── components │ │ │ │ │ ├── meta.json │ │ │ │ │ ├── card-preview.png │ │ │ │ │ ├── button-preview.png │ │ │ │ │ ├── card.mdx │ │ │ │ │ └── button.mdx │ │ │ │ ├── default_template_preview.png │ │ │ │ └── meta.json │ │ │ ├── not-found.mdx │ │ │ ├── meta.json │ │ │ ├── swagger.mdx │ │ │ └── index.mdx │ │ │ ├── ui │ │ │ ├── hooks │ │ │ │ ├── meta.json │ │ │ │ ├── use-mobile.mdx │ │ │ │ └── use-before-unload.mdx │ │ │ ├── alert.mdx │ │ │ ├── dialog.mdx │ │ │ ├── alert-dialog.mdx │ │ │ ├── colors.mdx │ │ │ ├── progress.mdx │ │ │ ├── skeleton.mdx │ │ │ ├── toggle.mdx │ │ │ ├── index.mdx │ │ │ ├── meta.json │ │ │ ├── badge.mdx │ │ │ ├── hover-card.mdx │ │ │ ├── card.mdx │ │ │ ├── popover.mdx │ │ │ ├── sonner.mdx │ │ │ ├── typography.mdx │ │ │ ├── button.mdx │ │ │ ├── separator.mdx │ │ │ ├── toggle-group.mdx │ │ │ ├── drawer.mdx │ │ │ └── scroll-area.mdx │ │ │ └── guides │ │ │ ├── meta.json │ │ │ └── index.mdx │ ├── src │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── [locale] │ │ │ │ ├── (main) │ │ │ │ │ ├── [...rest] │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── (home) │ │ │ │ │ │ └── sections │ │ │ │ │ │ │ ├── admin │ │ │ │ │ │ │ └── debug_panel.png │ │ │ │ │ │ │ ├── powering-by │ │ │ │ │ │ │ └── logos │ │ │ │ │ │ │ │ └── honojs.tsx │ │ │ │ │ │ │ └── call-to-action.tsx │ │ │ │ │ ├── not-found.tsx │ │ │ │ │ ├── layout.client.tsx │ │ │ │ │ ├── (plugins) │ │ │ │ │ │ └── (vitnode-core) │ │ │ │ │ │ │ ├── login │ │ │ │ │ │ │ ├── sso │ │ │ │ │ │ │ │ └── [providerId] │ │ │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ │ └── reset-password │ │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ │ └── register │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── layout.config.tsx │ │ │ │ │ └── layout.tsx │ │ │ │ ├── (docs) │ │ │ │ │ └── docs │ │ │ │ │ │ └── not-found.tsx │ │ │ │ └── admin │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── (auth) │ │ │ │ │ ├── (plugins) │ │ │ │ │ └── (vitnode-core) │ │ │ │ │ │ └── core │ │ │ │ │ │ ├── test │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── layout.tsx │ │ │ ├── api │ │ │ │ ├── search │ │ │ │ │ └── route.ts │ │ │ │ └── [...route] │ │ │ │ │ └── route.ts │ │ │ ├── layout.tsx │ │ │ ├── llms-full.txt │ │ │ │ └── route.ts │ │ │ └── global-error.tsx │ │ ├── content │ │ │ └── docs │ │ │ │ └── guides │ │ │ │ ├── sso │ │ │ │ ├── facebook │ │ │ │ │ ├── 1.png │ │ │ │ │ ├── 2.png │ │ │ │ │ └── 3.png │ │ │ │ └── index.mdx │ │ │ │ ├── meta.json │ │ │ │ └── index.mdx │ │ ├── examples │ │ │ ├── toggle.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── progress.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── popover.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── sonner.tsx │ │ │ ├── card.tsx │ │ │ ├── confirm-action-alert-dialog.tsx │ │ │ ├── separator.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── switch.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── textarea.tsx │ │ │ ├── combobox.tsx │ │ │ ├── drawer.tsx │ │ │ └── sheet.tsx │ │ ├── components │ │ │ └── fumadocs │ │ │ │ ├── img.tsx │ │ │ │ ├── code-block.tsx │ │ │ │ └── search-dialog.tsx │ │ ├── proxy.ts │ │ ├── vitnode.config.ts │ │ └── lib │ │ │ └── source.ts │ ├── public │ │ └── logo_vitnode_dark.png │ ├── postcss.config.mjs │ ├── global.d.ts │ ├── .env.example │ ├── drizzle.config.ts │ ├── e2e │ │ └── homepage.spec.ts │ ├── components.json │ ├── next.config.ts │ ├── .gitignore │ ├── docker-compose.yml │ ├── source.config.ts │ ├── eslint.config.mjs │ └── tsconfig.json └── api │ ├── .env.example │ ├── drizzle.config.ts │ ├── .gitignore │ ├── tsconfig.json │ ├── eslint.config.mjs │ └── src │ ├── index.ts │ └── vitnode.api.config.ts ├── plugins └── blog │ ├── src │ ├── const.ts │ ├── database │ │ ├── index.ts │ │ ├── categories.ts │ │ ├── relations.ts │ │ └── posts.ts │ ├── api │ │ └── modules │ │ │ ├── posts │ │ │ └── posts.module.ts │ │ │ ├── categories │ │ │ └── categories.module.ts │ │ │ └── admin │ │ │ ├── admin.module.ts │ │ │ ├── posts │ │ │ ├── posts.admin.module.ts │ │ │ └── routes │ │ │ │ └── delete.route.ts │ │ │ └── categories │ │ │ └── categories.admin.module.ts │ ├── config.api.ts │ ├── emails │ │ └── test-template.tsx │ ├── config.tsx │ └── views │ │ └── admin │ │ ├── posts │ │ └── table │ │ │ └── actions │ │ │ └── delete │ │ │ └── mutation-api.ts │ │ └── categories │ │ └── table │ │ └── actions │ │ └── delete │ │ └── mutation-api.ts │ ├── .npmignore │ ├── global.d.ts │ ├── .swcrc │ ├── eslint.config.mjs │ └── tsconfig.json ├── pnpm-workspace.yaml ├── .prettierrc.mjs ├── .gitignore ├── .vscode └── settings.json ├── scripts └── bump-version │ └── bump-version.ts ├── package.json └── LICENSE.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: VitNode 2 | -------------------------------------------------------------------------------- /packages/config/.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /packages/vitnode/src/lib/tiptap/toolbar/actions/bold-action.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/.npmignore: -------------------------------------------------------------------------------- 1 | /src 2 | /node_modules 3 | /.turbo 4 | /tsconfig.json -------------------------------------------------------------------------------- /apps/docs/migrations/0001_common_spirit.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "core_cron" ADD COLUMN "nextRun" timestamp; -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/advanced/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Advanced", 3 | "pages": ["..."] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/deployments/cloud/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Cloud", 3 | "pages": ["..."] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/blog/src/const.ts: -------------------------------------------------------------------------------- 1 | export const CONFIG_PLUGIN = { 2 | pluginId: "@vitnode/blog" as const, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/docs/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/src/app/favicon.ico -------------------------------------------------------------------------------- /packages/node-cron/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | /src 3 | /node_modules 4 | /tsconfig.json 5 | /.swcrc 6 | /eslint.config.mjs -------------------------------------------------------------------------------- /packages/nodemailer/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | /src 3 | /node_modules 4 | /tsconfig.json 5 | /.swcrc 6 | /eslint.config.mjs -------------------------------------------------------------------------------- /packages/resend/.npmignore: -------------------------------------------------------------------------------- 1 | /.turbo 2 | /src 3 | /node_modules 4 | /tsconfig.json 5 | /.swcrc 6 | /eslint.config.mjs -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/captcha/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Captcha", 3 | "pages": ["...", "custom-adapter"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs/migrations/0002_public_millenium_guard.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "core_cron" ADD COLUMN "schedule" varchar(100) NOT NULL; -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/monorepo/apps/web/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API_URL=http://localhost:8080 -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/hooks/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hooks", 3 | "icon": "Webhook", 4 | "pages": ["..."] 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/sso/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Single Sign-On (SSO)", 3 | "pages": ["...", "custom-adapter"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs/public/logo_vitnode_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/public/logo_vitnode_dark.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/cron/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "CRON Jobs", 3 | "pages": ["rest-api", "...", "custom-adapter"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/api/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "REST API", 3 | "defaultOpen": true, 4 | "pages": ["modules", "..."] 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/sso/facebook/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/sso/facebook/1.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/sso/facebook/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/sso/facebook/2.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/sso/facebook/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/sso/facebook/3.png -------------------------------------------------------------------------------- /packages/vitnode/src/config.ts: -------------------------------------------------------------------------------- 1 | export const CONFIG_PLUGIN = { 2 | pluginId: "@vitnode/core" as const, 3 | version: "2.0.0-canary.0", 4 | }; 5 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/captcha/recaptcha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/captcha/recaptcha.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/captcha/cloudflare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/captcha/cloudflare.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/deployments/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Deployments", 3 | "icon": "HardDriveUpload", 4 | "pages": ["cloud", "self-hosted"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/content/docs/guides/sso/facebook/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/src/content/docs/guides/sso/facebook/1.png -------------------------------------------------------------------------------- /apps/docs/src/content/docs/guides/sso/facebook/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/src/content/docs/guides/sso/facebook/2.png -------------------------------------------------------------------------------- /apps/docs/src/content/docs/guides/sso/facebook/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/src/content/docs/guides/sso/facebook/3.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/captcha/captcha_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/captcha/captcha_preview.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/i18n/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Internationalization (I18n)", 3 | "pages": ["expand-langs", "namespaces", "messages", "..."] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/blog/src/database/index.ts: -------------------------------------------------------------------------------- 1 | // Tables 2 | export * from "./categories"; 3 | export * from "./posts"; 4 | 5 | // Relations 6 | export * from "./relations"; 7 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/email/components/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Components", 3 | "pages": ["...", "[More Components](https://react.email/components)"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx: -------------------------------------------------------------------------------- 1 | import { notFound } from "next/navigation"; 2 | 3 | export default function RestPage() { 4 | notFound(); 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/email/components/card-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/email/components/card-preview.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/email/components/button-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/email/components/button-preview.png -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/email/default_template_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/content/docs/dev/email/default_template_preview.png -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/* 3 | - packages/* 4 | - plugins/* 5 | 6 | ignoredBuiltDependencies: 7 | - '@swc/core' 8 | - esbuild 9 | - sharp 10 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/with-If.ts: -------------------------------------------------------------------------------- 1 | export const withIf = >( 2 | cond: boolean, 3 | obj: T, 4 | ) => (cond ? obj : {}) as Partial; 5 | -------------------------------------------------------------------------------- /packages/vitnode/src/app_admin/core/test/page.tsx: -------------------------------------------------------------------------------- 1 | import { TestView } from "../../../views/admin/views/core/test"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/(home)/sections/admin/debug_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aXenDeveloper/vitnode/HEAD/apps/docs/src/app/[locale]/(main)/(home)/sections/admin/debug_panel.png -------------------------------------------------------------------------------- /apps/docs/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | "@tailwindcss/postcss": {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorView } from "@vitnode/core/views/error/error-view"; 2 | 3 | export default function NotFoundPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { createFromSource } from "fumadocs-core/search/server"; 2 | 3 | import { source } from "@/lib/source"; 4 | 5 | export const { GET } = createFromSource(source); 6 | -------------------------------------------------------------------------------- /apps/docs/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./global.css"; 2 | 3 | export default async function RootLayout({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return children; 9 | } 10 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(docs)/docs/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorView } from "@vitnode/core/views/error/error-view"; 2 | 3 | export default function NotFoundPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/admin/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignInAdminView } from "@vitnode/core/views/admin/sign-in/sign-in-admin-view"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/email/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Email", 3 | "pages": [ 4 | "templates", 5 | "components", 6 | "---Adapters---", 7 | "...", 8 | "custom-adapter" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx: -------------------------------------------------------------------------------- 1 | import { TestView } from "@vitnode/core/views/admin/views/core/test"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /packages/vitnode/src/app_admin/core/page.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardAdminView } from "../../views/admin/views/core/dashboard/dashboard-admin-view"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/content/docs/guides/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Guides", 3 | "description": "Learn how to use VitNode", 4 | "icon": "BookOpenText", 5 | "root": true, 6 | "pages": ["index", "...", "---Plugins by VitNode---", "blog"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/vitnode/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import plugin from "./src/locales/en.json" with { type: "json" }; 4 | 5 | declare module "next-intl" { 6 | interface AppConfig { 7 | Messages: typeof plugin; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardAdminView } from "@vitnode/core/views/admin/views/core/dashboard/dashboard-admin-view"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/views/core/debug/actions/clear-cache/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | 5 | export const clearCacheMutation = async () => { 6 | await Promise.resolve(revalidatePath("/", "layout")); 7 | }; 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Improving Documentation 2 | 3 | - Run `pnpm lint:fix` to fix formatting issues before opening the PR. 4 | - Read the Docs Contribution Guide: https://vitnode.com/docs/dev/contribution 5 | 6 | ## Description 7 | 8 | ### What? 9 | 10 | ### Why? 11 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import vitnodePrettier from "@vitnode/config/prettierrc"; 2 | 3 | /** 4 | * @see https://prettier.io/docs/en/configuration.html 5 | * @type {import("prettier").Config} 6 | */ 7 | const config = { 8 | ...vitnodePrettier, 9 | }; 10 | 11 | export default config; 12 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/alert.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Alert 3 | description: Display important messages to users. 4 | --- 5 | 6 | 7 | We're working hard to bring you the best documentation experience. 8 | 9 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/dialog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dialog 3 | description: Display content in a modal dialog. 4 | --- 5 | 6 | 7 | We're working hard to bring you the best documentation experience. 8 | 9 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/ctrl-or-command-character.tsx: -------------------------------------------------------------------------------- 1 | export const CtrlOrCommandCharacter = () => { 2 | const isWindows = 3 | typeof window !== "undefined" && 4 | window.navigator.userAgent.includes("Windows"); 5 | const key = isWindows ? "Ctrl" : "⌘"; 6 | 7 | return key; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode 2 | 3 | NEXT_PUBLIC_WEB_URL=http://localhost:3000 4 | 5 | # === Docker Database Postgres === 6 | POSTGRES_USER=root 7 | POSTGRES_PASSWORD=root 8 | POSTGRES_NAME=vitnode -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/monorepo/apps/api/.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode 2 | 3 | NEXT_PUBLIC_WEB_URL=http://localhost:3000 4 | 5 | # === Docker Database Postgres === 6 | POSTGRES_USER=root 7 | POSTGRES_PASSWORD=root 8 | POSTGRES_NAME=vitnode -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import core from "./src/locales/@vitnode/core/en.json" with { type: "json" }; 4 | 5 | declare module "next-intl" { 6 | interface AppConfig { 7 | Messages: typeof core; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts: -------------------------------------------------------------------------------- 1 | import { vitNodeNextConfig } from "@vitnode/core/config/next.config"; 2 | import type { NextConfig } from "next"; 3 | 4 | const nextConfig: NextConfig = { 5 | reactCompiler: true, 6 | }; 7 | 8 | export default vitNodeNextConfig(nextConfig); 9 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/get-package-json.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "node:fs"; 2 | 3 | import type { PackageJSON } from "./packages-json.js"; 4 | 5 | export const packageJson: PackageJSON = JSON.parse( 6 | readFileSync(new URL("../../../package.json", import.meta.url), "utf-8"), 7 | ); 8 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/editor-content.tsx: -------------------------------------------------------------------------------- 1 | export const EditorContent = ({ content }: { content: string }) => { 2 | return ( 3 | // eslint-disable-next-line @eslint-react/dom/no-dangerously-set-innerhtml 4 |
5 | ); 6 | }; 7 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/alert-dialog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Alert Dialog 3 | description: Display important messages to users in a modal dialog. 4 | --- 5 | 6 | 7 | We're working hard to bring you the best documentation experience. 8 | 9 | -------------------------------------------------------------------------------- /packages/vitnode/src/tests/setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/react"; 2 | // import { expect, vi } from 'vitest'; 3 | 4 | // Set up global mocks if needed 5 | // vi.stubGlobal('fetch', vi.fn()); 6 | 7 | // Custom matchers can be added here if needed 8 | // expect.extend({ ... }); 9 | 10 | // Add global environment setup 11 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/deployments/self-hosted.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self-Hosted 3 | description: How to deploy your Vitnode app on your own infrastructure. 4 | --- 5 | 6 | 7 | We're working hard to bring you the best documentation experience. 8 | 9 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode 2 | 3 | NEXT_PUBLIC_API_URL=http://localhost:3000 4 | NEXT_PUBLIC_WEB_URL=http://localhost:3000 5 | 6 | # === Docker Database Postgres === 7 | POSTGRES_USER=root 8 | POSTGRES_PASSWORD=root 9 | POSTGRES_NAME=vitnode -------------------------------------------------------------------------------- /apps/docs/src/app/llms-full.txt/route.ts: -------------------------------------------------------------------------------- 1 | import { getLLMText, source } from "@/lib/source"; 2 | 3 | export const revalidate = false; 4 | 5 | export async function GET() { 6 | const scan = source.getPages().map(getLLMText); 7 | const scanned = await Promise.all(scan); 8 | 9 | return new Response(scanned.join("\n\n")); 10 | } 11 | -------------------------------------------------------------------------------- /apps/docs/src/content/docs/guides/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Guides", 3 | "description": "Learn how to use VitNode", 4 | "icon": "BookOpenText", 5 | "root": true, 6 | "pages": [ 7 | "index", 8 | "...", 9 | "--- Authorization ---", 10 | "sso", 11 | "--- Security ---", 12 | "captcha" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/docs/src/examples/toggle.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Toggle } from "@vitnode/core/components/ui/toggle"; 4 | import { Bold } from "lucide-react"; 5 | 6 | export default function SonnerDemo() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/eslint/.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import vitnodePrettier from "@vitnode/config/prettierrc"; 2 | 3 | /** 4 | * @see https://prettier.io/docs/en/configuration.html 5 | * @type {import("prettier").Config} 6 | */ 7 | const config = { 8 | ...vitnodePrettier, 9 | }; 10 | 11 | export default config; 12 | -------------------------------------------------------------------------------- /plugins/blog/.npmignore: -------------------------------------------------------------------------------- 1 | /src/* 2 | !/src/app 3 | !/src/app/** 4 | !/src/app_admin 5 | !/src/app_admin/** 6 | !/src/locales 7 | !/src/locales/** 8 | 9 | /node_modules 10 | /.turbo 11 | /tsconfig.json 12 | /.swcrc 13 | /components.json 14 | /global.d.ts 15 | /tsup.config.ts 16 | /vitest.config.ts 17 | /tsconfig.json 18 | /scripts 19 | /config -------------------------------------------------------------------------------- /plugins/blog/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import core from "@vitnode/core/locales/en.json" with { type: "json" }; 4 | import plugin from "./src/locales/en.json" with { type: "json" }; 5 | 6 | declare module "next-intl" { 7 | interface AppConfig { 8 | Messages: typeof plugin & typeof core; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/vitnode/config/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | import createNextIntlPlugin from "next-intl/plugin"; 3 | 4 | const withNextIntl = createNextIntlPlugin("./src/vitnode.config.ts"); 5 | 6 | export const vitNodeNextConfig = (config: NextConfig): NextConfig => 7 | withNextIntl({ 8 | ...config, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/api/.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode 2 | 3 | NEXT_PUBLIC_WEB_URL=http://localhost:3000 4 | 5 | # === CRON Secret for Internal API Calls === 6 | CRON_SECRET=your-secure-cron-secret-key 7 | 8 | # === Docker Database Postgres === 9 | POSTGRES_USER=root 10 | POSTGRES_PASSWORD=root 11 | POSTGRES_NAME=vitnode -------------------------------------------------------------------------------- /apps/docs/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import blog from "./src/locales/@vitnode/blog/en.json" with { type: "json" }; 4 | import core from "./src/locales/@vitnode/core/en.json" with { type: "json" }; 5 | 6 | declare module "next-intl" { 7 | interface AppConfig { 8 | Messages: typeof core & typeof blog; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/models/user.ts: -------------------------------------------------------------------------------- 1 | import { getUserById } from "./user/get-user-by-id"; 2 | import { signInWithPassword } from "./user/sign-in-with-passwords"; 3 | import { signUp } from "./user/sign-up"; 4 | 5 | export class UserModel { 6 | getUserById = getUserById; 7 | signInWithPassword = signInWithPassword; 8 | signUp = signUp; 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/is-writeable.ts: -------------------------------------------------------------------------------- 1 | import { W_OK } from "node:constants"; 2 | import { access } from "node:fs/promises"; 3 | 4 | export async function isWriteable(directory: string): Promise { 5 | try { 6 | await access(directory, W_OK); 7 | 8 | return true; 9 | } catch { 10 | return false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/form/common/desc.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | export const AutoFormDesc = ({ 4 | children, 5 | className, 6 | ...props 7 | }: React.ComponentProps<"p">) => { 8 | return ( 9 |

10 | {children} 11 |

12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/colors.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Colors 3 | description: A collection of color utilities and components for consistent theming across your application. 4 | icon: Palette 5 | --- 6 | 7 | 8 | We're working hard to bring you the best documentation experience. 9 | 10 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/tiptap/toolbar/use-toolbar-editor.ts: -------------------------------------------------------------------------------- 1 | import type { Editor } from "@tiptap/react"; 2 | 3 | import React from "react"; 4 | 5 | export const ToolbarEditorContext = React.createContext<{ 6 | editor: Editor; 7 | }>({ 8 | editor: {} as Editor, 9 | }); 10 | 11 | export const useToolbarEditor = () => React.use(ToolbarEditorContext); 12 | -------------------------------------------------------------------------------- /plugins/blog/src/api/modules/posts/posts.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@vitnode/core/api/lib/module"; 2 | 3 | import { CONFIG_PLUGIN } from "@/const"; 4 | 5 | import { postsRoute } from "./routes/get.route"; 6 | 7 | export const postsModule = buildModule({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | name: "posts", 10 | routes: [postsRoute], 11 | }); 12 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/admin/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AdminLayout, 3 | type AdminLayoutProps, 4 | } from "@vitnode/core/views/admin/layouts/admin-layout"; 5 | 6 | import { vitNodeConfig } from "@/vitnode.config"; 7 | 8 | export default function Layout(props: AdminLayoutProps) { 9 | return ; 10 | } 11 | -------------------------------------------------------------------------------- /packages/vitnode/.npmignore: -------------------------------------------------------------------------------- 1 | /src/* 2 | !/src/app 3 | !/src/app/**Add commentMore actions 4 | !/src/app_admin 5 | !/src/app_admin/** 6 | !/src/locales 7 | !/src/locales/** 8 | !/src/tiptap.css 9 | 10 | /node_modules 11 | /.turbo 12 | /tsconfig.json 13 | /.swcrc 14 | /components.json 15 | /global.d.ts 16 | /tsup.config.ts 17 | /vitest.config.ts 18 | /tsconfig.json 19 | /scripts -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/middleware/middleware.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { routeMiddleware } from "./route"; 5 | 6 | export const middlewareModule = buildModule({ 7 | pluginId: CONFIG_PLUGIN.pluginId, 8 | name: "middleware", 9 | routes: [routeMiddleware], 10 | }); 11 | -------------------------------------------------------------------------------- /packages/node-cron/src/index.ts: -------------------------------------------------------------------------------- 1 | import { type CronAdapter, handleCronJobs } from "@vitnode/core/api/lib/cron"; 2 | import { schedule } from "node-cron"; 3 | 4 | export const NodeCronAdapter = (): CronAdapter => { 5 | return { 6 | schedule() { 7 | schedule("*/1 * * * *", async () => { 8 | await handleCronJobs(); 9 | }); 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-plugin/root/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import core from "@vitnode/core/locales/en.json" with { type: "json" }; 4 | import plugin from "./src/locales/en.json" with { type: "json" }; 5 | 6 | declare module "next-intl" { 7 | interface AppConfig { 8 | Messages: typeof plugin & typeof core; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/api/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineVitNodeDrizzleConfig } from "@vitnode/core/drizzle.config"; 2 | 3 | import { POSTGRES_URL, vitNodeApiConfig } from "./src/vitnode.api.config"; 4 | 5 | export default defineVitNodeDrizzleConfig({ 6 | vitNodeApiConfig, 7 | out: "./migrations/", 8 | dialect: "postgresql", 9 | dbCredentials: { 10 | url: POSTGRES_URL, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /apps/docs/.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode 2 | 3 | NEXT_PUBLIC_API_URL=http://localhost:3000 4 | NEXT_PUBLIC_WEB_URL=http://localhost:3000 5 | 6 | # === CRON Secret for Internal API Calls === 7 | CRON_SECRET=your-secure-cron-secret-key 8 | 9 | # === Docker Database Postgres === 10 | POSTGRES_USER=root 11 | POSTGRES_PASSWORD=root 12 | POSTGRES_NAME=vitnode -------------------------------------------------------------------------------- /apps/docs/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineVitNodeDrizzleConfig } from "@vitnode/core/drizzle.config"; 2 | 3 | import { POSTGRES_URL, vitNodeApiConfig } from "./src/vitnode.api.config"; 4 | 5 | export default defineVitNodeDrizzleConfig({ 6 | vitNodeApiConfig, 7 | out: "./migrations/", 8 | dialect: "postgresql", 9 | dbCredentials: { 10 | url: POSTGRES_URL, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/config/prettierrc.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://prettier.io/docs/en/configuration.html 3 | * @type {import("prettier").Config} 4 | */ 5 | const config = { 6 | singleQuote: false, 7 | arrowParens: "avoid", 8 | trailingComma: "all", 9 | printWidth: 80, 10 | plugins: ["prettier-plugin-tailwindcss"], 11 | tailwindFunctions: ["cn"], 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-plugin/root/npmignore.template: -------------------------------------------------------------------------------- 1 | /src/* 2 | !/src/app 3 | !/src/app/** 4 | !/src/app_admin 5 | !/src/app_admin/** 6 | !/src/locales 7 | !/src/locales/** 8 | 9 | /node_modules 10 | /.turbo 11 | /tsconfig.json 12 | /.swcrc 13 | /components.json 14 | /global.d.ts 15 | /tsup.config.ts 16 | /vitest.config.ts 17 | /tsconfig.json 18 | /scripts 19 | /config 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/admin/users/users.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { listUsersAdminRoute } from "./routes/list.route"; 5 | 6 | export const usersAdminModule = buildModule({ 7 | pluginId: CONFIG_PLUGIN.pluginId, 8 | name: "users", 9 | routes: [listUsersAdminRoute], 10 | }); 11 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/admin/debug/debug.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { CONFIG_PLUGIN } from "../../../../config"; 2 | import { buildModule } from "../../../lib/module"; 3 | import { logsDebugAdminRoute } from "./routes/logs.route"; 4 | 5 | export const debugAdminModule = buildModule({ 6 | pluginId: CONFIG_PLUGIN.pluginId, 7 | name: "debug", 8 | routes: [logsDebugAdminRoute], 9 | }); 10 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/i18n/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Expand Languages 3 | description: Expand the languages to your application. 4 | --- 5 | 6 | 7 | We're working hard to bring you the best documentation experience. 8 | 9 | 10 | As default VitNode uses the `en` _(English USA)_ language for the application. 11 | -------------------------------------------------------------------------------- /apps/docs/e2e/homepage.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test"; 2 | 3 | const vitNodeTitleRegex = /VitNode/; 4 | 5 | test.describe("Homepage", () => { 6 | test("should load successfully", async ({ page }) => { 7 | await page.goto("/"); 8 | await expect(page).toHaveTitle(vitNodeTitleRegex); 9 | await expect(page.getByText("Start Your Journey!")).toBeVisible(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /apps/docs/src/content/docs/guides/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: General Information 3 | description: Before you start. 4 | icon: PackageOpen 5 | --- 6 | 7 | VitNode is designed to simplify app management and development—even for users without technical expertise. This guide provides an overview of the framework and its features, so you can get started quickly. 8 | 9 | We're still working on the documentation :> 10 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/admin/advanced/advanced.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { cronAdminModule } from "./cron/cron.admin.module"; 5 | 6 | export const advancedAdminModule = buildModule({ 7 | pluginId: CONFIG_PLUGIN.pluginId, 8 | name: "advanced", 9 | routes: [], 10 | modules: [cronAdminModule], 11 | }); 12 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/not-found.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Not Found 3 | description: xxx 4 | --- 5 | 6 | VitNode has implemented own 404 page. You can create your own page by modifying the `app/[locale]/(main)/not-found.tsx` file. 7 | 8 | ```tsx 9 | import { ErrorView } from '@vitnode/core/views/error/error-view'; 10 | 11 | export default function NotFoundPage() { 12 | return ; 13 | } 14 | ``` 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Turbo 5 | .turbo 6 | 7 | # Local env files 8 | .env 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | 14 | # Build Outputs 15 | .next/ 16 | out/ 17 | build 18 | dist 19 | 20 | # Debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Misc 26 | .DS_Store 27 | *.pem 28 | 29 | # Others 30 | /docker 31 | .vercel -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Turbo 5 | .turbo 6 | 7 | # Test & Build 8 | /coverage 9 | /desc 10 | *.tsbuildinfo 11 | 12 | # Misc 13 | .DS_Store 14 | *.pem 15 | /.pnp 16 | .pnp.js 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | # Local env files 22 | .env 23 | .env.local 24 | .env.development.local 25 | .env.test.local 26 | .env.production.local 27 | 28 | # Others 29 | .vercel -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "NodeNext", 7 | "baseUrl": ".", 8 | "outDir": "./dist", 9 | "types": ["node"], 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | } 13 | }, 14 | "include": ["src"], 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/tiptap/toolbar/actions/utils/tooltip-shortcut.tsx: -------------------------------------------------------------------------------- 1 | import { CtrlOrCommandCharacter } from "@/lib/ctrl-or-command-character"; 2 | 3 | export const TooltipShortcut = ({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) => { 8 | return ( 9 | 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/cron/cron.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { cleanCron } from "./cron/clean.cron"; 5 | import { runCronRoute } from "./routes/cron.route"; 6 | 7 | export const cronModule = buildModule({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | name: "cron", 10 | routes: [runCronRoute], 11 | cronJobs: [cleanCron], 12 | }); 13 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/api-bun/src/index.ts: -------------------------------------------------------------------------------- 1 | import { OpenAPIHono } from "@hono/zod-openapi"; 2 | import { VitNodeAPI } from "@vitnode/core/api/config"; 3 | 4 | import { vitNodeApiConfig } from "./vitnode.api.config.js"; 5 | 6 | const app = new OpenAPIHono().basePath("/api"); 7 | 8 | VitNodeAPI({ 9 | app, 10 | vitNodeApiConfig, 11 | }); 12 | 13 | export default { 14 | port: 8080, 15 | fetch: app.fetch, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/users/sso/sso.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { callbackRoute } from "./routes/callback.route"; 5 | import { createUrlRoute } from "./routes/create-url.route"; 6 | 7 | export const ssoUserModule = buildModule({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | name: "sso", 10 | routes: [callbackRoute, createUrlRoute], 11 | }); 12 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/admin/advanced/cron/cron.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { getCronsRoute } from "./routes/get.route"; 5 | import { runCronRoute } from "./routes/run.route"; 6 | 7 | export const cronAdminModule = buildModule({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | name: "cron", 10 | routes: [getCronsRoute, runCronRoute], 11 | }); 12 | -------------------------------------------------------------------------------- /plugins/blog/src/api/modules/categories/categories.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@vitnode/core/api/lib/module"; 2 | 3 | import { CONFIG_PLUGIN } from "@/const"; 4 | 5 | import { categoriesRoute } from "./routes/get.route"; 6 | import { testRoute } from "./test.route"; 7 | 8 | export const categoriesModule = buildModule({ 9 | pluginId: CONFIG_PLUGIN.pluginId, 10 | name: "categories", 11 | routes: [categoriesRoute, testRoute], 12 | }); 13 | -------------------------------------------------------------------------------- /apps/docs/src/examples/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@vitnode/core/components/ui/skeleton"; 2 | 3 | export default function SkeletonDemo() { 4 | return ( 5 |
6 | 7 |
8 | 9 | 10 |
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/api/get-middleware-api.ts: -------------------------------------------------------------------------------- 1 | import { middlewareModule } from "@/api/modules/middleware/middleware.module"; 2 | import { fetcher } from "@/lib/fetcher"; 3 | 4 | export const getMiddlewareApi = async () => { 5 | const res = await fetcher(middlewareModule, { 6 | path: "/", 7 | method: "get", 8 | module: "middleware", 9 | options: { 10 | cache: "force-cache", 11 | }, 12 | }); 13 | 14 | return await res.json(); 15 | }; 16 | -------------------------------------------------------------------------------- /apps/docs/src/examples/progress.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Progress } from "@vitnode/core/components/ui/progress"; 4 | import React from "react"; 5 | 6 | export default function ProgressDemo() { 7 | const [progress, setProgress] = React.useState(13); 8 | 9 | React.useEffect(() => { 10 | const timer = setTimeout(() => setProgress(66), 500); 11 | 12 | return () => clearTimeout(timer); 13 | }, []); 14 | 15 | return ; 16 | } 17 | -------------------------------------------------------------------------------- /packages/node-cron/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname } from "node:path"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default [ 8 | ...eslintVitNode, 9 | { 10 | languageOptions: { 11 | parserOptions: { 12 | project: "./tsconfig.json", 13 | tsconfigRootDir: __dirname, 14 | }, 15 | }, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /packages/nodemailer/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname } from "node:path"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default [ 8 | ...eslintVitNode, 9 | { 10 | languageOptions: { 11 | parserOptions: { 12 | project: "./tsconfig.json", 13 | tsconfigRootDir: __dirname, 14 | }, 15 | }, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /packages/resend/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname } from "node:path"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default [ 8 | ...eslintVitNode, 9 | { 10 | languageOptions: { 11 | parserOptions: { 12 | project: "./tsconfig.json", 13 | tsconfigRootDir: __dirname, 14 | }, 15 | }, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /packages/vitnode/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["scripts/scripts.ts"], 5 | outDir: "dist/scripts", 6 | clean: false, 7 | minify: true, 8 | splitting: true, 9 | format: "esm", 10 | target: "esnext", 11 | platform: "node", 12 | external: ["jiti"], 13 | banner: { 14 | js: `import { createRequire } from 'module'; 15 | const require = createRequire(import.meta.url); 16 | `, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /plugins/blog/src/database/categories.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from "drizzle-orm/pg-core"; 2 | 3 | export const blog_categories = pgTable("blog_categories", t => ({ 4 | id: t.serial().primaryKey(), 5 | title: t.varchar({ length: 100 }).notNull(), 6 | createdAt: t.timestamp().notNull().defaultNow(), 7 | updatedAt: t 8 | .timestamp() 9 | .notNull() 10 | .$onUpdate(() => new Date()), 11 | titleSeo: t.varchar({ length: 100 }).notNull().unique().default(""), 12 | })).enableRLS(); 13 | -------------------------------------------------------------------------------- /apps/docs/src/examples/hover-card.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | HoverCard, 5 | HoverCardContent, 6 | HoverCardTrigger, 7 | } from "@vitnode/core/components/ui/hover-card"; 8 | 9 | export default function HoverCardExample() { 10 | return ( 11 | 12 | Hover 13 | 14 | Extendable Framework - created and maintained by @axendev. 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "NodeNext", 7 | "moduleResolution": "nodenext", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "strict": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true 13 | }, 14 | "exclude": ["node_modules", "dist"], 15 | "include": ["src"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/progress.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Progress 3 | description: A progress component for indicating the completion of a task. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { Progress } from '@vitnode/core/components/ui/progress'; 14 | ``` 15 | 16 | ```tsx 17 | 18 | ``` 19 | 20 | ## API Reference 21 | 22 | [Radix UI - Progress](https://www.radix-ui.com/primitives/docs/components/progress#api-reference) 23 | -------------------------------------------------------------------------------- /plugins/blog/src/api/modules/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@vitnode/core/api/lib/module"; 2 | 3 | import { CONFIG_PLUGIN } from "../../../const"; 4 | import { categoriesAdminModule } from "./categories/categories.admin.module"; 5 | import { postsAdminModule } from "./posts/posts.admin.module"; 6 | 7 | export const adminModule = buildModule({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | name: "admin", 10 | modules: [categoriesAdminModule, postsAdminModule], 11 | routes: [], 12 | }); 13 | -------------------------------------------------------------------------------- /.github/release.yaml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: 🔒 Secure Changes 4 | labels: 5 | - 🔒 Secure Changes 6 | - title: 🚨 Breaking Changes 7 | labels: 8 | - 🚨 Breaking Changes 9 | - title: 🚨 Breaking Changes 10 | labels: 11 | - 🚨 Breaking Changes 12 | - title: 🎉 Features 13 | labels: 14 | - 💡 Feature 15 | - title: 🔧 Fixes 16 | labels: 17 | - 🐞 Bug 18 | - title: Other Changes 19 | labels: 20 | - '*' 21 | -------------------------------------------------------------------------------- /apps/docs/content/docs/guides/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: General Information 3 | description: Before you start. 4 | icon: PackageOpen 5 | --- 6 | 7 | VitNode is designed to simplify app management and development—even for users without technical expertise. This guide provides an overview of the framework and its features, so you can get started quickly. 8 | 9 | 10 | We're working hard to bring you the best documentation experience. 11 | 12 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/views/core/debug/system-logs/badges/badge-status.tsx: -------------------------------------------------------------------------------- 1 | import { Badge } from "@/components/ui/badge"; 2 | 3 | export const BadgeStatus = ({ statusCode }: { statusCode: number }) => { 4 | let variant: "default" | "destructive" | "secondary" = "secondary"; 5 | 6 | if (statusCode >= 200 && statusCode < 300) { 7 | variant = "default"; 8 | } else if (statusCode >= 400) { 9 | variant = "destructive"; 10 | } 11 | 12 | return {statusCode}; 13 | }; 14 | -------------------------------------------------------------------------------- /apps/api/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname } from "node:path"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default [ 8 | ...eslintVitNode, 9 | { 10 | ignores: ["drizzle.config.ts"], 11 | }, 12 | { 13 | languageOptions: { 14 | parserOptions: { 15 | project: "./tsconfig.json", 16 | tsconfigRootDir: __dirname, 17 | }, 18 | }, 19 | }, 20 | ]; 21 | -------------------------------------------------------------------------------- /apps/docs/src/app/api/[...route]/route.ts: -------------------------------------------------------------------------------- 1 | import { OpenAPIHono } from "@hono/zod-openapi"; 2 | import { VitNodeAPI } from "@vitnode/core/api/config"; 3 | import { handle } from "hono/vercel"; 4 | 5 | import { vitNodeApiConfig } from "@/vitnode.api.config"; 6 | 7 | const app = new OpenAPIHono().basePath("/api"); 8 | VitNodeAPI({ 9 | app, 10 | vitNodeApiConfig, 11 | }); 12 | 13 | export const GET = handle(app); 14 | export const POST = handle(app); 15 | export const PUT = handle(app); 16 | export const DELETE = handle(app); 17 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/packages-json.ts: -------------------------------------------------------------------------------- 1 | export interface PackageJSON { 2 | dependencies?: Record; 3 | devDependencies?: Record; 4 | exports?: Record>; 5 | name: string; 6 | overrides?: Record; 7 | packageManager?: string; 8 | pnpm?: Record>; 9 | private: boolean; 10 | scripts?: Record; 11 | type?: string; 12 | version?: string; 13 | workspaces?: string[]; 14 | } 15 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/fetcher/helpers.ts: -------------------------------------------------------------------------------- 1 | export const buildSearchParams = (query: Record) => { 2 | const searchParams = new URLSearchParams(); 3 | 4 | for (const [k, v] of Object.entries(query)) { 5 | if (v === undefined) { 6 | continue; 7 | } 8 | 9 | if (Array.isArray(v)) { 10 | for (const v2 of v) { 11 | searchParams.append(k, v2); 12 | } 13 | } else { 14 | searchParams.set(k, v); 15 | } 16 | } 17 | 18 | return searchParams; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/error/back-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { useRouter } from "@/lib/navigation"; 5 | 6 | export const BackButtonNotFound = ({ 7 | children, 8 | className, 9 | }: { 10 | children: React.ReactNode; 11 | className?: string; 12 | }) => { 13 | const { back } = useRouter(); 14 | 15 | return ( 16 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /plugins/blog/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/docs/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/app/global.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /packages/node-cron/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/resend/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/vitnode/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/docs/next.config.ts: -------------------------------------------------------------------------------- 1 | import nextAnalyzer from "@next/bundle-analyzer"; 2 | import { vitNodeNextConfig } from "@vitnode/core/config/next.config"; 3 | import { createMDX } from "fumadocs-mdx/next"; 4 | import type { NextConfig } from "next"; 5 | 6 | const withMDX = createMDX(); 7 | 8 | const withBundleAnalyzer = nextAnalyzer({ 9 | enabled: process.env.ANALYZE === "true", 10 | }); 11 | 12 | const nextConfig: NextConfig = { 13 | reactCompiler: true, 14 | }; 15 | 16 | export default withBundleAnalyzer(withMDX(vitNodeNextConfig(nextConfig))); 17 | -------------------------------------------------------------------------------- /packages/node-cron/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "emitDeclarationOnly": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | } 16 | }, 17 | "exclude": ["node_modules"], 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/nodemailer/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/nodemailer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "emitDeclarationOnly": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | } 16 | }, 17 | "exclude": ["node_modules"], 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/resend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "emitDeclarationOnly": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | } 16 | }, 17 | "exclude": ["node_modules"], 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/tiptap.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | .tiptap { 4 | h1 { 5 | @apply text-4xl font-extrabold tracking-tight text-balance; 6 | } 7 | 8 | h2 { 9 | @apply text-3xl font-semibold tracking-tight; 10 | } 11 | 12 | h3 { 13 | @apply text-2xl font-semibold tracking-tight; 14 | } 15 | 16 | h4 { 17 | @apply text-xl font-semibold tracking-tight; 18 | } 19 | 20 | ul, 21 | ol { 22 | @apply my-4 ml-6 space-y-2; 23 | } 24 | 25 | p { 26 | @apply [&:not(:first-child)]:mt-2; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/blog/src/api/modules/admin/posts/posts.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@vitnode/core/api/lib/module"; 2 | 3 | import { CONFIG_PLUGIN } from "../../../../const"; 4 | import { createPostRoute } from "./routes/create.route"; 5 | import { deletePostRoute } from "./routes/delete.route"; 6 | import { editPostRoute } from "./routes/edit.route"; 7 | 8 | export const postsAdminModule = buildModule({ 9 | pluginId: CONFIG_PLUGIN.pluginId, 10 | name: "posts", 11 | routes: [editPostRoute, createPostRoute, deletePostRoute], 12 | }); 13 | -------------------------------------------------------------------------------- /packages/vitnode/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/views/global.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /apps/docs/src/examples/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@vitnode/core/components/ui/button"; 4 | import { 5 | Popover, 6 | PopoverContent, 7 | PopoverTrigger, 8 | } from "@vitnode/core/components/ui/popover"; 9 | 10 | export default function PopoverDemo() { 11 | return ( 12 | 13 | 14 | 15 | 16 | Place content for the popover here. 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/api/get-session-api.ts: -------------------------------------------------------------------------------- 1 | import { usersModule } from "@/api/modules/users/users.module"; 2 | import { fetcher } from "@/lib/fetcher"; 3 | 4 | export const getSessionApi = async () => { 5 | const res = await fetcher(usersModule, { 6 | path: "/session", 7 | method: "get", 8 | module: "users", 9 | options: { 10 | cache: "force-cache", 11 | }, 12 | }); 13 | 14 | const data = await res.json(); 15 | 16 | return data; 17 | }; 18 | 19 | export type SessionApi = Awaited>; 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/config.ts: -------------------------------------------------------------------------------- 1 | const ENVS = { 2 | apiUrl: process.env.NEXT_PUBLIC_API_URL, 3 | webUrl: process.env.NEXT_PUBLIC_WEB_URL, 4 | cronConfig: 5 | process.env.CRON_SECRET ?? "default-cron-secret-change-in-production", 6 | }; 7 | 8 | const urls = { 9 | api: new URL(ENVS.apiUrl ?? "http://localhost:3000"), 10 | web: new URL(ENVS.webUrl ?? "http://localhost:3000"), 11 | }; 12 | 13 | export const CONFIG = { 14 | node_development: process.env.NODE_ENV === "development", 15 | ...urls, 16 | cronJobSecret: ENVS.cronConfig, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/layouts/theme/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { VitNodeConfig } from "../../../vitnode.config"; 2 | 3 | import { HeaderLayout } from "./header/header"; 4 | 5 | export const ThemeLayout = ({ 6 | children, 7 | logo, 8 | vitNodeConfig, 9 | }: React.ComponentProps & { 10 | children: React.ReactNode; 11 | vitNodeConfig: VitNodeConfig; 12 | }) => { 13 | return ( 14 | <> 15 | {" "} 16 |
{children}
17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /apps/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Turbo 5 | .turbo 6 | 7 | # Generated content 8 | .contentlayer 9 | .content-collections 10 | .source 11 | 12 | # Test & Build 13 | /coverage 14 | /.next/ 15 | /out/ 16 | /build 17 | *.tsbuildinfo 18 | 19 | # Misc 20 | .DS_Store 21 | *.pem 22 | /.pnp 23 | .pnp.js 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Local env files 29 | .env 30 | .env.local 31 | .env.development.local 32 | .env.test.local 33 | .env.production.local 34 | 35 | # Others 36 | .vercel 37 | next-env.d.ts -------------------------------------------------------------------------------- /apps/docs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | database: 5 | container_name: vitnode_postgres_dev_bun 6 | image: postgres:17.5-alpine 7 | restart: unless-stopped 8 | environment: 9 | POSTGRES_USER: ${POSTGRES_USER-root} 10 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD-root} 11 | POSTGRES_DB: ${POSTGRES_NAME-vitnode} 12 | volumes: 13 | - ../../docker/dev:/var/lib/postgresql/data 14 | ports: 15 | - "5432:5432" 16 | networks: 17 | - vitnode_dev 18 | 19 | networks: 20 | vitnode_dev: 21 | -------------------------------------------------------------------------------- /apps/docs/source.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | defineDocs, 4 | frontmatterSchema, 5 | metaSchema, 6 | } from "fumadocs-mdx/config"; 7 | import jsonSchema from "fumadocs-mdx/plugins/json-schema"; 8 | 9 | export const docs = defineDocs({ 10 | dir: "content/docs", 11 | docs: { 12 | schema: frontmatterSchema, 13 | postprocess: { 14 | includeProcessedMarkdown: true, 15 | }, 16 | }, 17 | meta: { 18 | schema: metaSchema, 19 | }, 20 | }); 21 | 22 | export default defineConfig({ 23 | plugins: [jsonSchema()], 24 | }); 25 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/loader.tsx: -------------------------------------------------------------------------------- 1 | import { Loader2 } from "lucide-react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export const Loader = ({ 6 | className, 7 | small, 8 | }: { 9 | className?: string; 10 | small?: boolean; 11 | }) => { 12 | if (small) { 13 | return ; 14 | } 15 | 16 | return ( 17 |
18 | 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /plugins/blog/src/database/relations.ts: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm"; 2 | 3 | import { blog_categories } from "./categories"; 4 | import { blog_posts } from "./posts"; 5 | 6 | export const blog_posts_relations = relations(blog_posts, ({ one }) => ({ 7 | category: one(blog_categories, { 8 | fields: [blog_posts.categoryId], 9 | references: [blog_categories.id], 10 | }), 11 | })); 12 | 13 | export const blog_categories_relations = relations( 14 | blog_categories, 15 | ({ many }) => ({ 16 | posts: many(blog_posts), 17 | }), 18 | ); 19 | -------------------------------------------------------------------------------- /plugins/blog/src/config.api.ts: -------------------------------------------------------------------------------- 1 | import { buildApiPlugin } from "@vitnode/core/api/lib/plugin"; 2 | 3 | import { CONFIG_PLUGIN } from "@/const"; 4 | 5 | import { adminModule } from "./api/modules/admin/admin.module"; 6 | import { categoriesModule } from "./api/modules/categories/categories.module"; 7 | import { postsModule } from "./api/modules/posts/posts.module"; 8 | 9 | export const blogApiPlugin = () => { 10 | return buildApiPlugin({ 11 | pluginId: CONFIG_PLUGIN.pluginId, 12 | modules: [adminModule, categoriesModule, postsModule], 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/vitnode/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import eslintVitNodeReact from "@vitnode/config/eslint.react"; 3 | import { fileURLToPath } from "node:url"; 4 | import { dirname } from "node:path"; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | export default [ 9 | ...eslintVitNode, 10 | ...eslintVitNodeReact, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | project: "./tsconfig.json", 15 | tsconfigRootDir: __dirname, 16 | }, 17 | }, 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/database/cron.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from "drizzle-orm/pg-core"; 2 | 3 | export const core_cron = pgTable("core_cron", t => ({ 4 | id: t.serial().primaryKey(), 5 | name: t.varchar({ length: 255 }).notNull(), 6 | description: t.varchar({ length: 255 }), 7 | lastRun: t.timestamp(), 8 | createdAt: t.timestamp().notNull().defaultNow(), 9 | pluginId: t.varchar({ length: 100 }).notNull(), 10 | module: t.varchar({ length: 100 }).notNull(), 11 | nextRun: t.timestamp(), 12 | schedule: t.varchar({ length: 100 }).notNull(), 13 | })).enableRLS(); 14 | -------------------------------------------------------------------------------- /plugins/blog/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import eslintVitNodeReact from "@vitnode/config/eslint.react"; 3 | import { fileURLToPath } from "node:url"; 4 | import { dirname } from "node:path"; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | export default [ 9 | ...eslintVitNode, 10 | ...eslintVitNodeReact, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | project: "./tsconfig.json", 15 | tsconfigRootDir: __dirname, 16 | }, 17 | }, 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { LogoVitNode } from "@vitnode/core/components/logo-vitnode"; 2 | import { ThemeLayout } from "@vitnode/core/views/layouts/theme/layout"; 3 | 4 | import { vitNodeConfig } from "../../../vitnode.config"; 5 | 6 | export default function Layout({ children }: { children: React.ReactNode }) { 7 | return ( 8 | } 10 | vitNodeConfig={vitNodeConfig} 11 | > 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-plugin/root/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://swc.rs/schema.json", 3 | "minify": true, 4 | "jsc": { 5 | "baseUrl": "./", 6 | "target": "esnext", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "parser": { 11 | "syntax": "typescript", 12 | "tsx": true 13 | }, 14 | "transform": { 15 | "react": { 16 | "runtime": "automatic" 17 | } 18 | } 19 | }, 20 | "module": { 21 | "type": "nodenext", 22 | "strict": true, 23 | "resolveFully": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/plugin.ts: -------------------------------------------------------------------------------- 1 | import { CONFIG_PLUGIN } from "@/config"; 2 | 3 | import { buildApiPlugin } from "./lib/plugin"; 4 | import { adminModule } from "./modules/admin/admin.module"; 5 | import { cronModule } from "./modules/cron/cron.module"; 6 | import { middlewareModule } from "./modules/middleware/middleware.module"; 7 | import { usersModule } from "./modules/users/users.module"; 8 | 9 | export const newBuildPluginApiCore = buildApiPlugin({ 10 | pluginId: CONFIG_PLUGIN.pluginId, 11 | modules: [middlewareModule, usersModule, adminModule, cronModule], 12 | }); 13 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/skeleton.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Skeleton 3 | description: A placeholder component for loading states. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { Skeleton } from '@vitnode/core/components/ui/skeleton'; 14 | ``` 15 | 16 | ```tsx 17 |
18 | 19 |
20 | 21 | 22 |
23 |
24 | ``` 25 | -------------------------------------------------------------------------------- /packages/vitnode/src/app/login/sso/[providerId]/page.tsx: -------------------------------------------------------------------------------- 1 | import { CallbackSSOView } from "@/views/auth/sso/callback/callback-sso-view"; 2 | 3 | export default async function Page({ 4 | params, 5 | searchParams, 6 | }: { 7 | params: Promise<{ providerId: string }>; 8 | searchParams: Promise>; 9 | }) { 10 | const { providerId } = await params; 11 | const currentSearchParams = await searchParams; 12 | 13 | return ( 14 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/conventional-label.yaml: -------------------------------------------------------------------------------- 1 | name: Conventional Release Labels 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited] 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: read 10 | 11 | jobs: 12 | label: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: bcoe/conventional-release-labels@v1 16 | with: 17 | type_labels: '{"feat": "💡 Feature", "fix": "🐞 Bug", "breaking": "🚨 Breaking Changes", "docs": "📖 Documentation", "style": "💅 Style", "refactor": "🧱 Refactor", "perf": "🚀 Performerce", "test": "🧪 Test"}' 18 | -------------------------------------------------------------------------------- /plugins/blog/src/api/modules/admin/categories/categories.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@vitnode/core/api/lib/module"; 2 | 3 | import { CONFIG_PLUGIN } from "../../../../const"; 4 | import { createCategoryRoute } from "./routes/create.route"; 5 | import { deleteCategoryRoute } from "./routes/delete.route"; 6 | import { editCategoryRoute } from "./routes/edit.route"; 7 | 8 | export const categoriesAdminModule = buildModule({ 9 | pluginId: CONFIG_PLUGIN.pluginId, 10 | name: "categories", 11 | routes: [createCategoryRoute, editCategoryRoute, deleteCategoryRoute], 12 | }); 13 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/toggle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Toggle 3 | description: A toggle component for switching between two states. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { Bold } from 'lucide-react'; 14 | 15 | import { Toggle } from '@vitnode/core/components/ui/toggle'; 16 | ``` 17 | 18 | ```tsx 19 | 20 | 21 | 22 | ``` 23 | 24 | ## API Reference 25 | 26 | [Radix UI - Toggle](https://www.radix-ui.com/primitives/docs/components/toggle#api-reference) 27 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | database: 5 | container_name: vitnode_postgres_dev 6 | image: postgres:17.5-alpine 7 | restart: unless-stopped 8 | environment: 9 | POSTGRES_USER: ${POSTGRES_USER-root} 10 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD-root} 11 | POSTGRES_DB: ${POSTGRES_NAME-vitnode} 12 | volumes: 13 | - ./docker/dev:/var/lib/postgresql/data 14 | ports: 15 | - '5432:5432' 16 | networks: 17 | - vitnode_dev 18 | 19 | networks: 20 | vitnode_dev: 21 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/layouts/sidebar/sign-out.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; 4 | import { logOutMutationApi } from "@/views/layouts/theme/header/user/auth/log-out-mutation-api"; 5 | 6 | export const SignOutUserFooterSidebarAdmin = ({ 7 | children, 8 | }: { 9 | children: React.ReactNode; 10 | }) => { 11 | return ( 12 | { 14 | await logOutMutationApi({ isAdmin: true }); 15 | }} 16 | > 17 | {children} 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/vitnode/src/database/roles.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from "drizzle-orm/pg-core"; 2 | 3 | export const core_roles = pgTable("core_roles", t => ({ 4 | id: t.serial().primaryKey(), 5 | createdAt: t.timestamp().notNull().defaultNow(), 6 | updatedAt: t 7 | .timestamp() 8 | .notNull() 9 | .$onUpdate(() => new Date()), 10 | protected: t.boolean().notNull().default(false), 11 | default: t.boolean().notNull().default(false), 12 | root: t.boolean().notNull().default(false), 13 | guest: t.boolean().notNull().default(false), 14 | color: t.varchar({ length: 19 }), 15 | })).enableRLS(); 16 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/eslint/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import eslintVitNodeReact from "@vitnode/config/eslint.react"; 3 | import { fileURLToPath } from "node:url"; 4 | import { dirname } from "node:path"; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | export default [ 9 | ...eslintVitNode, 10 | ...eslintVitNodeReact, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | project: "./tsconfig.json", 15 | tsconfigRootDir: __dirname, 16 | }, 17 | }, 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | description: Welcome to the VitNode documentation! 4 | icon: Power 5 | --- 6 | 7 | 8 | We're working hard to bring you the best documentation experience. 9 | 10 | 11 | Welcome to the docs! You can start writing documents in `/content/docs`. 12 | 13 | ## What is Next? 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/docs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import eslintVitNodeReact from "@vitnode/config/eslint.react"; 3 | import { fileURLToPath } from "node:url"; 4 | import { dirname } from "node:path"; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | export default [ 9 | ...eslintVitNode, 10 | ...eslintVitNodeReact, 11 | { 12 | ignores: [".source"], 13 | }, 14 | { 15 | languageOptions: { 16 | parserOptions: { 17 | project: "./tsconfig.json", 18 | tsconfigRootDir: __dirname, 19 | }, 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/eslint-react/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import eslintVitNodeReact from "@vitnode/config/eslint.react"; 3 | import { fileURLToPath } from "node:url"; 4 | import { dirname } from "node:path"; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | export default [ 9 | ...eslintVitNode, 10 | ...eslintVitNodeReact, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | project: "./tsconfig.json", 15 | tsconfigRootDir: __dirname, 16 | }, 17 | }, 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "noEmit": true, 9 | "baseUrl": ".", 10 | "plugins": [ 11 | { 12 | "name": "next" 13 | } 14 | ], 15 | "paths": { 16 | "@/*": ["./src/*"] 17 | } 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/special-characters.ts: -------------------------------------------------------------------------------- 1 | export const removeSpecialCharacters = ( 2 | text: string, 3 | replaceSpace = true, 4 | ): string => 5 | text 6 | .trimStart() 7 | .trimEnd() 8 | .replace(/\s/g, replaceSpace ? "-" : " ") 9 | .normalize("NFD") 10 | .replace(/\p{Diacritic}/gu, "") 11 | .replace(/[#%&?^|'{}\\/]/g, "") 12 | .replace(/ł/gi, "l") 13 | .replace(/@/g, "-at-") 14 | .replace(/\./g, "-") 15 | .replace(/-+/g, "-") 16 | .trim(); 17 | 18 | export const checkSpecialCharacters = (text: string): boolean => { 19 | return /^[a-z0-9-]+$/i.test(text); 20 | }; 21 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/layout.client.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "fumadocs-ui/utils/cn"; 4 | import { useParams } from "next/navigation"; 5 | 6 | export function useMode(): string | undefined { 7 | const { slug } = useParams(); 8 | 9 | return Array.isArray(slug) && slug.length > 0 ? slug[0] : undefined; 10 | } 11 | 12 | export function Body({ 13 | children, 14 | className, 15 | }: { 16 | children: React.ReactNode; 17 | className?: string; 18 | }): React.ReactElement { 19 | const mode = useMode(); 20 | 21 | return {children}; 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "noEmit": true, 9 | "baseUrl": ".", 10 | "plugins": [ 11 | { 12 | "name": "next" 13 | } 14 | ], 15 | "paths": { 16 | "@/.source": ["./.source/index.ts"], 17 | "@/*": ["./src/*"] 18 | } 19 | }, 20 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "User Interface (UI)", 3 | "description": "Make it pretty", 4 | "icon": "Paintbrush", 5 | "root": true, 6 | "pages": [ 7 | "index", 8 | "hooks", 9 | "typography", 10 | "colors", 11 | "not-found", 12 | "---Components---", 13 | "data-table", 14 | "confirm-action-alert-dialog", 15 | "---Forms---", 16 | "auto-form", 17 | "checkbox", 18 | "combobox-async", 19 | "combobox", 20 | "input", 21 | "radio-group", 22 | "select", 23 | "switch", 24 | "textarea", 25 | "---UI---", 26 | "..." 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx: -------------------------------------------------------------------------------- 1 | import { CallbackSSOView } from "@vitnode/core/views/auth/sso/callback/callback-sso-view"; 2 | 3 | export default async function Page({ 4 | params, 5 | searchParams, 6 | }: { 7 | params: Promise<{ providerId: string }>; 8 | searchParams: Promise>; 9 | }) { 10 | const { providerId } = await params; 11 | const currentSearchParams = await searchParams; 12 | 13 | return ( 14 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/vitnode/src/app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { SignInView } from "../../views/auth/sign-in/sign-in-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ locale, namespace: "core.global" }); 14 | 15 | return { 16 | title: t("login"), 17 | }; 18 | }; 19 | 20 | export default function Page() { 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /packages/vitnode/src/app/register/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { SignUpView } from "../../views/auth/sign-up/sign-up-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ locale, namespace: "core.global" }); 14 | 15 | return { 16 | title: t("register"), 17 | }; 18 | }; 19 | 20 | export default function Page() { 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/src/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { GlobalErrorView } from "@vitnode/core/views/error/global-error-view"; 4 | import { Geist, Geist_Mono } from "next/font/google"; 5 | 6 | import "./global.css"; 7 | 8 | const geistSans = Geist({ 9 | variable: "--font-geist-sans", 10 | subsets: ["latin"], 11 | }); 12 | 13 | const geistMono = Geist_Mono({ 14 | variable: "--font-geist-mono", 15 | subsets: ["latin"], 16 | }); 17 | 18 | export default function GlobalError() { 19 | return ( 20 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/api/get-session-admin-api.ts: -------------------------------------------------------------------------------- 1 | import { adminModule } from "@/api/modules/admin/admin.module"; 2 | import { fetcher } from "@/lib/fetcher"; 3 | 4 | import { redirect } from "../navigation"; 5 | 6 | export const getSessionAdminApi = async () => { 7 | const res = await fetcher(adminModule, { 8 | path: "/session", 9 | method: "get", 10 | module: "admin", 11 | options: { 12 | cache: "force-cache", 13 | }, 14 | }); 15 | 16 | if (res.status !== 200) { 17 | await redirect("/admin"); 18 | 19 | return; 20 | } 21 | 22 | const data = await res.json(); 23 | 24 | return data; 25 | }; 26 | -------------------------------------------------------------------------------- /plugins/blog/src/emails/test-template.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from "@react-email/components"; 2 | import DefaultTemplateEmail, { 3 | type DefaultTemplateEmailProps, 4 | } from "@vitnode/core/emails/default-template"; 5 | 6 | TestTemplateEmail.PreviewProps = 7 | DefaultTemplateEmail.PreviewProps satisfies DefaultTemplateEmailProps; 8 | 9 | export default function TestTemplateEmail({ 10 | user, 11 | ...props 12 | }: DefaultTemplateEmailProps) { 13 | if (!user) return null; 14 | 15 | return ( 16 | 17 | This message is for {user.name} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/docs/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "7", 8 | "when": 1756667224412, 9 | "tag": "0000_puzzling_rictor", 10 | "breakpoints": true 11 | }, 12 | { 13 | "idx": 1, 14 | "version": "7", 15 | "when": 1756795000370, 16 | "tag": "0001_common_spirit", 17 | "breakpoints": true 18 | }, 19 | { 20 | "idx": 2, 21 | "version": "7", 22 | "when": 1756828546879, 23 | "tag": "0002_public_millenium_guard", 24 | "breakpoints": true 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintVitNode from "@vitnode/config/eslint"; 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname } from "node:path"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default [ 8 | ...eslintVitNode, 9 | { 10 | rules: { 11 | "no-console": "off", 12 | }, 13 | }, 14 | { 15 | ignores: ["copy-of-vitnode-app", "copy-of-vitnode-plugin"], 16 | }, 17 | { 18 | languageOptions: { 19 | parserOptions: { 20 | project: "./tsconfig.json", 21 | tsconfigRootDir: __dirname, 22 | }, 23 | }, 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /apps/docs/src/examples/tooltip.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@vitnode/core/components/ui/button"; 4 | import { 5 | Tooltip, 6 | TooltipContent, 7 | TooltipProvider, 8 | TooltipTrigger, 9 | } from "@vitnode/core/components/ui/tooltip"; 10 | 11 | export default function TooltipDemo() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 |

Add to library

20 |
21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { ItemNavAdmin } from "../views/admin/layouts/sidebar/nav/item"; 2 | 3 | interface AdminNavItem 4 | extends Pick< 5 | React.ComponentProps, 6 | "href" | "icon" | "isOpenInNewTab" 7 | > { 8 | id: string; 9 | } 10 | 11 | export interface BuildPluginReturn

{ 12 | admin?: { 13 | nav?: (AdminNavItem & { 14 | items?: Omit[]; 15 | })[]; 16 | }; 17 | pluginId: P; 18 | } 19 | 20 | export function buildPlugin

( 21 | props: BuildPluginReturn

, 22 | ): BuildPluginReturn

{ 23 | return props; 24 | } 25 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Development", 3 | "description": "Make plugins and APIs", 4 | "icon": "Braces", 5 | "root": true, 6 | "pages": [ 7 | "index", 8 | "contribution", 9 | "debugging", 10 | "swagger", 11 | "deployments", 12 | "---Framework---", 13 | "plugins", 14 | "api", 15 | "database", 16 | "advanced", 17 | "---Adapters---", 18 | "captcha", 19 | "email", 20 | "sso", 21 | "cron", 22 | "---Frontend---", 23 | "layouts-and-pages", 24 | "admin-page", 25 | "fetcher", 26 | "---UI---", 27 | "i18n", 28 | "not-found", 29 | "..." 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts: -------------------------------------------------------------------------------- 1 | import { buildApiConfig } from "@vitnode/core/vitnode.config"; 2 | import { drizzle } from "drizzle-orm/postgres-js"; 3 | 4 | export const POSTGRES_URL = 5 | process.env.POSTGRES_URL ?? "postgresql://root:root@localhost:5432/vitnode"; 6 | 7 | export const vitNodeApiConfig = buildApiConfig({ 8 | pathToMessages: async path => await import(`./locales/${path}`), 9 | metadata: { 10 | title: "VitNode", 11 | shortTitle: "VitNode", 12 | }, 13 | plugins: [], 14 | dbProvider: drizzle({ 15 | connection: POSTGRES_URL, 16 | casing: "camelCase", 17 | }), 18 | }); 19 | -------------------------------------------------------------------------------- /plugins/blog/src/config.tsx: -------------------------------------------------------------------------------- 1 | import { buildPlugin } from "@vitnode/core/lib/plugin"; 2 | import { ListIcon, NotebookPenIcon } from "lucide-react"; 3 | 4 | import { CONFIG_PLUGIN } from "@/const"; 5 | 6 | export const blogPlugin = () => { 7 | return buildPlugin({ 8 | pluginId: CONFIG_PLUGIN.pluginId, 9 | admin: { 10 | nav: [ 11 | { 12 | id: "posts", 13 | href: "/admin/blog/posts", 14 | icon: , 15 | }, 16 | { 17 | id: "categories", 18 | href: "/admin/blog/categories", 19 | icon: , 20 | }, 21 | ], 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /plugins/blog/src/database/posts.ts: -------------------------------------------------------------------------------- 1 | import { pgTable } from "drizzle-orm/pg-core"; 2 | 3 | import { blog_categories } from "./categories"; 4 | 5 | export const blog_posts = pgTable("blog_posts", t => ({ 6 | id: t.serial().primaryKey(), 7 | title: t.varchar({ length: 255 }).notNull(), 8 | titleSeo: t.varchar({ length: 255 }).notNull().unique(), 9 | content: t.text().notNull(), 10 | categoryId: t 11 | .integer() 12 | .references(() => blog_categories.id) 13 | .notNull(), 14 | createdAt: t.timestamp().notNull().defaultNow(), 15 | updatedAt: t 16 | .timestamp() 17 | .notNull() 18 | .$onUpdate(() => new Date()), 19 | })).enableRLS(); 20 | -------------------------------------------------------------------------------- /plugins/blog/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "jsx": "react-jsx", 11 | "emitDeclarationOnly": true, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "plugins": [ 15 | { 16 | "name": "next" 17 | } 18 | ], 19 | "paths": { 20 | "@/*": ["./src/*"] 21 | } 22 | }, 23 | "exclude": ["node_modules"], 24 | "include": ["src", "global.d.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/swagger.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Swagger 3 | description: Generate API documentation with OpenAPI. 4 | icon: ListCollapse 5 | --- 6 | 7 | 8 | We're working hard to bring you the best documentation experience. 9 | 10 | 11 | Swagger is a tool that helps you generate API documentation with OpenAPI. It's a powerful tool that can help you write documentation automatically and validate the request. 12 | 13 | ## Usage 14 | 15 | To check documentation, open the following URL in your browser: 16 | 17 | [http://localhost:3000/api/swagger](http://localhost:3000/api/swagger) 18 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { SignInView } from "@vitnode/core/views/auth/sign-in/sign-in-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ locale, namespace: "core.global" }); 14 | 15 | return { 16 | title: t("login"), 17 | }; 18 | }; 19 | 20 | export default function Page() { 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/src/examples/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@vitnode/core/components/ui/button"; 4 | import { toast } from "sonner"; 5 | 6 | export default function SonnerDemo() { 7 | return ( 8 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/monorepo/.gitignore_template: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # Local env files 9 | .env 10 | .env.local 11 | .env.development.local 12 | .env.test.local 13 | .env.production.local 14 | 15 | # Testing 16 | coverage 17 | 18 | # Turbo 19 | .turbo 20 | 21 | # Vercel 22 | .vercel 23 | 24 | # Build Outputs 25 | .next/ 26 | out/ 27 | build 28 | dist 29 | 30 | 31 | # Debug 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | 36 | # Misc 37 | .DS_Store 38 | *.pem 39 | 40 | # VitNode 41 | /docker -------------------------------------------------------------------------------- /packages/vitnode/src/api/modules/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@/api/lib/module"; 2 | import { CONFIG_PLUGIN } from "@/config"; 3 | 4 | import { advancedAdminModule } from "./advanced/advanced.admin.module"; 5 | import { debugAdminModule } from "./debug/debug.admin.module"; 6 | import { sessionAdminRoute } from "./routes/session.route"; 7 | import { usersAdminModule } from "./users/users.admin.module"; 8 | 9 | export const adminModule = buildModule({ 10 | pluginId: CONFIG_PLUGIN.pluginId, 11 | name: "admin", 12 | routes: [sessionAdminRoute], 13 | modules: [usersAdminModule, debugAdminModule, advancedAdminModule], 14 | cronJobs: [], 15 | }); 16 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/auth/password-reset/form/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { usersModule } from "@/api/modules/users/users.module"; 4 | import { fetcher } from "@/lib/fetcher"; 5 | 6 | export const mutationApi = async ({ 7 | email, 8 | captchaToken, 9 | }: { 10 | captchaToken: string; 11 | email: string; 12 | }) => { 13 | const res = await fetcher(usersModule, { 14 | module: "users", 15 | path: "/reset-password", 16 | method: "post", 17 | captchaToken, 18 | args: { 19 | body: { email }, 20 | }, 21 | }); 22 | 23 | if (res.status !== 201) { 24 | return { error: "internal_server_error" }; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { SignUpView } from "@vitnode/core/views/auth/sign-up/sign-up-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ locale, namespace: "core.global" }); 14 | 15 | return { 16 | title: t("register"), 17 | }; 18 | }; 19 | 20 | export default function Page() { 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/hooks/use-mobile.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useMobile 3 | description: A custom hook to determine if the user is on a mobile device 4 | --- 5 | 6 | ## Example 7 | 8 | ```tsx 9 | import { useIsMobile } from '@vitnode/core/hooks/use-mobile'; 10 | ``` 11 | 12 | ```tsx 13 | import { useIsMobile } from '@vitnode/core/hooks/use-mobile'; 14 | 15 | export const ExampleComponent = () => { 16 | const isMobile = useIsMobile(); // [!code ++] 17 | 18 | return ( 19 |

20 | {isMobile ? ( 21 |

You are using a mobile device.

22 | ) : ( 23 |

You are using a desktop device.

24 | )} 25 |
26 | ); 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/get-vitnode-package-version.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from "fs/promises"; 2 | import { dirname, join } from "path"; 3 | import { fileURLToPath } from "url"; 4 | 5 | import type { PackageJSON } from "./packages-json.js"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = dirname(__filename); 9 | 10 | export const getVitnodePackageVersion = async () => { 11 | // Resolve local version of @vitnode/* based on this CLI's package.json 12 | const cliPkg: PackageJSON = JSON.parse( 13 | await readFile(join(__dirname, "..", "..", "..", "package.json"), "utf-8"), 14 | ); 15 | 16 | return `^${cliPkg.version}`; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/api/get-next-cron-run-date.ts: -------------------------------------------------------------------------------- 1 | import cronParser from "cron-parser"; 2 | 3 | export const getNextCronRunDate = ( 4 | schedule: string, 5 | lastRun: Date | null, 6 | ): Date | null => { 7 | try { 8 | const options = { 9 | currentDate: lastRun ?? new Date(0), 10 | }; 11 | 12 | const interval = cronParser.parse(schedule, options); 13 | 14 | return interval.next().toDate(); 15 | } catch (err) { 16 | // eslint-disable-next-line no-console 17 | console.error( 18 | `\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule for nextRun\x1b[0m: ${schedule}`, 19 | err, 20 | ); 21 | 22 | return null; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/form/common/label.tsx: -------------------------------------------------------------------------------- 1 | import { FormLabel } from "@/components/ui/form"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | export const AutoFormLabel = ({ 5 | children, 6 | labelRight, 7 | className, 8 | ...props 9 | }: React.ComponentProps & { 10 | labelRight?: React.ReactNode; 11 | }) => { 12 | return ( 13 | 22 | {children} 23 | {labelRight && {labelRight}} 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /apps/docs/src/components/fumadocs/img.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react"; 2 | 3 | import { ImageZoom } from "fumadocs-ui/components/image-zoom"; 4 | import { cn } from "fumadocs-ui/utils/cn"; 5 | 6 | export const ImgDocs = ({ 7 | className, 8 | imgClassName, 9 | ...props 10 | }: React.ComponentProps & { 11 | imgClassName?: string; 12 | }) => { 13 | return ( 14 |
20 | 21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/api/src/index.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "@hono/node-server"; 2 | import { OpenAPIHono } from "@hono/zod-openapi"; 3 | import { VitNodeAPI } from "@vitnode/core/api/config"; 4 | 5 | import { vitNodeApiConfig } from "./vitnode.api.config.js"; 6 | 7 | const app = new OpenAPIHono().basePath("/api"); 8 | 9 | VitNodeAPI({ 10 | app, 11 | vitNodeApiConfig, 12 | }); 13 | 14 | serve( 15 | { 16 | fetch: app.fetch, 17 | port: 8080, 18 | }, 19 | info => { 20 | const initMessage = "\x1b[34m[VitNode]\x1b[0m"; 21 | 22 | // eslint-disable-next-line no-console 23 | console.log( 24 | `${initMessage} API server is running on http://localhost:${info.port}`, 25 | ); 26 | }, 27 | ); 28 | -------------------------------------------------------------------------------- /apps/docs/src/examples/card.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardAction, 4 | CardContent, 5 | CardDescription, 6 | CardFooter, 7 | CardHeader, 8 | CardTitle, 9 | } from "@vitnode/core/components/ui/card"; 10 | 11 | export default function CardExample() { 12 | return ( 13 | 14 | 15 | Card Title 16 | Card Description 17 | 18 | 19 | This is the content of the card. You can put any content here. 20 | 21 | 22 | Action Button 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-plugin/root/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "jsx": "react-jsx", 11 | "emitDeclarationOnly": true, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "plugins": [ 15 | { 16 | "name": "next" 17 | } 18 | ], 19 | "paths": { 20 | "@/*": ["./src/*"] 21 | } 22 | }, 23 | "exclude": ["node_modules"], 24 | "include": ["src", "global.d.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/badge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Badge 3 | description: Display small labels or indicators. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { Home } from 'lucide-react'; 14 | import { Badge } from '@vitnode/core/components/ui/badge'; 15 | ``` 16 | 17 | ```tsx 18 | 19 | Default 20 | 21 | ``` 22 | 23 | ## Props 24 | 25 | import { TypeTable } from 'fumadocs-ui/components/type-table'; 26 | 27 | 36 | -------------------------------------------------------------------------------- /packages/vitnode/scripts/run-interactive-shell-command.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from "node:child_process"; 2 | 3 | export const runInteractiveShellCommand = async ( 4 | cmd: string, 5 | args: string[] = [], 6 | ) => { 7 | return await new Promise((resolve, reject) => { 8 | const child = spawn(cmd, args, { 9 | stdio: "inherit", 10 | shell: true, 11 | env: process.env, 12 | }); 13 | 14 | child.on("error", error => { 15 | reject(error); 16 | }); 17 | 18 | child.on("close", code => { 19 | if (code !== 0) { 20 | reject(new Error(`Command failed with exit code ${code}`)); 21 | } else { 22 | resolve(true); 23 | } 24 | }); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/validate-pkg.ts: -------------------------------------------------------------------------------- 1 | import validateProjectName from "validate-npm-package-name"; 2 | 3 | type ValidateNpmNameResult = 4 | | { 5 | problems: string[]; 6 | valid: false; 7 | } 8 | | { 9 | valid: true; 10 | }; 11 | 12 | export const validateNpmName = ({ 13 | name, 14 | }: { 15 | name: string; 16 | }): ValidateNpmNameResult => { 17 | const nameValidation = validateProjectName(name); 18 | if (nameValidation.validForNewPackages) { 19 | return { valid: true }; 20 | } 21 | 22 | return { 23 | valid: false, 24 | problems: [ 25 | ...(nameValidation.errors ?? []), 26 | ...(nameValidation.warnings ?? []), 27 | ], 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts: -------------------------------------------------------------------------------- 1 | import { buildApiConfig } from "@vitnode/core/vitnode.config"; 2 | import { config } from "dotenv"; 3 | import { drizzle } from "drizzle-orm/postgres-js"; 4 | 5 | config({ 6 | quiet: true, 7 | }); 8 | 9 | export const POSTGRES_URL = 10 | process.env.POSTGRES_URL ?? "postgresql://root:root@localhost:5432/vitnode"; 11 | 12 | export const vitNodeApiConfig = buildApiConfig({ 13 | plugins: [], 14 | pathToMessages: async path => await import(`./locales/${path}`), 15 | dbProvider: drizzle({ 16 | connection: POSTGRES_URL, 17 | casing: "camelCase", 18 | }), 19 | metadata: { 20 | title: "VitNode API", 21 | shortTitle: "VitNode", 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /packages/vitnode/README.md: -------------------------------------------------------------------------------- 1 | # (VitNode) Core 2 | 3 | This package provides the core functionality for VitNode project. 4 | 5 |

6 |
7 | 8 | 9 | 10 | 11 | VitNode Logo 12 | 13 | 14 |
15 |
16 |

17 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/auth/sso/buttons/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { usersModule } from "@/api/modules/users/users.module"; 4 | import { fetcher } from "@/lib/fetcher"; 5 | import { redirect } from "@/lib/navigation"; 6 | 7 | export const mutationApi = async (providerId: string) => { 8 | const res = await fetcher(usersModule, { 9 | path: "/{providerId}", 10 | method: "post", 11 | module: "users/sso", 12 | args: { 13 | params: { 14 | providerId, 15 | }, 16 | }, 17 | allowSaveCookies: true, 18 | }); 19 | 20 | if (res.status !== 200) { 21 | return { message: "Something is wrong" }; 22 | } 23 | 24 | const data = await res.json(); 25 | await redirect(data.url); 26 | }; 27 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/hover-card.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hover Card 3 | description: A component for displaying additional information on hover. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { 14 | HoverCard, 15 | HoverCardContent, 16 | HoverCardTrigger, 17 | } from '@vitnode/core/components/ui/hover-card'; 18 | ``` 19 | 20 | ```tsx 21 | 22 | Hover 23 | 24 | The React Framework - created and maintained by @vercel. 25 | 26 | 27 | ``` 28 | 29 | ## API Reference 30 | 31 | [Radix UI - Hover Card](https://www.radix-ui.com/primitives/docs/components/hover-card#api-reference) 32 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts: -------------------------------------------------------------------------------- 1 | import { serve } from '@hono/node-server'; 2 | import { OpenAPIHono } from '@hono/zod-openapi'; 3 | import { VitNodeAPI } from '@vitnode/core/api/config'; 4 | 5 | import { vitNodeApiConfig } from './vitnode.api.config.js'; 6 | 7 | const app = new OpenAPIHono().basePath('/api'); 8 | 9 | VitNodeAPI({ 10 | app, 11 | vitNodeApiConfig, 12 | }); 13 | 14 | serve( 15 | { 16 | fetch: app.fetch, 17 | port: 8080, 18 | }, 19 | info => { 20 | const initMessage = '\x1b[34m[VitNode]\x1b[0m'; 21 | 22 | // eslint-disable-next-line no-console 23 | console.log( 24 | `${initMessage} API server is running on http://localhost:${info.port}`, 25 | ); 26 | }, 27 | ); 28 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/sign-in/sign-in-admin-view.tsx: -------------------------------------------------------------------------------- 1 | import { I18nProvider } from "@/components/i18n-provider"; 2 | import { LogoVitNode } from "@/components/logo-vitnode"; 3 | import { Card } from "@/components/ui/card"; 4 | import { FormSignIn } from "@/views/auth/sign-in/form/form"; 5 | 6 | export const SignInAdminView = () => { 7 | return ( 8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/helpers/get-package-manager-from-root.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "node:fs"; 2 | import { join } from "node:path"; 3 | 4 | import type { PackageJSON } from "./packages-json.js"; 5 | 6 | export const getPackageManagerFromRoot = (rootPath: string): string => { 7 | try { 8 | const packageJsonPath = join(rootPath, "package.json"); 9 | const packageJson: PackageJSON = JSON.parse( 10 | readFileSync(packageJsonPath, "utf-8"), 11 | ); 12 | 13 | if (packageJson.packageManager) { 14 | // Extract package manager name from "pnpm@10.18.3" -> "pnpm" 15 | return packageJson.packageManager.split("@")[0]; 16 | } 17 | 18 | return "npm"; 19 | } catch { 20 | return "npm"; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /packages/vitnode/src/api/middlewares/cron-auth.middleware.ts: -------------------------------------------------------------------------------- 1 | import type { Context, Next } from "hono"; 2 | 3 | import { HTTPException } from "hono/http-exception"; 4 | 5 | export const cronAuthMiddleware = () => { 6 | return async (c: Context, next: Next) => { 7 | const cronSecret = c.get("core").cronSecret; 8 | if (!cronSecret) { 9 | throw new HTTPException(403, { message: "Cron access not configured" }); 10 | } 11 | 12 | const authHeader = c.req.header("authorization"); 13 | const providedSecret = authHeader?.replace("Bearer ", ""); 14 | 15 | if (providedSecret !== cronSecret) { 16 | throw new HTTPException(403, { message: "Invalid cron authorization" }); 17 | } 18 | 19 | await next(); 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /apps/docs/src/components/fumadocs/code-block.tsx: -------------------------------------------------------------------------------- 1 | import { highlight } from "fumadocs-core/highlight"; 2 | import { 3 | CodeBlock as BaseCodeBlock, 4 | type CodeBlockProps as BaseCodeBlockProps, 5 | Pre, 6 | } from "fumadocs-ui/components/codeblock"; 7 | 8 | export interface CodeBlockProps { 9 | code: string; 10 | lang: string; 11 | wrapper?: BaseCodeBlockProps; 12 | } 13 | 14 | export async function CodeBlock({ code, lang, wrapper }: CodeBlockProps) { 15 | const rendered = await highlight(code, { 16 | lang, 17 | themes: { 18 | light: "github-light", 19 | dark: "vesper", 20 | }, 21 | components: { 22 | pre: Pre, 23 | }, 24 | }); 25 | 26 | return {rendered}; 27 | } 28 | -------------------------------------------------------------------------------- /apps/docs/src/examples/confirm-action-alert-dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ConfirmActionAlertDialog } from "@vitnode/core/components/confirm-action/confirm-action-alert-dialog"; 4 | import { Button } from "@vitnode/core/components/ui/button"; 5 | import { toast } from "sonner"; 6 | 7 | export default function ConfirmActionAlertDialogExample() { 8 | return ( 9 | { 11 | toast.success("Category deleted successfully!", { 12 | description: "The category has been removed from your list.", 13 | }); 14 | onClose(); 15 | }} 16 | > 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type * as React from "react"; 4 | 5 | import { Label as LabelPrimitive } from "radix-ui"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | function Label({ 10 | className, 11 | ...props 12 | }: React.ComponentProps) { 13 | return ( 14 | 22 | ); 23 | } 24 | 25 | export { Label }; 26 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/api/should-cron-job-run.ts: -------------------------------------------------------------------------------- 1 | import cronParser from "cron-parser"; 2 | 3 | export const shouldCronJobRun = ( 4 | schedule: string, 5 | lastRun: Date | null, 6 | ): boolean => { 7 | try { 8 | const now = new Date(); 9 | const options = { 10 | currentDate: lastRun ?? new Date(0), 11 | }; 12 | 13 | const interval = cronParser.parse(schedule, options); 14 | const nextScheduledRun = interval.next().toDate(); 15 | 16 | return nextScheduledRun <= now; 17 | } catch (err) { 18 | // eslint-disable-next-line no-console 19 | console.error( 20 | `\x1b[34m[VitNode]\x1b[0m \x1b[38;5;208mError parsing schedule\x1b[0m: ${schedule}`, 21 | err, 22 | ); 23 | 24 | return false; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/tiptap/extension.ts: -------------------------------------------------------------------------------- 1 | import type { Extensions } from "@tiptap/react"; 2 | 3 | import TextAlign from "@tiptap/extension-text-align"; 4 | import StarterKit from "@tiptap/starter-kit"; 5 | 6 | export const SUPPORTED_HEADINGS_LEVELS = [1, 2, 3, 4] as const; 7 | 8 | export const tiptapExtensions: Extensions = [ 9 | StarterKit.configure({ 10 | orderedList: { 11 | HTMLAttributes: { 12 | class: "list-decimal", 13 | }, 14 | }, 15 | bulletList: { 16 | HTMLAttributes: { 17 | class: "list-disc", 18 | }, 19 | }, 20 | heading: { 21 | levels: [...SUPPORTED_HEADINGS_LEVELS], 22 | }, 23 | }), 24 | TextAlign.configure({ 25 | types: ["heading", "paragraph"], 26 | }), 27 | ]; 28 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/layout.config.tsx: -------------------------------------------------------------------------------- 1 | import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; 2 | 3 | import { LogoVitNode } from "@/components/logo-vitnode"; 4 | 5 | /** 6 | * Shared layout configurations 7 | * 8 | * you can customise layouts individually from: 9 | * Home Layout: app/(home)/layout.tsx 10 | * Docs Layout: app/docs/layout.tsx 11 | */ 12 | // TODO: Remove this 13 | export const baseOptions: BaseLayoutProps = { 14 | githubUrl: "https://github.com/VitNode/vitnode", 15 | nav: { 16 | title: ( 17 | <> 18 | 19 | 20 | ), 21 | }, 22 | links: [ 23 | { 24 | text: "Documentation", 25 | url: "/docs", 26 | active: "nested-url", 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /apps/docs/src/examples/separator.tsx: -------------------------------------------------------------------------------- 1 | import { Separator } from "@vitnode/core/components/ui/separator"; 2 | 3 | export default function ProgressDemo() { 4 | return ( 5 |
6 |
7 |

Radix Primitives

8 |

9 | An open-source UI component library. 10 |

11 |
12 | 13 |
14 |
Blog
15 | 16 |
Docs
17 | 18 |
Source
19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/vitnode/src/app/login/reset-password/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { PasswordResetView } from "@/views/auth/password-reset/password-reset-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ 14 | locale, 15 | namespace: "core.auth.reset_password", 16 | }); 17 | 18 | return { 19 | title: t("title"), 20 | }; 21 | }; 22 | 23 | export default function Page( 24 | props: React.ComponentProps, 25 | ) { 26 | return ; 27 | } 28 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/src/plugin/questions.ts: -------------------------------------------------------------------------------- 1 | import type { Command } from "commander"; 2 | 3 | import { confirm } from "@inquirer/prompts"; 4 | import color from "picocolors"; 5 | 6 | export interface CreatePluginCliReturn { 7 | install: boolean; 8 | } 9 | 10 | export const createPluginQuestionsCli = async ( 11 | program: Command, 12 | ): Promise => { 13 | const optionsFromProgram = program.opts(); 14 | 15 | const options: CreatePluginCliReturn = { 16 | install: !optionsFromProgram.skipInstall, 17 | }; 18 | 19 | if (optionsFromProgram.skipInstall === undefined) { 20 | options.install = await confirm({ 21 | message: `Would you like to ${color.blue("Install dependencies")}?`, 22 | }); 23 | } 24 | 25 | return options; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/navigation.ts: -------------------------------------------------------------------------------- 1 | import type { QueryParams } from "next-intl/navigation"; 2 | import type { RedirectType } from "next/navigation"; 3 | 4 | import { createNavigation } from "next-intl/navigation"; 5 | import { getLocale } from "next-intl/server"; 6 | 7 | const { 8 | Link, 9 | redirect: redirectFromImport, 10 | usePathname, 11 | useRouter, 12 | getPathname, 13 | } = createNavigation(); 14 | 15 | const redirect = async ( 16 | href: 17 | | string 18 | | { 19 | pathname: string; 20 | query?: QueryParams; 21 | }, 22 | type?: RedirectType, 23 | ) => { 24 | const locale = await getLocale(); 25 | 26 | redirectFromImport({ href, locale }, type); 27 | }; 28 | 29 | export { getPathname, Link, redirect, usePathname, useRouter }; 30 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | description: Welcome to the VitNode documentation! 4 | icon: Power 5 | --- 6 | 7 | 8 | We're working hard to bring you the best documentation experience. 9 | 10 | 11 | ## Get started 12 | 13 | import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; 14 | 15 | 16 | 17 | ```bash tab="bun" 18 | bun create vitnode-app@canary 19 | ``` 20 | 21 | ```bash tab="pnpm" 22 | pnpm create vitnode-app@canary 23 | ``` 24 | 25 | ```bash tab="npm" 26 | npx create-vitnode-app@canary 27 | ``` 28 | 29 | 30 | 31 | ## Why VitNode? 32 | 33 | something here 34 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/card.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Card 3 | description: Display content in a card layout. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```tsx 13 | import { 14 | Card, 15 | CardAction, 16 | CardContent, 17 | CardDescription, 18 | CardFooter, 19 | CardHeader, 20 | CardTitle, 21 | } from '@vitnode/core/components/ui/card'; 22 | ``` 23 | 24 | ```tsx 25 | 26 | 27 | Card Title 28 | Card Description 29 | 30 | 31 | This is the content of the card. You can put any content here. 32 | 33 | 34 | Action Button 35 | 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/hooks/use-before-unload.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useBeforeUnload 3 | description: A custom hook to handle the beforeunload event in React applications 4 | --- 5 | 6 | ## Example 7 | 8 | ```tsx 9 | import { useBeforeUnload } from '@vitnode/core/hooks/use-before-unload'; 10 | ``` 11 | 12 | ```tsx 13 | export const CustomMessageExample = () => { 14 | const [isEditing, setIsEditing] = useState(false); 15 | 16 | // [!code ++:4] 17 | useBeforeUnload( 18 | isEditing, 19 | 'You have unsaved changes. Are you sure you want to leave?', 20 | ); 21 | 22 | return ( 23 |
24 | 25 |

Note: Modern browsers may not show the custom message

26 |
27 | ); 28 | }; 29 | ``` 30 | -------------------------------------------------------------------------------- /apps/docs/src/proxy.ts: -------------------------------------------------------------------------------- 1 | import createMiddleware from "next-intl/middleware"; 2 | 3 | import { vitNodeConfig } from "./vitnode.config"; 4 | 5 | export default createMiddleware({ 6 | locales: vitNodeConfig.i18n.locales.map(locale => locale.code), 7 | defaultLocale: vitNodeConfig.i18n.defaultLocale, 8 | localePrefix: vitNodeConfig.i18n.localePrefix, 9 | }); 10 | 11 | export const config = { 12 | matcher: [ 13 | // Enable a redirect to a matching locale at the root 14 | "/", 15 | 16 | // Set a cookie to remember the previous locale for 17 | // all requests that have a locale prefix 18 | "/(en)/:path*", 19 | 20 | // Enable redirects that add missing locales 21 | // (e.g. `/pathnames` -> `/en/pathnames`) 22 | "/((?!_next|_vercel|api|.*\\..*).*)", 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": false, 6 | "skipLibCheck": true, 7 | "resolveJsonModule": true, 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "isolatedModules": true, 11 | "jsx": "preserve", 12 | "incremental": true, 13 | "composite": false, 14 | "removeComments": true, 15 | "noImplicitAny": false, 16 | "strictNullChecks": true, 17 | "strictPropertyInitialization": false, 18 | "strictBindCallApply": false, 19 | "allowSyntheticDefaultImports": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "erasableSyntaxOnly": true, 22 | "verbatimModuleSyntax": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/vitnode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vitnode/config/tsconfig", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "./", 9 | "outDir": "./dist", 10 | "jsx": "react-jsx", 11 | "emitDeclarationOnly": false, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "plugins": [ 15 | { 16 | "name": "next" 17 | } 18 | ], 19 | "paths": { 20 | "@/*": ["./src/*"] 21 | } 22 | }, 23 | "exclude": ["node_modules"], 24 | "include": [ 25 | "src", 26 | "scripts", 27 | "src/emails", 28 | "vitest.config.ts", 29 | "tsup.config.ts", 30 | "global.d.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/vitnode/src/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState( 7 | undefined, 8 | ); 9 | 10 | React.useEffect(() => { 11 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); 12 | const onChange = () => { 13 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 14 | }; 15 | mql.addEventListener("change", onChange); 16 | // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect 17 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 18 | 19 | return () => mql.removeEventListener("change", onChange); 20 | }, []); 21 | 22 | return !!isMobile; 23 | } 24 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/views/core/advanced/cron/run-action/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | 5 | import { cronAdminModule } from "@/api/modules/admin/advanced/cron/cron.admin.module"; 6 | import { fetcher } from "@/lib/fetcher"; 7 | 8 | export const mutationApi = async (id: number) => { 9 | const res = await fetcher(cronAdminModule, { 10 | path: "/{id}", 11 | method: "post", 12 | module: "cron", 13 | prefixPath: "/admin/advanced", 14 | args: { 15 | params: { id: id.toString() }, 16 | }, 17 | }); 18 | 19 | if (!res.ok) { 20 | return { error: "Failed to run cron job" }; 21 | } 22 | 23 | revalidatePath( 24 | "[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/advanced/cron", 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /plugins/blog/src/views/admin/posts/table/actions/delete/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { fetcher } from "@vitnode/core/lib/fetcher"; 4 | import { revalidatePath } from "next/cache"; 5 | 6 | import { postsAdminModule } from "@/api/modules/admin/posts/posts.admin.module"; 7 | 8 | export const mutationApi = async (id: number) => { 9 | const res = await fetcher(postsAdminModule, { 10 | prefixPath: "/admin", 11 | method: "delete", 12 | path: "/{id}", 13 | module: "posts", 14 | args: { 15 | params: { 16 | id, 17 | }, 18 | }, 19 | }); 20 | 21 | if (!res.ok) { 22 | return { error: await res.text() }; 23 | } 24 | 25 | revalidatePath( 26 | "/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts", 27 | "page", 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/popover.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Popover 3 | description: A popover component for displaying additional content on user interaction. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { Button } from '@vitnode/core/components/ui/button'; 14 | import { 15 | Popover, 16 | PopoverContent, 17 | PopoverTrigger, 18 | } from '@vitnode/core/components/ui/popover'; 19 | ``` 20 | 21 | ```tsx 22 | 23 | 24 | 25 | 26 | Place content for the popover here. 27 | 28 | ``` 29 | 30 | ## API Reference 31 | 32 | [Radix UI - Popover](https://www.radix-ui.com/primitives/docs/components/popover#api-reference) 33 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/auth/password-reset/change-password-form/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import type z from "zod"; 4 | 5 | import type { zodChangePasswordSchema } from "@/api/modules/users/routes/change-password.route"; 6 | 7 | import { usersModule } from "@/api/modules/users/users.module"; 8 | import { fetcher } from "@/lib/fetcher"; 9 | 10 | export const mutationApi = async ({ 11 | password, 12 | token, 13 | userId, 14 | }: z.infer) => { 15 | const res = await fetcher(usersModule, { 16 | module: "users", 17 | path: "/change-password", 18 | method: "post", 19 | args: { 20 | body: { password, token, userId }, 21 | }, 22 | }); 23 | 24 | if (res.status !== 201) { 25 | return { error: "internal_server_error" }; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /apps/docs/src/examples/toggle-group.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | ToggleGroup, 5 | ToggleGroupItem, 6 | } from "@vitnode/core/components/ui/toggle-group"; 7 | import { Bold, Italic, Underline } from "lucide-react"; 8 | 9 | export default function ToggleGroupDemo() { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next/dist/types"; 2 | 3 | import { getTranslations } from "next-intl/server"; 4 | 5 | import { PasswordResetView } from "@vitnode/core/views/auth/password-reset/password-reset-view"; 6 | 7 | export const generateMetadata = async ({ 8 | params, 9 | }: { 10 | params: Promise<{ locale: string }>; 11 | }): Promise => { 12 | const { locale } = await params; 13 | const t = await getTranslations({ 14 | locale, 15 | namespace: "core.auth.reset_password", 16 | }); 17 | 18 | return { 19 | title: t("title"), 20 | }; 21 | }; 22 | 23 | export default function Page( 24 | props: React.ComponentProps, 25 | ) { 26 | return ; 27 | } 28 | -------------------------------------------------------------------------------- /apps/docs/src/examples/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollArea } from "@vitnode/core/components/ui/scroll-area"; 2 | import { Separator } from "@vitnode/core/components/ui/separator"; 3 | import React from "react"; 4 | 5 | const tags = Array.from({ length: 50 }).map( 6 | (_, i, a) => `v1.2.0-beta.${a.length - i}`, 7 | ); 8 | 9 | export default function ScrollAreaDemo() { 10 | return ( 11 | 12 |
13 |

Tags

14 | {tags.map(tag => ( 15 | 16 |
{tag}
17 | 18 |
19 | ))} 20 |
21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/vitnode/src/lib/fetcher/cookie-from-string-to-object.ts: -------------------------------------------------------------------------------- 1 | export const cookieFromStringToObject = ( 2 | str: string[], 3 | ): { 4 | // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents 5 | [key: string]: "lax" | "none" | "strict" | boolean | string | undefined; 6 | Domain: string; 7 | Expires: string; 8 | HttpOnly: boolean; 9 | Path: string; 10 | SameSite: "lax" | "none" | "strict" | boolean | undefined; 11 | Secure: boolean; 12 | }[] => { 13 | return str.map(item => 14 | Object.fromEntries( 15 | item.split("; ").map(v => { 16 | const current = v.split(/=(.*)/s).map(decodeURIComponent); 17 | 18 | if (current.length === 1) { 19 | return [current[0], true]; 20 | } 21 | 22 | return current; 23 | }), 24 | ), 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /plugins/blog/src/views/admin/categories/table/actions/delete/mutation-api.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { fetcher } from "@vitnode/core/lib/fetcher"; 4 | import { revalidatePath } from "next/cache"; 5 | 6 | import { categoriesAdminModule } from "@/api/modules/admin/categories/categories.admin.module"; 7 | 8 | export const mutationApi = async (id: number) => { 9 | const res = await fetcher(categoriesAdminModule, { 10 | prefixPath: "/admin", 11 | method: "delete", 12 | path: "/{id}", 13 | module: "categories", 14 | args: { 15 | params: { 16 | id, 17 | }, 18 | }, 19 | }); 20 | 21 | if (!res.ok) { 22 | return { error: await res.text() }; 23 | } 24 | 25 | revalidatePath( 26 | "/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories", 27 | "page", 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/create-vitnode-app/copy-of-vitnode-app/root/src/proxy.ts: -------------------------------------------------------------------------------- 1 | import createMiddleware from "next-intl/middleware"; 2 | 3 | import { vitNodeConfig } from "./vitnode.config"; 4 | 5 | export default createMiddleware({ 6 | locales: vitNodeConfig.i18n.locales.map(locale => locale.code), 7 | defaultLocale: vitNodeConfig.i18n.defaultLocale, 8 | localePrefix: vitNodeConfig.i18n.localePrefix, 9 | }); 10 | 11 | export const config = { 12 | matcher: [ 13 | // Enable a redirect to a matching locale at the root 14 | "/", 15 | 16 | // Set a cookie to remember the previous locale for 17 | // all requests that have a locale prefix 18 | "/(en)/:path*", 19 | 20 | // Enable redirects that add missing locales 21 | // (e.g. `/pathnames` -> `/en/pathnames`) 22 | "/((?!_next|_vercel|api|.*\\..*).*)", 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /apps/docs/src/examples/switch.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AutoForm } from "@vitnode/core/components/form/auto-form"; 4 | import { AutoFormSwitch } from "@vitnode/core/components/form/fields/switch"; 5 | import { z } from "zod"; 6 | 7 | export default function SwitchExample() { 8 | const formSchema = z.object({ 9 | acceptTerms: z.boolean().refine(val => val, { 10 | message: "You must accept the terms and conditions", 11 | }), 12 | }); 13 | 14 | return ( 15 | ( 20 | 24 | ), 25 | }, 26 | ]} 27 | formSchema={formSchema} 28 | /> 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/docs/src/examples/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AutoForm } from "@vitnode/core/components/form/auto-form"; 4 | import { AutoFormCheckbox } from "@vitnode/core/components/form/fields/checkbox"; 5 | import { z } from "zod"; 6 | 7 | export default function SwitchExample() { 8 | const formSchema = z.object({ 9 | acceptTerms: z.boolean().refine(val => val, { 10 | message: "You must accept the terms and conditions", 11 | }), 12 | }); 13 | 14 | return ( 15 | ( 20 | 24 | ), 25 | }, 26 | ]} 27 | formSchema={formSchema} 28 | /> 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/docs/content/docs/dev/sso/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Single Sign-On (SSO) 3 | description: Simplifies the user experience by allowing access to multiple systems with a single authentication process. 4 | --- 5 | 6 | ## How it works 7 | 8 | 1. The user clicks on the SSO button. 9 | 2. The user is redirected to the SSO provider's login page. 10 | 3. The user logs in. 11 | 4. The user is redirected back to your application with a token. 12 | 5. Your application verifies the token and logs in the user. 13 | 14 | ## Adapters 15 | 16 | Before you can use SSO, you need to provide an adapter to your application. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | or create your own custom SSO adapter... 25 | -------------------------------------------------------------------------------- /apps/docs/content/docs/ui/sonner.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sonner 3 | description: A toast notification component for displaying messages to users. 4 | --- 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { toast } from 'sonner'; 14 | ``` 15 | 16 | ```ts 17 | toast('Event has been created', { 18 | description: 'Sunday, December 03, 2023 at 9:00 AM', 19 | action: { 20 | label: 'Undo', 21 | onClick: () => console.log('Undo'), 22 | }, 23 | }); 24 | ``` 25 | 26 | ```ts 27 | toast.error('An error occurred.'); 28 | ``` 29 | 30 | ```ts 31 | toast.success('Operation was successful.'); 32 | ``` 33 | 34 | ```ts 35 | toast.info('Here is some information.'); 36 | ``` 37 | 38 | ```ts 39 | toast.warning('This is a warning.'); 40 | ``` 41 | 42 | ## Documentation 43 | 44 | [Sonner Documentation](https://sonner.emilkowal.ski/) 45 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type * as React from "react"; 4 | 5 | import { Separator as SeparatorPrimitive } from "radix-ui"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | function Separator({ 10 | className, 11 | orientation = "horizontal", 12 | decorative = true, 13 | ...props 14 | }: React.ComponentProps) { 15 | return ( 16 | 26 | ); 27 | } 28 | 29 | export { Separator }; 30 | -------------------------------------------------------------------------------- /packages/vitnode/src/views/admin/layouts/sidebar/sidebar.tsx: -------------------------------------------------------------------------------- 1 | import { LogoVitNode } from "@/components/logo-vitnode"; 2 | import { 3 | Sidebar, 4 | SidebarContent, 5 | SidebarHeader, 6 | } from "@/components/ui/sidebar"; 7 | import { Link } from "@/lib/navigation"; 8 | 9 | import { NavSidebarAdmin } from "./nav/nav"; 10 | 11 | export const SidebarAdmin = ({ 12 | pluginNav, 13 | }: React.ComponentProps) => { 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vitnode/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import type * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { 6 | return ( 7 |