create-vitnode-app 0.0.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +78 -0
  3. package/copy-of-vitnode-app/.vscode/settings.json +10 -0
  4. package/copy-of-vitnode-app/README.md +40 -0
  5. package/copy-of-vitnode-app/api/.gitignore_template +32 -0
  6. package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
  7. package/copy-of-vitnode-app/api/src/i18n.ts +38 -0
  8. package/copy-of-vitnode-app/api/src/index.ts +27 -0
  9. package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +40 -0
  10. package/copy-of-vitnode-app/api/tsconfig.json +17 -0
  11. package/copy-of-vitnode-app/api-bun/src/index.ts +16 -0
  12. package/copy-of-vitnode-app/api-single-app/drizzle.config.ts +12 -0
  13. package/copy-of-vitnode-app/api-single-app/src/routes/api/$.ts +32 -0
  14. package/copy-of-vitnode-app/api-single-app/src/server/api-bridge.ts +22 -0
  15. package/copy-of-vitnode-app/api-single-app/src/server/vitnode-api.server.ts +29 -0
  16. package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +37 -0
  17. package/copy-of-vitnode-app/docker/docker-compose.yml +30 -0
  18. package/copy-of-vitnode-app/eslint/.prettierrc.mjs +11 -0
  19. package/copy-of-vitnode-app/eslint/eslint.config.mjs +19 -0
  20. package/copy-of-vitnode-app/eslint-react/.prettierrc.mjs +11 -0
  21. package/copy-of-vitnode-app/eslint-react/eslint.config.mjs +19 -0
  22. package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
  23. package/copy-of-vitnode-app/monorepo/apps/api/.env.example +12 -0
  24. package/copy-of-vitnode-app/monorepo/apps/web/.env.example +12 -0
  25. package/copy-of-vitnode-app/monorepo/turbo.json +53 -0
  26. package/copy-of-vitnode-app/root/.env.example +22 -0
  27. package/copy-of-vitnode-app/root/.gitignore_template +41 -0
  28. package/copy-of-vitnode-app/root/global.d.ts +28 -0
  29. package/copy-of-vitnode-app/root/src/components/admin-shell.tsx +72 -0
  30. package/copy-of-vitnode-app/root/src/components/main-header.tsx +33 -0
  31. package/copy-of-vitnode-app/root/src/i18n.ts +36 -0
  32. package/copy-of-vitnode-app/root/src/lib/admin-auth.ts +11 -0
  33. package/copy-of-vitnode-app/root/src/lib/admin-nav.ts +43 -0
  34. package/copy-of-vitnode-app/root/src/lib/admin-search.ts +37 -0
  35. package/copy-of-vitnode-app/root/src/lib/auth.ts +66 -0
  36. package/copy-of-vitnode-app/root/src/lib/content-registry.ts +51 -0
  37. package/copy-of-vitnode-app/root/src/lib/document-headers.ts +128 -0
  38. package/copy-of-vitnode-app/root/src/lib/i18n/runtime.ts +75 -0
  39. package/copy-of-vitnode-app/root/src/lib/i18n/shared.ts +20 -0
  40. package/copy-of-vitnode-app/root/src/lib/navigation.ts +24 -0
  41. package/copy-of-vitnode-app/root/src/lib/page-head.ts +19 -0
  42. package/copy-of-vitnode-app/root/src/locales/app.ts +28 -0
  43. package/copy-of-vitnode-app/root/src/locales/packages.ts +43 -0
  44. package/copy-of-vitnode-app/root/src/router.tsx +227 -0
  45. package/copy-of-vitnode-app/root/src/routes/__root.tsx +212 -0
  46. package/copy-of-vitnode-app/root/src/routes/_admin/admin.core.index.tsx +70 -0
  47. package/copy-of-vitnode-app/root/src/routes/_admin.tsx +258 -0
  48. package/copy-of-vitnode-app/root/src/routes/_main/index.tsx +60 -0
  49. package/copy-of-vitnode-app/root/src/routes/_main.tsx +64 -0
  50. package/copy-of-vitnode-app/root/src/server/messages.server.ts +29 -0
  51. package/copy-of-vitnode-app/root/src/start.ts +88 -0
  52. package/copy-of-vitnode-app/root/src/styles.css +171 -0
  53. package/copy-of-vitnode-app/root/src/vitnode.config.ts +58 -0
  54. package/copy-of-vitnode-app/root/src/vitnode.shell.config.ts +35 -0
  55. package/copy-of-vitnode-app/root/tsconfig.json +29 -0
  56. package/copy-of-vitnode-app/root/tsr.config.json +3 -0
  57. package/copy-of-vitnode-app/root/vite.config.ts +119 -0
  58. package/copy-of-vitnode-plugin/root/.swcrc +26 -0
  59. package/copy-of-vitnode-plugin/root/global.d.ts +21 -0
  60. package/copy-of-vitnode-plugin/root/npmignore.template +17 -0
  61. package/copy-of-vitnode-plugin/root/tsconfig.build.json +5 -0
  62. package/copy-of-vitnode-plugin/root/tsconfig.json +25 -0
  63. package/dist/src/create/create-package-json.js +276 -0
  64. package/dist/src/create/create-vitnode.js +204 -0
  65. package/dist/src/create/package-versions.js +49 -0
  66. package/dist/src/helpers/get-available-package-managers.js +24 -0
  67. package/dist/src/helpers/get-package-json.js +2 -0
  68. package/dist/src/helpers/get-package-manager-from-root.js +15 -0
  69. package/dist/src/helpers/get-vitnode-package-version.js +9 -0
  70. package/dist/src/helpers/init-vitnode.js +34 -0
  71. package/dist/src/helpers/install-dependencies.js +71 -0
  72. package/dist/src/helpers/is-folder-empty.js +48 -0
  73. package/dist/src/helpers/is-online.js +38 -0
  74. package/dist/src/helpers/is-writeable.js +11 -0
  75. package/dist/src/helpers/packages-json.js +1 -0
  76. package/dist/src/helpers/validate-pkg.js +14 -0
  77. package/dist/src/helpers/with-If.js +1 -0
  78. package/dist/src/index.js +68 -0
  79. package/dist/src/plugin/create/add-plugin-to-workspace.js +74 -0
  80. package/dist/src/plugin/create/create-package-json.js +54 -0
  81. package/dist/src/plugin/create/create-plugin-vitnode.js +80 -0
  82. package/dist/src/plugin/create/route-templates.js +144 -0
  83. package/dist/src/plugin/index.js +29 -0
  84. package/dist/src/plugin/questions.js +14 -0
  85. package/dist/src/plugin/validation.js +76 -0
  86. package/dist/src/prepare/prepare.js +19 -0
  87. package/dist/src/questions.js +83 -0
  88. package/dist/src/validation.js +40 -0
  89. package/dist/tsconfig.build.tsbuildinfo +1 -0
  90. package/package.json +55 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 aXenDev Maciej Balcerzak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Create VitNode App
