create-veap 0.1.4 → 0.3.1
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/README.md +72 -1
- package/bin/veap.js +1 -0
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +6 -8
- package/dist/index.js +5 -0
- package/dist/package-manager.d.ts +29 -10
- package/dist/package-manager.js +85 -22
- package/package.json +1 -1
- package/stubs/docker/.dockerignore.stub +39 -39
- package/stubs/docker/Dockerfile.stub +49 -49
- package/stubs/docker/compose.yml.stub +58 -58
- package/stubs/overlay-full/AGENTS.md.stub +1 -1
- package/stubs/overlay-full/README.md.stub +132 -0
- package/stubs/overlay-full/app/(site)/layout.tsx.stub +10 -0
- package/stubs/overlay-full/app/[[...catchAll]]/page.tsx.stub +47 -47
- package/stubs/overlay-full/app/api/[...catchAll]/route.ts.stub +111 -111
- package/stubs/overlay-full/app/error.tsx.stub +34 -34
- package/stubs/overlay-full/app/globals.css.stub +123 -123
- package/stubs/overlay-full/app/layout.tsx.stub +71 -71
- package/stubs/overlay-full/app/storage/[...path]/route.ts.stub +61 -61
- package/stubs/overlay-full/components/footer.tsx.stub +25 -0
- package/stubs/overlay-full/components/navbar.tsx.stub +61 -0
- package/stubs/overlay-full/components/site-layout.tsx.stub +22 -0
- package/stubs/overlay-full/lib/plugins.gen.ts.stub +6 -6
- package/stubs/overlay-full/lib/veap.ts.stub +27 -26
- package/stubs/overlay-full/migrations/index.ts.stub +1 -1
- package/stubs/overlay-full/next.config.ts.stub +44 -34
- package/stubs/overlay-full/proxy.ts.stub +27 -27
|
@@ -11,7 +11,7 @@ This project is a **Veap** application: the official `create-next-app` base plus
|
|
|
11
11
|
- `app/[[...catchAll]]/page.tsx` - every page render goes through `VeapRouter` (route tree built from plugins + app routes). Do not add competing page routes without understanding the router.
|
|
12
12
|
- `app/api/[...catchAll]/route.ts` - API pipeline: middlewares, auth, roles/permissions resolved from the route tree.
|
|
13
13
|
- `app/layout.tsx`, `app/not-found.tsx` - root shell with the bootstrap and the **force-dynamic contract (below)**.
|
|
14
|
-
- `plugins
|
|
14
|
+
- `plugins/` - workspace packages (pnpm workspace / npm workspaces).
|
|
15
15
|
- `migrations/` - app migrations (`veap make:migration`).
|
|
16
16
|
|
|
17
17
|
## Critical Contracts (do not break)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
A modern full-stack web application built with [Veap](https://veap.pl) and Next.js 16 (App Router & React Server Components).
|
|
4
|
+
|
|
5
|
+
## What is Veap?
|
|
6
|
+
|
|
7
|
+
**Veap** is a modular application framework built on top of the Next.js App Router. It combines Next.js frontend flexibility with robust, enterprise-grade backend architectural primitives:
|
|
8
|
+
|
|
9
|
+
- **Clean Architecture & Service Providers**: Inversion of control container and modular service providers configured in `lib/veap.ts`.
|
|
10
|
+
- **Virtual Routing**: Dynamic route tree serving installed plugin routes and application routes through `app/[[...catchAll]]/page.tsx` and `app/api/[...catchAll]/route.ts`.
|
|
11
|
+
- **Plugin System**: Isolated, composable packages located in `plugins/` with declarative extensions and widgets.
|
|
12
|
+
- **ActiveRecord ORM**: Eloquent-style models and query builder backed by Knex with transactions and migrations.
|
|
13
|
+
- **Authentication & RBAC**: Built-in session management, user models, role-based access control, and password hashing.
|
|
14
|
+
- **Storage & Communication**: Unified file storage drivers and mail transports.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Getting Started
|
|
19
|
+
|
|
20
|
+
### 1. Requirements
|
|
21
|
+
|
|
22
|
+
- **Node.js** `>= 22` or **Bun** `>= 1.2`
|
|
23
|
+
- Supported package managers: `bun`, `pnpm`, `npm`, or `yarn`
|
|
24
|
+
|
|
25
|
+
### 2. Environment Configuration
|
|
26
|
+
|
|
27
|
+
During project initialization, a default `.env` file is generated with a secure random `ENCRYPTION_KEY` and a local SQLite database configuration:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
DATABASE_CLIENT=sqlite3
|
|
31
|
+
DATABASE_URL=./storage/veap.sqlite
|
|
32
|
+
ENCRYPTION_KEY=<generated-key>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> **Important**: The application enforces fail-fast verification on `ENCRYPTION_KEY`. Never commit `.env` to version control.
|
|
36
|
+
|
|
37
|
+
### 3. Run the Development Server
|
|
38
|
+
|
|
39
|
+
Start the local development server:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Using bun
|
|
43
|
+
bun dev
|
|
44
|
+
|
|
45
|
+
# Or using pnpm
|
|
46
|
+
pnpm dev
|
|
47
|
+
|
|
48
|
+
# Or using npm
|
|
49
|
+
npm run dev
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to view the application.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Available Scripts
|
|
57
|
+
|
|
58
|
+
| Command | Description |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `bun dev` | Starts the Next.js development server with Turbopack |
|
|
61
|
+
| `bun run build` | Builds the application for production |
|
|
62
|
+
| `bun run start` | Runs the production build |
|
|
63
|
+
| `bun run lint` | Runs ESLint checks |
|
|
64
|
+
|
|
65
|
+
*(Replace `bun` with `pnpm` or `npm run` depending on your package manager)*
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Veap CLI
|
|
70
|
+
|
|
71
|
+
The `veap` CLI manages plugins, database migrations, and framework components:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Add and register a plugin package
|
|
75
|
+
veap add <plugin-name>
|
|
76
|
+
|
|
77
|
+
# Scaffold a new workspace plugin in plugins/
|
|
78
|
+
veap make:plugin <plugin-name>
|
|
79
|
+
|
|
80
|
+
# Create a new database migration
|
|
81
|
+
veap make:migration <migration-name>
|
|
82
|
+
|
|
83
|
+
# Scaffold a new site layout template
|
|
84
|
+
veap make:template <template-name>
|
|
85
|
+
|
|
86
|
+
# Re-synchronize and generate lib/plugins.gen.ts
|
|
87
|
+
veap register
|
|
88
|
+
|
|
89
|
+
# Generate Docker deployment configuration
|
|
90
|
+
veap docker
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Project Structure
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
├── app/
|
|
99
|
+
│ ├── (site)/ # Declarative site layout route group
|
|
100
|
+
│ ├── [[...catchAll]]/ # Veap virtual page router
|
|
101
|
+
│ ├── api/[...catchAll]/ # Veap API pipeline (middlewares, RBAC, auth)
|
|
102
|
+
│ ├── storage/ # Local storage route handler
|
|
103
|
+
│ ├── layout.tsx # Root layout shell with force-dynamic bootstrap
|
|
104
|
+
│ └── globals.css # Tailwind CSS styling
|
|
105
|
+
├── components/ # Reusable UI components (SiteLayout, Navbar, Footer)
|
|
106
|
+
├── lib/
|
|
107
|
+
│ ├── veap.ts # Composition root: Application configuration & bootstrap
|
|
108
|
+
│ └── plugins.gen.ts # Generated plugin registry (managed by veap register)
|
|
109
|
+
├── migrations/ # Database migrations
|
|
110
|
+
├── plugins/ # Local workspace plugins
|
|
111
|
+
├── storage/ # Local SQLite database & uploaded files
|
|
112
|
+
├── AGENTS.md # AI coding agent instructions and architectural contracts
|
|
113
|
+
└── next.config.ts # Next.js configuration with Veap & Turbopack support
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Documentation & Resources
|
|
119
|
+
|
|
120
|
+
Explore the official Veap documentation to learn more about architecture, plugins, and guides:
|
|
121
|
+
|
|
122
|
+
- **Official Website**: [https://veap.pl](https://veap.pl)
|
|
123
|
+
- **Documentation**: [https://veap.pl/docs](https://veap.pl/docs)
|
|
124
|
+
- [Getting Started](https://veap.pl/docs/getting-started/introduction)
|
|
125
|
+
- [Architecture & Fundamentals](https://veap.pl/docs/fundamentals/architecture)
|
|
126
|
+
- [Virtual Routing & Middleware](https://veap.pl/docs/routing/routing)
|
|
127
|
+
- [Database & ORM](https://veap.pl/docs/data/database)
|
|
128
|
+
- [Authentication & RBAC](https://veap.pl/docs/auth)
|
|
129
|
+
- [Authoring Plugins](https://veap.pl/docs/plugins/creating-plugins)
|
|
130
|
+
- [CLI Reference](https://veap.pl/docs/reference/cli)
|
|
131
|
+
- **GitHub Repository**: [https://github.com/veapjs/veap](https://github.com/veapjs/veap)
|
|
132
|
+
- **Community Discussions**: [https://github.com/veapjs/veap/discussions](https://github.com/veapjs/veap/discussions)
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { VeapRouter } from "@veap/framework/router";
|
|
2
|
-
import { buildRouteTree } from "@veap/framework/router/server";
|
|
3
|
-
import type { Metadata } from "next";
|
|
4
|
-
import { initializeSystem } from "@/lib/veap";
|
|
5
|
-
|
|
6
|
-
export const dynamic = "force-dynamic";
|
|
7
|
-
|
|
8
|
-
export async function generateMetadata({
|
|
9
|
-
params,
|
|
10
|
-
searchParams,
|
|
11
|
-
}: {
|
|
12
|
-
params: Promise<{ catchAll?: string[] }>;
|
|
13
|
-
searchParams: Promise<Record<string, string>>;
|
|
14
|
-
}): Promise<Metadata> {
|
|
15
|
-
const { catchAll } = await params;
|
|
16
|
-
const sParams = await searchParams;
|
|
17
|
-
const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
|
|
18
|
-
|
|
19
|
-
await initializeSystem();
|
|
20
|
-
const tree = await buildRouteTree(true);
|
|
21
|
-
const result = await tree.generateMetadata(path, sParams);
|
|
22
|
-
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default async function CatchAllPage({
|
|
27
|
-
params,
|
|
28
|
-
searchParams,
|
|
29
|
-
}: {
|
|
30
|
-
params: Promise<{ catchAll?: string[] }>;
|
|
31
|
-
searchParams: Promise<any>;
|
|
32
|
-
}) {
|
|
33
|
-
const { catchAll } = await params;
|
|
34
|
-
const sParams = await searchParams;
|
|
35
|
-
const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
|
|
36
|
-
|
|
37
|
-
await initializeSystem();
|
|
38
|
-
const tree = await buildRouteTree(true);
|
|
39
|
-
|
|
40
|
-
const result = await VeapRouter({
|
|
41
|
-
tree,
|
|
42
|
-
path,
|
|
43
|
-
searchParams: sParams,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
1
|
+
import { VeapRouter } from "@veap/framework/router";
|
|
2
|
+
import { buildRouteTree } from "@veap/framework/router/server";
|
|
3
|
+
import type { Metadata } from "next";
|
|
4
|
+
import { initializeSystem } from "@/lib/veap";
|
|
5
|
+
|
|
6
|
+
export const dynamic = "force-dynamic";
|
|
7
|
+
|
|
8
|
+
export async function generateMetadata({
|
|
9
|
+
params,
|
|
10
|
+
searchParams,
|
|
11
|
+
}: {
|
|
12
|
+
params: Promise<{ catchAll?: string[] }>;
|
|
13
|
+
searchParams: Promise<Record<string, string>>;
|
|
14
|
+
}): Promise<Metadata> {
|
|
15
|
+
const { catchAll } = await params;
|
|
16
|
+
const sParams = await searchParams;
|
|
17
|
+
const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
|
|
18
|
+
|
|
19
|
+
await initializeSystem();
|
|
20
|
+
const tree = await buildRouteTree(true);
|
|
21
|
+
const result = await tree.generateMetadata(path, sParams);
|
|
22
|
+
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default async function CatchAllPage({
|
|
27
|
+
params,
|
|
28
|
+
searchParams,
|
|
29
|
+
}: {
|
|
30
|
+
params: Promise<{ catchAll?: string[] }>;
|
|
31
|
+
searchParams: Promise<any>;
|
|
32
|
+
}) {
|
|
33
|
+
const { catchAll } = await params;
|
|
34
|
+
const sParams = await searchParams;
|
|
35
|
+
const path = catchAll && catchAll.length > 0 ? `/${catchAll.join("/")}` : "/";
|
|
36
|
+
|
|
37
|
+
await initializeSystem();
|
|
38
|
+
const tree = await buildRouteTree(true);
|
|
39
|
+
|
|
40
|
+
const result = await VeapRouter({
|
|
41
|
+
tree,
|
|
42
|
+
path,
|
|
43
|
+
searchParams: sParams,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import { ApiEnsuredAuth, runApiPipeline } from "@veap/framework/router/server";
|
|
2
|
-
import { initializeSystem } from "@/lib/veap";
|
|
3
|
-
|
|
4
|
-
export const dynamic = "force-dynamic";
|
|
5
|
-
|
|
6
|
-
async function handleRequest(
|
|
7
|
-
request: Request,
|
|
8
|
-
context: { params: Promise<{ catchAll: string[] }> },
|
|
9
|
-
) {
|
|
10
|
-
// Ensure system is initialized (DB injected, plugins registered)
|
|
11
|
-
await initializeSystem();
|
|
12
|
-
|
|
13
|
-
const { catchAll } = await context.params;
|
|
14
|
-
const path = `/api/${catchAll.join("/")}`;
|
|
15
|
-
|
|
16
|
-
const { buildRouteTree } = await import("@veap/framework/router/server");
|
|
17
|
-
const tree = await buildRouteTree(true);
|
|
18
|
-
|
|
19
|
-
const match = tree.match(path);
|
|
20
|
-
|
|
21
|
-
const node = match?.node as any;
|
|
22
|
-
|
|
23
|
-
if (match?.isExact && node?.route) {
|
|
24
|
-
const method = request.method.toUpperCase();
|
|
25
|
-
const handler = node.route[method] || node.route.handler;
|
|
26
|
-
|
|
27
|
-
if (handler) {
|
|
28
|
-
const middlewares = [];
|
|
29
|
-
|
|
30
|
-
// Collect middlewares from the layout chain
|
|
31
|
-
for (const entry of match.layoutChain) {
|
|
32
|
-
if (entry.middlewares) {
|
|
33
|
-
middlewares.push(...entry.middlewares);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Node middlewares
|
|
38
|
-
if (node.middlewares) {
|
|
39
|
-
middlewares.push(...node.middlewares);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Resolve auth from chain (closest to leaf wins)
|
|
43
|
-
let authRequired = node.auth;
|
|
44
|
-
if (authRequired === undefined) {
|
|
45
|
-
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
46
|
-
if (match.layoutChain[i].auth !== undefined) {
|
|
47
|
-
authRequired = match.layoutChain[i].auth;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Roles and permissions
|
|
54
|
-
let roles = node.roles;
|
|
55
|
-
if (!roles) {
|
|
56
|
-
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
57
|
-
if (match.layoutChain[i].roles) {
|
|
58
|
-
roles = match.layoutChain[i].roles;
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let permissions = node.permissions;
|
|
65
|
-
if (!permissions) {
|
|
66
|
-
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
67
|
-
if (match.layoutChain[i].permissions) {
|
|
68
|
-
permissions = match.layoutChain[i].permissions;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (authRequired) {
|
|
75
|
-
middlewares.unshift(ApiEnsuredAuth);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return await runApiPipeline(
|
|
79
|
-
middlewares as any,
|
|
80
|
-
request,
|
|
81
|
-
{
|
|
82
|
-
params: match.params,
|
|
83
|
-
roles,
|
|
84
|
-
permissions,
|
|
85
|
-
},
|
|
86
|
-
async (req: Request, ctx: any) => {
|
|
87
|
-
return handler(req, ctx);
|
|
88
|
-
},
|
|
89
|
-
);
|
|
90
|
-
} else {
|
|
91
|
-
return new Response(
|
|
92
|
-
JSON.stringify({ error: `Method ${method} Not Allowed` }),
|
|
93
|
-
{
|
|
94
|
-
status: 405,
|
|
95
|
-
headers: { "Content-Type": "application/json" },
|
|
96
|
-
},
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return new Response(JSON.stringify({ error: "API Route not found" }), {
|
|
102
|
-
status: 404,
|
|
103
|
-
headers: { "Content-Type": "application/json" },
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export const GET = handleRequest;
|
|
108
|
-
export const POST = handleRequest;
|
|
109
|
-
export const PUT = handleRequest;
|
|
110
|
-
export const PATCH = handleRequest;
|
|
111
|
-
export const DELETE = handleRequest;
|
|
1
|
+
import { ApiEnsuredAuth, runApiPipeline } from "@veap/framework/router/server";
|
|
2
|
+
import { initializeSystem } from "@/lib/veap";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
|
|
6
|
+
async function handleRequest(
|
|
7
|
+
request: Request,
|
|
8
|
+
context: { params: Promise<{ catchAll: string[] }> },
|
|
9
|
+
) {
|
|
10
|
+
// Ensure system is initialized (DB injected, plugins registered)
|
|
11
|
+
await initializeSystem();
|
|
12
|
+
|
|
13
|
+
const { catchAll } = await context.params;
|
|
14
|
+
const path = `/api/${catchAll.join("/")}`;
|
|
15
|
+
|
|
16
|
+
const { buildRouteTree } = await import("@veap/framework/router/server");
|
|
17
|
+
const tree = await buildRouteTree(true);
|
|
18
|
+
|
|
19
|
+
const match = tree.match(path);
|
|
20
|
+
|
|
21
|
+
const node = match?.node as any;
|
|
22
|
+
|
|
23
|
+
if (match?.isExact && node?.route) {
|
|
24
|
+
const method = request.method.toUpperCase();
|
|
25
|
+
const handler = node.route[method] || node.route.handler;
|
|
26
|
+
|
|
27
|
+
if (handler) {
|
|
28
|
+
const middlewares = [];
|
|
29
|
+
|
|
30
|
+
// Collect middlewares from the layout chain
|
|
31
|
+
for (const entry of match.layoutChain) {
|
|
32
|
+
if (entry.middlewares) {
|
|
33
|
+
middlewares.push(...entry.middlewares);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Node middlewares
|
|
38
|
+
if (node.middlewares) {
|
|
39
|
+
middlewares.push(...node.middlewares);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Resolve auth from chain (closest to leaf wins)
|
|
43
|
+
let authRequired = node.auth;
|
|
44
|
+
if (authRequired === undefined) {
|
|
45
|
+
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
46
|
+
if (match.layoutChain[i].auth !== undefined) {
|
|
47
|
+
authRequired = match.layoutChain[i].auth;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Roles and permissions
|
|
54
|
+
let roles = node.roles;
|
|
55
|
+
if (!roles) {
|
|
56
|
+
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
57
|
+
if (match.layoutChain[i].roles) {
|
|
58
|
+
roles = match.layoutChain[i].roles;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let permissions = node.permissions;
|
|
65
|
+
if (!permissions) {
|
|
66
|
+
for (let i = match.layoutChain.length - 1; i >= 0; i--) {
|
|
67
|
+
if (match.layoutChain[i].permissions) {
|
|
68
|
+
permissions = match.layoutChain[i].permissions;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (authRequired) {
|
|
75
|
+
middlewares.unshift(ApiEnsuredAuth);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return await runApiPipeline(
|
|
79
|
+
middlewares as any,
|
|
80
|
+
request,
|
|
81
|
+
{
|
|
82
|
+
params: match.params,
|
|
83
|
+
roles,
|
|
84
|
+
permissions,
|
|
85
|
+
},
|
|
86
|
+
async (req: Request, ctx: any) => {
|
|
87
|
+
return handler(req, ctx);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
} else {
|
|
91
|
+
return new Response(
|
|
92
|
+
JSON.stringify({ error: `Method ${method} Not Allowed` }),
|
|
93
|
+
{
|
|
94
|
+
status: 405,
|
|
95
|
+
headers: { "Content-Type": "application/json" },
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return new Response(JSON.stringify({ error: "API Route not found" }), {
|
|
102
|
+
status: 404,
|
|
103
|
+
headers: { "Content-Type": "application/json" },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const GET = handleRequest;
|
|
108
|
+
export const POST = handleRequest;
|
|
109
|
+
export const PUT = handleRequest;
|
|
110
|
+
export const PATCH = handleRequest;
|
|
111
|
+
export const DELETE = handleRequest;
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useTranslation } from "@veap/framework/intl";
|
|
4
|
-
import { useEffect } from "react";
|
|
5
|
-
|
|
6
|
-
export default function ErrorPage({
|
|
7
|
-
error,
|
|
8
|
-
reset,
|
|
9
|
-
}: {
|
|
10
|
-
error: Error & { digest?: string };
|
|
11
|
-
reset: () => void;
|
|
12
|
-
}) {
|
|
13
|
-
const { t } = useTranslation();
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
console.error(error);
|
|
16
|
-
}, [error]);
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
20
|
-
<div className="max-w-md text-center">
|
|
21
|
-
<h2 className="text-2xl font-bold mb-4">{t("Something went wrong!")}</h2>
|
|
22
|
-
<p className="text-muted-foreground mb-6">
|
|
23
|
-
{error.message || t("An unexpected error occurred.")}
|
|
24
|
-
</p>
|
|
25
|
-
<button
|
|
26
|
-
onClick={() => reset()}
|
|
27
|
-
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:opacity-90"
|
|
28
|
-
>
|
|
29
|
-
{t("Try Again")}
|
|
30
|
-
</button>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTranslation } from "@veap/framework/intl";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
export default function ErrorPage({
|
|
7
|
+
error,
|
|
8
|
+
reset,
|
|
9
|
+
}: {
|
|
10
|
+
error: Error & { digest?: string };
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}) {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
console.error(error);
|
|
16
|
+
}, [error]);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
20
|
+
<div className="max-w-md text-center">
|
|
21
|
+
<h2 className="text-2xl font-bold mb-4">{t("Something went wrong!")}</h2>
|
|
22
|
+
<p className="text-muted-foreground mb-6">
|
|
23
|
+
{error.message || t("An unexpected error occurred.")}
|
|
24
|
+
</p>
|
|
25
|
+
<button
|
|
26
|
+
onClick={() => reset()}
|
|
27
|
+
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:opacity-90"
|
|
28
|
+
>
|
|
29
|
+
{t("Try Again")}
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|