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.
- package/LICENSE.md +21 -0
- package/README.md +78 -0
- package/copy-of-vitnode-app/.vscode/settings.json +10 -0
- package/copy-of-vitnode-app/README.md +40 -0
- package/copy-of-vitnode-app/api/.gitignore_template +32 -0
- package/copy-of-vitnode-app/api/drizzle.config.ts +12 -0
- package/copy-of-vitnode-app/api/src/i18n.ts +38 -0
- package/copy-of-vitnode-app/api/src/index.ts +27 -0
- package/copy-of-vitnode-app/api/src/vitnode.api.config.ts +40 -0
- package/copy-of-vitnode-app/api/tsconfig.json +17 -0
- package/copy-of-vitnode-app/api-bun/src/index.ts +16 -0
- package/copy-of-vitnode-app/api-single-app/drizzle.config.ts +12 -0
- package/copy-of-vitnode-app/api-single-app/src/routes/api/$.ts +32 -0
- package/copy-of-vitnode-app/api-single-app/src/server/api-bridge.ts +22 -0
- package/copy-of-vitnode-app/api-single-app/src/server/vitnode-api.server.ts +29 -0
- package/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +37 -0
- package/copy-of-vitnode-app/docker/docker-compose.yml +30 -0
- package/copy-of-vitnode-app/eslint/.prettierrc.mjs +11 -0
- package/copy-of-vitnode-app/eslint/eslint.config.mjs +19 -0
- package/copy-of-vitnode-app/eslint-react/.prettierrc.mjs +11 -0
- package/copy-of-vitnode-app/eslint-react/eslint.config.mjs +19 -0
- package/copy-of-vitnode-app/monorepo/.gitignore_template +41 -0
- package/copy-of-vitnode-app/monorepo/apps/api/.env.example +12 -0
- package/copy-of-vitnode-app/monorepo/apps/web/.env.example +12 -0
- package/copy-of-vitnode-app/monorepo/turbo.json +53 -0
- package/copy-of-vitnode-app/root/.env.example +22 -0
- package/copy-of-vitnode-app/root/.gitignore_template +41 -0
- package/copy-of-vitnode-app/root/global.d.ts +28 -0
- package/copy-of-vitnode-app/root/src/components/admin-shell.tsx +72 -0
- package/copy-of-vitnode-app/root/src/components/main-header.tsx +33 -0
- package/copy-of-vitnode-app/root/src/i18n.ts +36 -0
- package/copy-of-vitnode-app/root/src/lib/admin-auth.ts +11 -0
- package/copy-of-vitnode-app/root/src/lib/admin-nav.ts +43 -0
- package/copy-of-vitnode-app/root/src/lib/admin-search.ts +37 -0
- package/copy-of-vitnode-app/root/src/lib/auth.ts +66 -0
- package/copy-of-vitnode-app/root/src/lib/content-registry.ts +51 -0
- package/copy-of-vitnode-app/root/src/lib/document-headers.ts +128 -0
- package/copy-of-vitnode-app/root/src/lib/i18n/runtime.ts +75 -0
- package/copy-of-vitnode-app/root/src/lib/i18n/shared.ts +20 -0
- package/copy-of-vitnode-app/root/src/lib/navigation.ts +24 -0
- package/copy-of-vitnode-app/root/src/lib/page-head.ts +19 -0
- package/copy-of-vitnode-app/root/src/locales/app.ts +28 -0
- package/copy-of-vitnode-app/root/src/locales/packages.ts +43 -0
- package/copy-of-vitnode-app/root/src/router.tsx +227 -0
- package/copy-of-vitnode-app/root/src/routes/__root.tsx +212 -0
- package/copy-of-vitnode-app/root/src/routes/_admin/admin.core.index.tsx +70 -0
- package/copy-of-vitnode-app/root/src/routes/_admin.tsx +258 -0
- package/copy-of-vitnode-app/root/src/routes/_main/index.tsx +60 -0
- package/copy-of-vitnode-app/root/src/routes/_main.tsx +64 -0
- package/copy-of-vitnode-app/root/src/server/messages.server.ts +29 -0
- package/copy-of-vitnode-app/root/src/start.ts +88 -0
- package/copy-of-vitnode-app/root/src/styles.css +171 -0
- package/copy-of-vitnode-app/root/src/vitnode.config.ts +58 -0
- package/copy-of-vitnode-app/root/src/vitnode.shell.config.ts +35 -0
- package/copy-of-vitnode-app/root/tsconfig.json +29 -0
- package/copy-of-vitnode-app/root/tsr.config.json +3 -0
- package/copy-of-vitnode-app/root/vite.config.ts +119 -0
- package/copy-of-vitnode-plugin/root/.swcrc +26 -0
- package/copy-of-vitnode-plugin/root/global.d.ts +21 -0
- package/copy-of-vitnode-plugin/root/npmignore.template +17 -0
- package/copy-of-vitnode-plugin/root/tsconfig.build.json +5 -0
- package/copy-of-vitnode-plugin/root/tsconfig.json +25 -0
- package/dist/src/create/create-package-json.js +276 -0
- package/dist/src/create/create-vitnode.js +204 -0
- package/dist/src/create/package-versions.js +49 -0
- package/dist/src/helpers/get-available-package-managers.js +24 -0
- package/dist/src/helpers/get-package-json.js +2 -0
- package/dist/src/helpers/get-package-manager-from-root.js +15 -0
- package/dist/src/helpers/get-vitnode-package-version.js +9 -0
- package/dist/src/helpers/init-vitnode.js +34 -0
- package/dist/src/helpers/install-dependencies.js +71 -0
- package/dist/src/helpers/is-folder-empty.js +48 -0
- package/dist/src/helpers/is-online.js +38 -0
- package/dist/src/helpers/is-writeable.js +11 -0
- package/dist/src/helpers/packages-json.js +1 -0
- package/dist/src/helpers/validate-pkg.js +14 -0
- package/dist/src/helpers/with-If.js +1 -0
- package/dist/src/index.js +68 -0
- package/dist/src/plugin/create/add-plugin-to-workspace.js +74 -0
- package/dist/src/plugin/create/create-package-json.js +54 -0
- package/dist/src/plugin/create/create-plugin-vitnode.js +80 -0
- package/dist/src/plugin/create/route-templates.js +144 -0
- package/dist/src/plugin/index.js +29 -0
- package/dist/src/plugin/questions.js +14 -0
- package/dist/src/plugin/validation.js +76 -0
- package/dist/src/prepare/prepare.js +19 -0
- package/dist/src/questions.js +83 -0
- package/dist/src/validation.js +40 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- 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,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,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
|