2
+
3
+ `create-vitnode-app` scaffolds a TanStack Start and Hono VitNode application,
4
+ or an installable plugin for an existing VitNode workspace.
5
+
6
+ <p align="center">
7
+ <a href="https://vitnode.com/" target="_blank">
8
+ <picture>
9
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_dark.svg">
10
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_light.svg">
11
+ <img alt="VitNode" src="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_light.svg" width="400">
12
+ </picture>
13
+ </a>
14
+ </p>
15
+
16
+ ## Create an app
17
+
18
+ ### Bun
19
+
20
+ ```bash
21
+ bun create vitnode-app@latest
22
+ ```
23
+
24
+ ### pnpm
25
+
26
+ ```bash
27
+ pnpm create vitnode-app@latest
28
+ ```
29
+
30
+ ### npm
31
+
32
+ ```bash
33
+ npm create vitnode-app@latest
34
+ ```
35
+
36
+ Choose Turborepo during setup if you will build plugins. It gives your project a
37
+ workspace root and a `plugins/*` home.
38
+
39
+ ## Create a plugin
40
+
41
+ Run this from an existing VitNode workspace, then enter the plugin package name
42
+ when prompted:
43
+
44
+ ### Bun
45
+
46
+ ```bash
47
+ bun create vitnode-app@latest --plugin
48
+ ```
49
+
50
+ ### pnpm
51
+
52
+ ```bash
53
+ pnpm create vitnode-app@latest --plugin
54
+ ```
55
+
56
+ ### npm
57
+
58
+ ```bash
59
+ npm create vitnode-app@latest -- --plugin
60
+ ```
61
+
62
+ The generator creates the package and adds its workspace dependency. Register it
63
+ in the host’s `vitnode.config.ts` to enable the feature.
64
+
65
+ ## Options
66
+
67
+ | Option | Description |
68
+ | --- | --- |
69
+ | `--package-manager` | Choose `npm` or `pnpm` for the generated project. |
70
+ | `--eslint` | Include ESLint and Prettier configuration. |
71
+ | `--skip-install` | Skip dependency installation after scaffolding. |
72
+ | `--mode` | Choose `singleApp`, `apiMonorepo`, or `onlyApi`. |
73
+ | `--monorepo` | Create a workspace layout for plugins and multiple applications. |
74
+ | `--docker` | Include local Docker services. |
75
+ | `--plugin` | Create a VitNode plugin package. |
76
+
77
+ Read the [VitNode documentation](https://vitnode.com/docs/dev) for setup,
78
+ plugins, deployment, and AdminCP guides.
@@ -0,0 +1,10 @@
1
+ {
2
+ "cSpell.words": [
3
+ "vitnode"
4
+ ],
5
+ "github.copilot.chat.commitMessageGeneration.instructions": [
6
+ {
7
+ "text": "Follow the Conventional Commits format strictly for commit messages. Use the structure below:\n\n```\n<type>[optional scope]: <gitmoji> <description>\n```\n\nGuidelines:\n\n1. **Type and Scope**: Choose an appropriate type (e.g., `feat`, `fix`, `refactor`, `docs`) and optional scope to describe the affected module or feature.\n\n2. **Gitmoji**: Include a relevant `gitmoji` that best represents the nature of the change.\n\n3. **Description**: Write a concise, informative description in the header; use backticks if referencing code or specific terms.\n\nCommit messages should be clear, informative, and professional, aiding readability and project tracking."
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,40 @@
1
+ <p align="center">
2
+ <br>
3
+ <a href="https://vitnode.com/" target="_blank">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_dark.svg">
6
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_light.svg">
7
+ <img alt="VitNode Logo" src="https://raw.githubusercontent.com/VitNode/vitnode/canary/assets/logo/vitnode_logo_light.svg" width="400">
8
+ </picture>
9
+ </a>
10
+ <br>
11
+ <br>
12
+ </p>
13
+
14
+ # VitNode App
15
+
16
+ This is a basic template for a [VitNode](https://vitnode.com/) app.
17
+
18
+ ## Getting Started
19
+
20
+ To get started, run the following commands:
21
+
22
+ ```bash
23
+ pnpm i
24
+ ```
25
+
26
+ Open {{START_URLS}} with your browser to see the result.
27
+
28
+ ## Development
29
+
30
+ To start the development server, you need to create a `.env` file from the provided `.env.example` file.
31
+
32
+ In the `development` environment, you can just copy and paste the content of `.env.example` to `.env`.
33
+
34
+ ### Development server
35
+
36
+ To start the development server, run the following command:
37
+
38
+ ```bash
39
+ pnpm dev
40
+ ```
@@ -0,0 +1,32 @@
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
30
+
31
+ # Local storage adapter uploads
32
+ /public/uploads
@@ -0,0 +1,12 @@
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
+ });
@@ -0,0 +1,38 @@
1
+ import type { VitNodeI18nConfig } from "@vitnode/core/lib/i18n/types";
2
+
3
+ /**
4
+ * The languages this installation serves.
5
+ *
6
+ * The API half of a split deployment. `apps/web` has a file of the same name,
7
+ * and the two are one declaration in two places by necessity rather than by
8
+ * design: they are separate packages, so neither can import the other's.
9
+ *
10
+ * They have to agree, and this is the copy that matters most - this app owns the
11
+ * schema, so `vitnode db:prepare` seeds `core_languages` from *this* list. A
12
+ * language that is here and not in the web app's renders nowhere; one that is in
13
+ * the web app's and not here has no row in the database, and everything keyed on
14
+ * a language row has nowhere to put it.
15
+ *
16
+ * Deliberately not discovered: nothing walks the filesystem looking for the web
17
+ * app's config. A bootstrap that guessed at a sibling application is exactly
18
+ * what this replaced, and it guessed wrong the moment the two were not laid out
19
+ * the way it expected.
20
+ *
21
+ * Packages ship their own translations, so a new locale needs no `messages`
22
+ * entry - anything untranslated falls back to `defaultLocale` key by key.
23
+ */
24
+ export const i18n = {
25
+ defaultLocale: "en",
26
+ /**
27
+ * Explicit, because this API renders emails on a server: without one, dates
28
+ * format in whatever zone the host happens to run in.
29
+ */
30
+ timeZone: "UTC",
31
+ locales: [
32
+ {
33
+ code: "en",
34
+ name: "English",
35
+ },
36
+ ],
37
+ messages: {},
38
+ } satisfies VitNodeI18nConfig;
@@ -0,0 +1,27 @@
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: 8000,
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
+ );
@@ -0,0 +1,40 @@
1
+ import { buildApiConfig } from "@vitnode/core/vitnode.config";
2
+ import { config } from "dotenv";
3
+ import { coreRelations } from "@vitnode/core/database/relations";
4
+ import { drizzle } from "drizzle-orm/postgres-js";
5
+
6
+ import { i18n } from "./i18n.js";
7
+
8
+ config({
9
+ quiet: true,
10
+ });
11
+
12
+ export const POSTGRES_URL =
13
+ process.env.POSTGRES_URL ?? "postgresql://root:root@localhost:5432/vitnode";
14
+
15
+ export const vitNodeApiConfig = buildApiConfig({
16
+ plugins: [],
17
+ /**
18
+ * The installation's languages - see `src/i18n.ts`, and keep it in step with
19
+ * the web app's file of the same name.
20
+ *
21
+ * This app owns the schema, so `vitnode db:prepare` seeds `core_languages`
22
+ * from this list. Leave it out and the seed falls back to `en` alone, whatever
23
+ * the site serves.
24
+ */
25
+ i18n,
26
+ dbProvider: drizzle({
27
+ connection: POSTGRES_URL,
28
+ relations: coreRelations,
29
+ }),
30
+ // Redis is opt-in: only enabled when REDIS_URL is set. Without it the cache
31
+ // is a no-op, the rate limiter uses in-memory storage, and WebSockets run in
32
+ // single-instance mode.
33
+ redis: process.env.REDIS_URL
34
+ ? { url: process.env.REDIS_URL, password: process.env.REDIS_PASSWORD }
35
+ : undefined,
36
+ metadata: {
37
+ title: "VitNode API",
38
+ shortTitle: "VitNode",
39
+ },
40
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@vitnode/config/tsconfig",
4
+ "compilerOptions": {
5
+ "target": "ESNext",
6
+ "module": "NodeNext",
7
+ "rootDir": "./",
8
+ "outDir": "./dist",
9
+ "incremental": false,
10
+ "types": ["node"],
11
+ "paths": {
12
+ "@/*": ["./src/*"]
13
+ }
14
+ },
15
+ "include": ["src", "global.d.ts"],
16
+ "exclude": ["node_modules"]
17
+ }
@@ -0,0 +1,16 @@
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: 8000,
15
+ fetch: app.fetch,
16
+ };
@@ -0,0 +1,12 @@
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
+ });
@@ -0,0 +1,32 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { apiBridge } from '#/server/vitnode-api.server'
4
+
5
+ /**
6
+ * `/api/*` - the existing VitNode Hono application, mounted.
7
+ *
8
+ * `server` is the only option on this route on purpose. TanStack Start prunes a
9
+ * route file whose sole option is `server` out of the client route tree
10
+ * entirely (and its client code-splitter deletes the `server` node on top of
11
+ * that), so none of the API - Hono, Drizzle, the plugins - can reach the browser
12
+ * bundle. The `.server.ts` import is refused by import protection if that ever
13
+ * stops being true.
14
+ *
15
+ * `ANY` rather than a handler per method: routing, OpenAPI, middleware, auth,
16
+ * plugin mounting and error handling all stay inside Hono, exactly as they are
17
+ * when the same app runs standalone in `apps/api` or under the Next.js catch-all
18
+ * in `apps/docs`. `ANY` is part of the framework's `RouteMethod` union and is
19
+ * what Start falls back to for any method it was given no handler for - `HEAD`
20
+ * included, where it calls this handler and strips the response body itself.
21
+ * So the API keeps answering for methods this file has never heard of, which is
22
+ * the point: there is nothing here to keep in sync with the API's routes.
23
+ *
24
+ * `src/tests/api-server-route.test.ts` drives the real request handler over this
25
+ * route to hold that to every method the API needs.
26
+ */
27
+ export const Route = createFileRoute('/api/$')({
28
+ server: {
29
+ handlers: ({ createHandlers }) =>
30
+ createHandlers({ ANY: async ({ request }) => apiBridge(request) }),
31
+ },
32
+ })
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The seam between the web runtime and the Hono API.
3
+ *
4
+ * A bridge is handed the `Request` the browser (or an SSR loader) made to
5
+ * `/api/*` on this origin and answers it with whatever Hono answers. It is one
6
+ * line, and that is the point: status, body, every `Set-Cookie`, the cookie and
7
+ * `x-forwarded-*` headers the API reads are all already correct on the request
8
+ * the platform built, and stay correct exactly as long as nobody rebuilds them.
9
+ *
10
+ * `src/tests/api-bridge-contract.ts` holds this to that behaviour, including the
11
+ * ways a rebuilt request loses it.
12
+ */
13
+ export type ApiBridge = (request: Request) => Promise<Response> | Response
14
+
15
+ interface FetchableApp {
16
+ fetch: (request: Request) => Promise<Response> | Response
17
+ }
18
+
19
+ export const createApiBridge =
20
+ (app: FetchableApp): ApiBridge =>
21
+ async (request) =>
22
+ app.fetch(request)
@@ -0,0 +1,29 @@
1
+ import '@tanstack/react-start/server-only'
2
+ import { OpenAPIHono } from '@hono/zod-openapi'
3
+ import { VitNodeAPI } from '@vitnode/core/api/config'
4
+
5
+ import { createApiBridge } from '#/server/api-bridge'
6
+ import { vitNodeApiConfig } from '#/vitnode.api.config'
7
+
8
+ // The same two lines `apps/api` and `apps/docs` run. `basePath("/api")` is what
9
+ // makes the mount point part of the API's own routing, so every path the plugins
10
+ // register - `/api/@vitnode/core/...` - resolves identically here.
11
+ const createVitNodeApi = () => {
12
+ const app = new OpenAPIHono().basePath('/api')
13
+
14
+ VitNodeAPI({ app, vitNodeApiConfig })
15
+
16
+ return app
17
+ }
18
+
19
+ // `VitNodeAPI` is a boot step, not a request step: it opens the Redis client,
20
+ // starts the cron scheduler and registers every plugin route. Vite re-evaluates
21
+ // server modules on HMR, so the instance is parked on `globalThis` to keep one
22
+ // API per process instead of one per edit.
23
+ const cache = globalThis as typeof globalThis & {
24
+ __vitnodeApi?: ReturnType<typeof createVitNodeApi>
25
+ }
26
+
27
+ export const vitNodeApi = (cache.__vitnodeApi ??= createVitNodeApi())
28
+
29
+ export const apiBridge = createApiBridge(vitNodeApi)
@@ -0,0 +1,37 @@
1
+ import { buildApiConfig } from "@vitnode/core/vitnode.config";
2
+ import { coreRelations } from "@vitnode/core/database/relations";
3
+ import { drizzle } from "drizzle-orm/postgres-js";
4
+
5
+ import { i18n } from "./i18n";
6
+
7
+ export const POSTGRES_URL =
8
+ process.env.POSTGRES_URL ?? "postgresql://root:root@localhost:5432/vitnode";
9
+
10
+ export const vitNodeApiConfig = buildApiConfig({
11
+ metadata: {
12
+ title: "VitNode",
13
+ shortTitle: "VitNode",
14
+ },
15
+ plugins: [],
16
+ /**
17
+ * The same `src/i18n.ts` the frontend config reads, because this app is both:
18
+ * one declaration of which languages exist, spread into `buildConfig` through
19
+ * `vitnode.shell.config.ts` and passed here.
20
+ *
21
+ * It is also what `vitnode db:prepare` seeds `core_languages` from - this app
22
+ * owns the schema - so adding a language here and re-running `dev` inserts its
23
+ * row. Leave it out and the seed falls back to `en` alone, whatever the site
24
+ * serves.
25
+ */
26
+ i18n,
27
+ dbProvider: drizzle({
28
+ connection: POSTGRES_URL,
29
+ relations: coreRelations,
30
+ }),
31
+ // Redis is opt-in: only enabled when REDIS_URL is set. Without it the cache
32
+ // is a no-op, the rate limiter uses in-memory storage, and WebSockets run in
33
+ // single-instance mode.
34
+ redis: process.env.REDIS_URL
35
+ ? { url: process.env.REDIS_URL, password: process.env.REDIS_PASSWORD }
36
+ : undefined,
37
+ });
@@ -0,0 +1,30 @@
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
+ cache:
20
+ container_name: vitnode_redis_dev
21
+ image: redis:8.8-alpine
22
+ restart: unless-stopped
23
+ command: redis-server --requirepass ${REDIS_PASSWORD-root}
24
+ ports:
25
+ - '6379:6379'
26
+ networks:
27
+ - vitnode_dev
28
+
29
+ networks:
30
+ vitnode_dev:
@@ -0,0 +1,11 @@
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;
@@ -0,0 +1,19 @@
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
+ ];
@@ -0,0 +1,11 @@
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;
@@ -0,0 +1,19 @@
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
+ ];
@@ -0,0 +1,41 @@
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
+ .output/
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
@@ -0,0 +1,12 @@
1
+ POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode
2
+ REDIS_URL=redis://localhost:6379
3
+
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
10
+
11
+ # === Docker Redis ===
12
+ REDIS_PASSWORD=root
@@ -0,0 +1,12 @@
1
+ NEXT_PUBLIC_API_URL=http://localhost:8000
2
+
3
+ # Optional. Set these to back the Next.js caches with Redis, so cached pages and
4
+ # `use cache` entries are shared between instances and a tag revalidation on one
5
+ # instance is seen by all of them.
6
+ #
7
+ # The web process needs its own copy: the cache handlers are loaded by Next.js
8
+ # itself and read the environment directly - they cannot see the API's
9
+ # `vitnode.api.config.ts`. Point them at the same Redis the API uses.
10
+ # https://vitnode.com/docs/dev/advanced/redis#caching-nextjs-output
11
+ # REDIS_URL=redis://localhost:6379
12
+ # REDIS_PASSWORD=root