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
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
@import "tw-animate-css";
|
|
3
|
-
|
|
4
|
-
@custom-variant dark (&:is(.dark *));
|
|
5
|
-
|
|
6
|
-
@theme inline {
|
|
7
|
-
--color-background: var(--background);
|
|
8
|
-
--color-foreground: var(--foreground);
|
|
9
|
-
--color-card: var(--card);
|
|
10
|
-
--color-card-foreground: var(--card-foreground);
|
|
11
|
-
--color-popover: var(--popover);
|
|
12
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
13
|
-
--color-primary: var(--primary);
|
|
14
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
15
|
-
--color-secondary: var(--secondary);
|
|
16
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
17
|
-
--color-muted: var(--muted);
|
|
18
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
19
|
-
--color-accent: var(--accent);
|
|
20
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
21
|
-
--color-destructive: var(--destructive);
|
|
22
|
-
--color-destructive-foreground: var(--destructive-foreground);
|
|
23
|
-
--color-border: var(--border);
|
|
24
|
-
--color-input: var(--input);
|
|
25
|
-
--color-ring: var(--ring);
|
|
26
|
-
--color-chart-1: var(--chart-1);
|
|
27
|
-
--color-chart-2: var(--chart-2);
|
|
28
|
-
--color-chart-3: var(--chart-3);
|
|
29
|
-
--color-chart-4: var(--chart-4);
|
|
30
|
-
--color-chart-5: var(--chart-5);
|
|
31
|
-
--radius-sm: calc(var(--radius) * 0.6);
|
|
32
|
-
--radius-md: calc(var(--radius) * 0.8);
|
|
33
|
-
--radius-lg: var(--radius);
|
|
34
|
-
--radius-xl: calc(var(--radius) * 1.4);
|
|
35
|
-
--radius-2xl: calc(var(--radius) * 1.8);
|
|
36
|
-
--radius-3xl: calc(var(--radius) * 2.2);
|
|
37
|
-
--radius-4xl: calc(var(--radius) * 2.6);
|
|
38
|
-
--color-sidebar: var(--sidebar);
|
|
39
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
40
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
41
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
42
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
43
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
44
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
45
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
:root {
|
|
49
|
-
--radius: 0.625rem;
|
|
50
|
-
--background: oklch(1 0 0);
|
|
51
|
-
--foreground: oklch(0.145 0 0);
|
|
52
|
-
--card: oklch(1 0 0);
|
|
53
|
-
--card-foreground: oklch(0.145 0 0);
|
|
54
|
-
--popover: oklch(1 0 0);
|
|
55
|
-
--popover-foreground: oklch(0.145 0 0);
|
|
56
|
-
--primary: oklch(0.205 0 0);
|
|
57
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
58
|
-
--secondary: oklch(0.97 0 0);
|
|
59
|
-
--secondary-foreground: oklch(0.205 0 0);
|
|
60
|
-
--muted: oklch(0.97 0 0);
|
|
61
|
-
--muted-foreground: oklch(0.556 0 0);
|
|
62
|
-
--accent: oklch(0.97 0 0);
|
|
63
|
-
--accent-foreground: oklch(0.205 0 0);
|
|
64
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
65
|
-
--border: oklch(0.922 0 0);
|
|
66
|
-
--input: oklch(0.922 0 0);
|
|
67
|
-
--ring: oklch(0.708 0 0);
|
|
68
|
-
--chart-1: oklch(0.646 0.222 41.116);
|
|
69
|
-
--chart-2: oklch(0.6 0.118 184.704);
|
|
70
|
-
--chart-3: oklch(0.398 0.07 227.392);
|
|
71
|
-
--chart-4: oklch(0.828 0.189 84.429);
|
|
72
|
-
--chart-5: oklch(0.769 0.188 70.08);
|
|
73
|
-
--sidebar: oklch(0.985 0 0);
|
|
74
|
-
--sidebar-foreground: oklch(0.145 0 0);
|
|
75
|
-
--sidebar-primary: oklch(0.205 0 0);
|
|
76
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
77
|
-
--sidebar-accent: oklch(0.97 0 0);
|
|
78
|
-
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
79
|
-
--sidebar-border: oklch(0.922 0 0);
|
|
80
|
-
--sidebar-ring: oklch(0.708 0 0);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.dark {
|
|
84
|
-
--background: oklch(0.145 0 0);
|
|
85
|
-
--foreground: oklch(0.985 0 0);
|
|
86
|
-
--card: oklch(0.205 0 0);
|
|
87
|
-
--card-foreground: oklch(0.985 0 0);
|
|
88
|
-
--popover: oklch(0.205 0 0);
|
|
89
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
90
|
-
--primary: oklch(0.922 0 0);
|
|
91
|
-
--primary-foreground: oklch(0.205 0 0);
|
|
92
|
-
--secondary: oklch(0.269 0 0);
|
|
93
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
94
|
-
--muted: oklch(0.269 0 0);
|
|
95
|
-
--muted-foreground: oklch(0.708 0 0);
|
|
96
|
-
--accent: oklch(0.269 0 0);
|
|
97
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
98
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
99
|
-
--border: oklch(1 0 0 / 10%);
|
|
100
|
-
--input: oklch(1 0 0 / 15%);
|
|
101
|
-
--ring: oklch(0.556 0 0);
|
|
102
|
-
--chart-1: oklch(0.488 0.243 264.376);
|
|
103
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
104
|
-
--chart-3: oklch(0.769 0.188 70.08);
|
|
105
|
-
--chart-4: oklch(0.627 0.265 303.9);
|
|
106
|
-
--chart-5: oklch(0.645 0.246 16.439);
|
|
107
|
-
--sidebar: oklch(0.205 0 0);
|
|
108
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
109
|
-
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
110
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
111
|
-
--sidebar-accent: oklch(0.269 0 0);
|
|
112
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
113
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
114
|
-
--sidebar-ring: oklch(0.556 0 0);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
@layer base {
|
|
118
|
-
* {
|
|
119
|
-
@apply border-border outline-ring/50;
|
|
120
|
-
}
|
|
121
|
-
body {
|
|
122
|
-
@apply bg-background text-foreground;
|
|
123
|
-
}
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--color-background: var(--background);
|
|
8
|
+
--color-foreground: var(--foreground);
|
|
9
|
+
--color-card: var(--card);
|
|
10
|
+
--color-card-foreground: var(--card-foreground);
|
|
11
|
+
--color-popover: var(--popover);
|
|
12
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
13
|
+
--color-primary: var(--primary);
|
|
14
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
15
|
+
--color-secondary: var(--secondary);
|
|
16
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
17
|
+
--color-muted: var(--muted);
|
|
18
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
19
|
+
--color-accent: var(--accent);
|
|
20
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
21
|
+
--color-destructive: var(--destructive);
|
|
22
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
23
|
+
--color-border: var(--border);
|
|
24
|
+
--color-input: var(--input);
|
|
25
|
+
--color-ring: var(--ring);
|
|
26
|
+
--color-chart-1: var(--chart-1);
|
|
27
|
+
--color-chart-2: var(--chart-2);
|
|
28
|
+
--color-chart-3: var(--chart-3);
|
|
29
|
+
--color-chart-4: var(--chart-4);
|
|
30
|
+
--color-chart-5: var(--chart-5);
|
|
31
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
32
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
33
|
+
--radius-lg: var(--radius);
|
|
34
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
35
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
36
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
37
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
38
|
+
--color-sidebar: var(--sidebar);
|
|
39
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
40
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
41
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
42
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
43
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
44
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
45
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
:root {
|
|
49
|
+
--radius: 0.625rem;
|
|
50
|
+
--background: oklch(1 0 0);
|
|
51
|
+
--foreground: oklch(0.145 0 0);
|
|
52
|
+
--card: oklch(1 0 0);
|
|
53
|
+
--card-foreground: oklch(0.145 0 0);
|
|
54
|
+
--popover: oklch(1 0 0);
|
|
55
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
56
|
+
--primary: oklch(0.205 0 0);
|
|
57
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
58
|
+
--secondary: oklch(0.97 0 0);
|
|
59
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
60
|
+
--muted: oklch(0.97 0 0);
|
|
61
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
62
|
+
--accent: oklch(0.97 0 0);
|
|
63
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
64
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
65
|
+
--border: oklch(0.922 0 0);
|
|
66
|
+
--input: oklch(0.922 0 0);
|
|
67
|
+
--ring: oklch(0.708 0 0);
|
|
68
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
69
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
70
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
71
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
72
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
73
|
+
--sidebar: oklch(0.985 0 0);
|
|
74
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
75
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
76
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
77
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
78
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
79
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
80
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.dark {
|
|
84
|
+
--background: oklch(0.145 0 0);
|
|
85
|
+
--foreground: oklch(0.985 0 0);
|
|
86
|
+
--card: oklch(0.205 0 0);
|
|
87
|
+
--card-foreground: oklch(0.985 0 0);
|
|
88
|
+
--popover: oklch(0.205 0 0);
|
|
89
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
90
|
+
--primary: oklch(0.922 0 0);
|
|
91
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
92
|
+
--secondary: oklch(0.269 0 0);
|
|
93
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
94
|
+
--muted: oklch(0.269 0 0);
|
|
95
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
96
|
+
--accent: oklch(0.269 0 0);
|
|
97
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
98
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
99
|
+
--border: oklch(1 0 0 / 10%);
|
|
100
|
+
--input: oklch(1 0 0 / 15%);
|
|
101
|
+
--ring: oklch(0.556 0 0);
|
|
102
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
103
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
104
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
105
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
106
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
107
|
+
--sidebar: oklch(0.205 0 0);
|
|
108
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
110
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
111
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
112
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
113
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
114
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@layer base {
|
|
118
|
+
* {
|
|
119
|
+
@apply border-border outline-ring/50;
|
|
120
|
+
}
|
|
121
|
+
body {
|
|
122
|
+
@apply bg-background text-foreground;
|
|
123
|
+
}
|
|
124
124
|
}
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import type { Metadata } from "next";
|
|
2
|
-
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
-
import "./globals.css";
|
|
4
|
-
import { I18nProvider } from "@veap/framework/intl/server";
|
|
5
|
-
import { ExtensionPoint, getPathPrefix } from "@veap/framework/plugins/server";
|
|
6
|
-
import { AppProvider } from "@veap/framework/react";
|
|
7
|
-
import { cookies } from "next/headers";
|
|
8
|
-
import { initializeSystem } from "@/lib/veap";
|
|
9
|
-
import {
|
|
10
|
-
getCurrentSession,
|
|
11
|
-
isSystemInstalled,
|
|
12
|
-
} from "@veap/framework/auth/server";
|
|
13
|
-
|
|
14
|
-
const geistSans = Geist({
|
|
15
|
-
variable: "--font-geist-sans",
|
|
16
|
-
subsets: ["latin"],
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const geistMono = Geist_Mono({
|
|
20
|
-
variable: "--font-geist-mono",
|
|
21
|
-
subsets: ["latin"],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export const metadata: Metadata = {
|
|
25
|
-
title: "Veap App",
|
|
26
|
-
description: "Built with Veap framework",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// The whole layout is request-time work: it boots Veap (providers + plugin
|
|
30
|
-
// context), reads cookies and renders plugin extension points. Without this,
|
|
31
|
-
// Next.js prerenders /_not-found during `next build` while
|
|
32
|
-
// NEXT_PHASE=phase-production-build - in which `Application.bootstrap()`
|
|
33
|
-
// intentionally skips provider boot - and context-bound helpers (e.g.
|
|
34
|
-
// getPathPrefix()) throw "Context is not bound".
|
|
35
|
-
export const dynamic = "force-dynamic";
|
|
36
|
-
|
|
37
|
-
export default async function RootLayout({
|
|
38
|
-
children,
|
|
39
|
-
}: Readonly<{
|
|
40
|
-
children: React.ReactNode;
|
|
41
|
-
}>) {
|
|
42
|
-
await initializeSystem();
|
|
43
|
-
const installed = await isSystemInstalled();
|
|
44
|
-
|
|
45
|
-
const session = installed
|
|
46
|
-
? await getCurrentSession()
|
|
47
|
-
: { session: null, user: null };
|
|
48
|
-
|
|
49
|
-
const pathPrefix = await getPathPrefix();
|
|
50
|
-
|
|
51
|
-
const cookieStore = await cookies();
|
|
52
|
-
const locale = cookieStore.get("VEAP_LOCALE")?.value || "en";
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<html lang={locale} suppressHydrationWarning>
|
|
56
|
-
<body
|
|
57
|
-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
58
|
-
>
|
|
59
|
-
<I18nProvider>
|
|
60
|
-
<AppProvider initialSession={session} prefix={pathPrefix}>
|
|
61
|
-
<div className="min-h-screen relative bg-background">
|
|
62
|
-
<ExtensionPoint target="app" point="before-content" />
|
|
63
|
-
{children}
|
|
64
|
-
<ExtensionPoint target="app" point="after-content" />
|
|
65
|
-
</div>
|
|
66
|
-
</AppProvider>
|
|
67
|
-
</I18nProvider>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
import { I18nProvider } from "@veap/framework/intl/server";
|
|
5
|
+
import { ExtensionPoint, getPathPrefix } from "@veap/framework/plugins/server";
|
|
6
|
+
import { AppProvider } from "@veap/framework/react";
|
|
7
|
+
import { cookies } from "next/headers";
|
|
8
|
+
import { initializeSystem } from "@/lib/veap";
|
|
9
|
+
import {
|
|
10
|
+
getCurrentSession,
|
|
11
|
+
isSystemInstalled,
|
|
12
|
+
} from "@veap/framework/auth/server";
|
|
13
|
+
|
|
14
|
+
const geistSans = Geist({
|
|
15
|
+
variable: "--font-geist-sans",
|
|
16
|
+
subsets: ["latin"],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const geistMono = Geist_Mono({
|
|
20
|
+
variable: "--font-geist-mono",
|
|
21
|
+
subsets: ["latin"],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const metadata: Metadata = {
|
|
25
|
+
title: "Veap App",
|
|
26
|
+
description: "Built with Veap framework",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// The whole layout is request-time work: it boots Veap (providers + plugin
|
|
30
|
+
// context), reads cookies and renders plugin extension points. Without this,
|
|
31
|
+
// Next.js prerenders /_not-found during `next build` while
|
|
32
|
+
// NEXT_PHASE=phase-production-build - in which `Application.bootstrap()`
|
|
33
|
+
// intentionally skips provider boot - and context-bound helpers (e.g.
|
|
34
|
+
// getPathPrefix()) throw "Context is not bound".
|
|
35
|
+
export const dynamic = "force-dynamic";
|
|
36
|
+
|
|
37
|
+
export default async function RootLayout({
|
|
38
|
+
children,
|
|
39
|
+
}: Readonly<{
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
}>) {
|
|
42
|
+
await initializeSystem();
|
|
43
|
+
const installed = await isSystemInstalled();
|
|
44
|
+
|
|
45
|
+
const session = installed
|
|
46
|
+
? await getCurrentSession()
|
|
47
|
+
: { session: null, user: null };
|
|
48
|
+
|
|
49
|
+
const pathPrefix = await getPathPrefix();
|
|
50
|
+
|
|
51
|
+
const cookieStore = await cookies();
|
|
52
|
+
const locale = cookieStore.get("VEAP_LOCALE")?.value || "en";
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<html lang={locale} suppressHydrationWarning>
|
|
56
|
+
<body
|
|
57
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
58
|
+
>
|
|
59
|
+
<I18nProvider>
|
|
60
|
+
<AppProvider initialSession={session} prefix={pathPrefix}>
|
|
61
|
+
<div className="min-h-screen relative bg-background">
|
|
62
|
+
<ExtensionPoint target="app" point="before-content" />
|
|
63
|
+
{children}
|
|
64
|
+
<ExtensionPoint target="app" point="after-content" />
|
|
65
|
+
</div>
|
|
66
|
+
</AppProvider>
|
|
67
|
+
</I18nProvider>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { type NextRequest, NextResponse } from "next/server";
|
|
4
|
-
|
|
5
|
-
export const dynamic = "force-dynamic";
|
|
6
|
-
|
|
7
|
-
export async function GET(
|
|
8
|
-
request: NextRequest,
|
|
9
|
-
context: { params: Promise<{ path: string[] }> },
|
|
10
|
-
) {
|
|
11
|
-
const resolvedParams = await context.params;
|
|
12
|
-
const filePath = path.join(
|
|
13
|
-
/*turbopackIgnore: true*/ process.cwd(),
|
|
14
|
-
"public",
|
|
15
|
-
"storage",
|
|
16
|
-
...resolvedParams.path,
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
// Prevent Directory Traversal Attacks!
|
|
20
|
-
const safePath = path.normalize(filePath);
|
|
21
|
-
const baseStoragePath = path.join(
|
|
22
|
-
/*turbopackIgnore: true*/ process.cwd(),
|
|
23
|
-
"public",
|
|
24
|
-
"storage",
|
|
25
|
-
);
|
|
26
|
-
if (!safePath.startsWith(baseStoragePath)) {
|
|
27
|
-
return new NextResponse("Forbidden", { status: 403 });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
!fs.existsSync(/*turbopackIgnore: true*/ safePath) ||
|
|
32
|
-
fs.statSync(/*turbopackIgnore: true*/ safePath).isDirectory()
|
|
33
|
-
) {
|
|
34
|
-
return new NextResponse("Not Found", { status: 404 });
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const fileBuffer = fs.readFileSync(/*turbopackIgnore: true*/ safePath);
|
|
38
|
-
|
|
39
|
-
// Determine Content-Type
|
|
40
|
-
const ext = path.extname(safePath).toLowerCase();
|
|
41
|
-
let contentType = "application/octet-stream";
|
|
42
|
-
if (ext === ".png") contentType = "image/png";
|
|
43
|
-
else if (ext === ".jpg" || ext === ".jpeg") contentType = "image/jpeg";
|
|
44
|
-
else if (ext === ".gif") contentType = "image/gif";
|
|
45
|
-
else if (ext === ".svg") contentType = "image/svg+xml";
|
|
46
|
-
else if (ext === ".webp") contentType = "image/webp";
|
|
47
|
-
else if (ext === ".md" || ext === ".mdx")
|
|
48
|
-
contentType = "text/markdown; charset=utf-8";
|
|
49
|
-
else if (ext === ".json") contentType = "application/json";
|
|
50
|
-
else if (ext === ".txt") contentType = "text/plain; charset=utf-8";
|
|
51
|
-
else if (ext === ".pdf") contentType = "application/pdf";
|
|
52
|
-
else if (ext === ".css") contentType = "text/css";
|
|
53
|
-
else if (ext === ".js") contentType = "application/javascript";
|
|
54
|
-
|
|
55
|
-
return new NextResponse(fileBuffer, {
|
|
56
|
-
headers: {
|
|
57
|
-
"Content-Type": contentType,
|
|
58
|
-
"Cache-Control": "public, max-age=31536000, immutable",
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
}
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { type NextRequest, NextResponse } from "next/server";
|
|
4
|
+
|
|
5
|
+
export const dynamic = "force-dynamic";
|
|
6
|
+
|
|
7
|
+
export async function GET(
|
|
8
|
+
request: NextRequest,
|
|
9
|
+
context: { params: Promise<{ path: string[] }> },
|
|
10
|
+
) {
|
|
11
|
+
const resolvedParams = await context.params;
|
|
12
|
+
const filePath = path.join(
|
|
13
|
+
/*turbopackIgnore: true*/ process.cwd(),
|
|
14
|
+
"public",
|
|
15
|
+
"storage",
|
|
16
|
+
...resolvedParams.path,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// Prevent Directory Traversal Attacks!
|
|
20
|
+
const safePath = path.normalize(filePath);
|
|
21
|
+
const baseStoragePath = path.join(
|
|
22
|
+
/*turbopackIgnore: true*/ process.cwd(),
|
|
23
|
+
"public",
|
|
24
|
+
"storage",
|
|
25
|
+
);
|
|
26
|
+
if (!safePath.startsWith(baseStoragePath)) {
|
|
27
|
+
return new NextResponse("Forbidden", { status: 403 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
!fs.existsSync(/*turbopackIgnore: true*/ safePath) ||
|
|
32
|
+
fs.statSync(/*turbopackIgnore: true*/ safePath).isDirectory()
|
|
33
|
+
) {
|
|
34
|
+
return new NextResponse("Not Found", { status: 404 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fileBuffer = fs.readFileSync(/*turbopackIgnore: true*/ safePath);
|
|
38
|
+
|
|
39
|
+
// Determine Content-Type
|
|
40
|
+
const ext = path.extname(safePath).toLowerCase();
|
|
41
|
+
let contentType = "application/octet-stream";
|
|
42
|
+
if (ext === ".png") contentType = "image/png";
|
|
43
|
+
else if (ext === ".jpg" || ext === ".jpeg") contentType = "image/jpeg";
|
|
44
|
+
else if (ext === ".gif") contentType = "image/gif";
|
|
45
|
+
else if (ext === ".svg") contentType = "image/svg+xml";
|
|
46
|
+
else if (ext === ".webp") contentType = "image/webp";
|
|
47
|
+
else if (ext === ".md" || ext === ".mdx")
|
|
48
|
+
contentType = "text/markdown; charset=utf-8";
|
|
49
|
+
else if (ext === ".json") contentType = "application/json";
|
|
50
|
+
else if (ext === ".txt") contentType = "text/plain; charset=utf-8";
|
|
51
|
+
else if (ext === ".pdf") contentType = "application/pdf";
|
|
52
|
+
else if (ext === ".css") contentType = "text/css";
|
|
53
|
+
else if (ext === ".js") contentType = "application/javascript";
|
|
54
|
+
|
|
55
|
+
return new NextResponse(fileBuffer, {
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": contentType,
|
|
58
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
|
|
3
|
+
export function Footer() {
|
|
4
|
+
return (
|
|
5
|
+
<footer className="w-full border-t bg-muted/40 py-10 mt-16 text-sm text-muted-foreground">
|
|
6
|
+
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-4 px-4 sm:flex-row sm:px-6">
|
|
7
|
+
<div className="flex flex-col items-center gap-1 sm:items-start">
|
|
8
|
+
<p className="font-semibold text-foreground">Veap Application</p>
|
|
9
|
+
<p className="text-xs">
|
|
10
|
+
Powered by Next.js 16 App Router & @veap/framework modular architecture.
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div className="flex items-center gap-6 text-xs">
|
|
15
|
+
<Link href="/" className="hover:underline">
|
|
16
|
+
Home
|
|
17
|
+
</Link>
|
|
18
|
+
<Link href="/app" className="hover:underline">
|
|
19
|
+
Panel
|
|
20
|
+
</Link>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</footer>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { getCurrentSession } from "@veap/framework/auth/server";
|
|
3
|
+
|
|
4
|
+
export async function Navbar() {
|
|
5
|
+
const { session, user } = await getCurrentSession();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<header className="sticky top-0 z-40 w-full border-b bg-background/80 backdrop-blur-md">
|
|
9
|
+
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-6">
|
|
10
|
+
<div className="flex items-center gap-6">
|
|
11
|
+
<Link href="/" className="flex items-center gap-2 font-bold text-xl tracking-tight">
|
|
12
|
+
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground font-mono text-sm font-bold shadow-xs">
|
|
13
|
+
V
|
|
14
|
+
</span>
|
|
15
|
+
<span>Veap</span>
|
|
16
|
+
</Link>
|
|
17
|
+
|
|
18
|
+
<nav className="hidden md:flex items-center gap-6 text-sm font-medium text-muted-foreground">
|
|
19
|
+
<Link href="/" className="transition hover:text-foreground">
|
|
20
|
+
Home
|
|
21
|
+
</Link>
|
|
22
|
+
<Link href="/app" className="transition hover:text-foreground">
|
|
23
|
+
Panel
|
|
24
|
+
</Link>
|
|
25
|
+
</nav>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div className="flex items-center gap-3">
|
|
29
|
+
{session && user ? (
|
|
30
|
+
<div className="flex items-center gap-3">
|
|
31
|
+
<span className="hidden sm:inline-block text-xs text-muted-foreground font-medium">
|
|
32
|
+
{user.email}
|
|
33
|
+
</span>
|
|
34
|
+
<Link
|
|
35
|
+
href="/app"
|
|
36
|
+
className="inline-flex h-9 items-center justify-center rounded-md bg-secondary px-3 text-xs font-medium text-secondary-foreground shadow-xs transition hover:bg-secondary/80"
|
|
37
|
+
>
|
|
38
|
+
Panel
|
|
39
|
+
</Link>
|
|
40
|
+
</div>
|
|
41
|
+
) : (
|
|
42
|
+
<div className="flex items-center gap-2">
|
|
43
|
+
<Link
|
|
44
|
+
href="/signin"
|
|
45
|
+
className="inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-xs transition hover:bg-accent hover:text-accent-foreground"
|
|
46
|
+
>
|
|
47
|
+
Sign In
|
|
48
|
+
</Link>
|
|
49
|
+
<Link
|
|
50
|
+
href="/signup"
|
|
51
|
+
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground shadow-xs transition hover:bg-primary/90"
|
|
52
|
+
>
|
|
53
|
+
Get Started
|
|
54
|
+
</Link>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</header>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ExtensionPoint } from "@veap/framework/plugins/server";
|
|
2
|
+
import { Navbar } from "@/components/navbar";
|
|
3
|
+
import { Footer } from "@/components/footer";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
export function SiteLayout({ children }: { children: ReactNode }) {
|
|
7
|
+
return (
|
|
8
|
+
<div className="bg-background relative min-h-screen flex flex-col justify-between">
|
|
9
|
+
{/* App Shell Slot for Navbar: single mode resolves plugin with highest priority, fallback to host Navbar */}
|
|
10
|
+
<ExtensionPoint target="app" point="navbar" mode="single">
|
|
11
|
+
<Navbar />
|
|
12
|
+
</ExtensionPoint>
|
|
13
|
+
|
|
14
|
+
<main className="flex-1 w-full">{children}</main>
|
|
15
|
+
|
|
16
|
+
{/* App Shell Slot for Footer */}
|
|
17
|
+
<ExtensionPoint target="app" point="footer" mode="single">
|
|
18
|
+
<Footer />
|
|
19
|
+
</ExtensionPoint>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FILE AUTOMATICALLY GENERATED BY veap/cli
|
|
3
|
-
* DO NOT EDIT THIS FILE MANUALLY!
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const plugins = [];
|
|
1
|
+
/**
|
|
2
|
+
* FILE AUTOMATICALLY GENERATED BY veap/cli
|
|
3
|
+
* DO NOT EDIT THIS FILE MANUALLY!
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const plugins = [];
|