@thinhnguyencth1204/nextcli 0.8.0 → 1.0.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/README.md +27 -24
- package/dist/cli.js +168 -107
- package/package.json +1 -1
- package/templates/features/api/src/lib/api/axios.ts +1 -90
- package/templates/features/auth/messages/vi/auth.json +2 -1
- package/templates/features/auth/src/app/(auth)/change-password/page.tsx +5 -4
- package/templates/features/auth/src/app/(auth)/layout.tsx +2 -5
- package/templates/features/auth/src/app/(auth)/sign-in/page.tsx +5 -4
- package/templates/features/auth/src/app/api/v1/auth/login/route.ts +24 -29
- package/templates/features/auth/src/app/api/v1/auth/logout/route.ts +0 -5
- package/templates/features/auth/src/components/layout/auth/auth-shell.tsx +24 -0
- package/templates/features/auth/src/features/auth/components/account-panel.tsx +15 -3
- package/templates/features/auth/src/features/auth/components/change-password-form.tsx +27 -30
- package/templates/features/auth/src/features/auth/components/sign-in-form.tsx +33 -42
- package/templates/features/auth/src/lib/auth/client.ts +2 -2
- package/templates/features/auth/src/lib/auth/server.ts +2 -2
- package/templates/features/dashboard/src/app/(dashboard)/account/page.tsx +9 -7
- package/templates/features/dashboard/src/app/(dashboard)/dashboard/page.tsx +24 -10
- package/templates/features/dashboard/src/components/layout/private/app-sidebar.tsx +1 -13
- package/templates/features/dashboard/src/components/layout/private/dashboard-layout.tsx +31 -22
- package/templates/features/dashboard/src/components/layout/private/page-shell.tsx +40 -0
- package/templates/features/database/prisma/schema.prisma +1 -0
- package/templates/features/example/messages/vi/example.json +11 -1
- package/templates/features/example/src/app/(dashboard)/example/page.tsx +92 -3
- package/templates/features/example/src/example/components/example-table.tsx +15 -2
- package/templates/next-base/.env +16 -0
- package/templates/next-base/.env.development +16 -0
- package/templates/next-base/.env.example +16 -0
- package/templates/next-base/SETUP.md +62 -10
- package/templates/next-base/bun.lock +407 -0
- package/templates/next-base/messages/vi/auth.json +43 -0
- package/templates/next-base/messages/vi/common.json +53 -0
- package/templates/next-base/messages/vi/example.json +20 -0
- package/templates/next-base/next-env.d.ts +1 -1
- package/templates/next-base/next.config.ts +4 -1
- package/templates/next-base/nextcli.json +12 -4
- package/templates/next-base/package.json +24 -5
- package/templates/next-base/prisma/schema.prisma +85 -0
- package/templates/next-base/prisma.config.ts +16 -0
- package/templates/next-base/src/app/(auth)/.gitkeep +1 -0
- package/templates/next-base/src/app/(auth)/change-password/layout.tsx +21 -0
- package/templates/next-base/src/app/(auth)/change-password/page.tsx +15 -0
- package/templates/next-base/src/app/(auth)/layout.tsx +6 -0
- package/templates/next-base/src/app/(auth)/sign-in/layout.tsx +17 -0
- package/templates/next-base/src/app/(auth)/sign-in/page.tsx +15 -0
- package/templates/next-base/src/app/(dashboard)/account/page.tsx +20 -0
- package/templates/next-base/src/app/(dashboard)/dashboard/page.tsx +31 -0
- package/templates/next-base/src/app/(dashboard)/example/page.tsx +102 -0
- package/templates/next-base/src/app/(dashboard)/layout.tsx +22 -0
- package/templates/next-base/src/app/api/auth/[...all]/route.ts +4 -0
- package/templates/next-base/src/app/api/v1/auth/change-password/route.ts +55 -0
- package/templates/next-base/src/app/api/v1/auth/login/route.ts +65 -0
- package/templates/next-base/src/app/api/v1/auth/logout/route.ts +23 -0
- package/templates/next-base/src/app/api/v1/auth/me/route.ts +24 -0
- package/templates/next-base/src/app/api/v1/example/route.ts +34 -0
- package/templates/next-base/src/app/api/v1/users/[id]/route.ts +104 -0
- package/templates/next-base/src/app/api/v1/users/route.ts +58 -0
- package/templates/next-base/src/app/layout.tsx +14 -6
- package/templates/next-base/src/app/page.tsx +2 -25
- package/templates/next-base/src/components/branding/logo.tsx +27 -4
- package/templates/next-base/src/components/layout/auth/auth-shell.tsx +24 -0
- package/templates/next-base/src/components/layout/private/app-sidebar.tsx +32 -0
- package/templates/next-base/src/components/layout/private/dashboard-layout.tsx +63 -0
- package/templates/next-base/src/components/layout/private/locale-switcher.tsx +45 -0
- package/templates/next-base/src/components/layout/private/nav-sidebar.tsx +55 -0
- package/templates/next-base/src/components/layout/private/nav-user.tsx +99 -0
- package/templates/next-base/src/components/layout/private/page-shell.tsx +40 -0
- package/templates/next-base/src/components/providers/query-provider.tsx +17 -0
- package/templates/next-base/src/components/ui/data-table/data-table-column-header.tsx +23 -0
- package/templates/next-base/src/components/ui/data-table/data-table-filter-list.tsx +3 -0
- package/templates/next-base/src/components/ui/data-table/data-table-pagination.tsx +35 -0
- package/templates/next-base/src/components/ui/data-table/data-table-skeleton.tsx +11 -0
- package/templates/next-base/src/components/ui/data-table/data-table-toolbar.tsx +14 -0
- package/templates/next-base/src/components/ui/data-table/data-table-view-options.tsx +3 -0
- package/templates/next-base/src/components/ui/data-table/data-table.tsx +72 -0
- package/templates/next-base/src/components/ui/sidebar.tsx +215 -0
- package/templates/next-base/src/data/sidebar-modules.ts +11 -0
- package/templates/next-base/src/example/api/use-example.ts +21 -0
- package/templates/next-base/src/example/api/use-mutations.ts +20 -0
- package/templates/next-base/src/example/components/example-table.tsx +64 -0
- package/templates/next-base/src/example/services.ts +9 -0
- package/templates/next-base/src/example/validations.ts +8 -0
- package/templates/next-base/src/features/auth/components/account-panel.tsx +92 -0
- package/templates/next-base/src/features/auth/components/change-password-form.tsx +79 -0
- package/templates/next-base/src/features/auth/components/sign-in-form.tsx +86 -0
- package/templates/next-base/src/features/auth/validations.ts +14 -0
- package/templates/next-base/src/features/users/services.ts +132 -0
- package/templates/next-base/src/features/users/validations.ts +21 -0
- package/templates/next-base/src/hooks/table/use-data-table.ts +33 -0
- package/templates/next-base/src/hooks/use-mobile.ts +25 -0
- package/templates/next-base/src/i18n/config.ts +7 -0
- package/templates/next-base/src/i18n/namespaces.ts +5 -0
- package/templates/next-base/src/i18n/request.ts +25 -0
- package/templates/next-base/src/instrumentation.ts +14 -0
- package/templates/next-base/src/lib/api/axios.ts +56 -0
- package/templates/next-base/src/lib/api/response.ts +45 -0
- package/templates/next-base/src/lib/auth/bootstrap.ts +95 -0
- package/templates/next-base/src/lib/auth/client.ts +7 -0
- package/templates/next-base/src/lib/auth/index.ts +1 -0
- package/templates/next-base/src/lib/auth/rbac.ts +59 -0
- package/templates/next-base/src/lib/auth/server.ts +21 -0
- package/templates/next-base/src/lib/constants.ts +10 -0
- package/templates/next-base/src/lib/db/prisma.ts +23 -0
- package/templates/next-base/src/lib/prisma.ts +23 -0
- package/templates/next-base/src/lib/supabase/client.ts +6 -0
- package/templates/next-base/src/lib/supabase/rich-text-image-sync.ts +28 -0
- package/templates/next-base/src/lib/supabase/storage-config.ts +69 -0
- package/templates/next-base/src/lib/supabase/storage.ts +164 -0
- package/templates/next-base/src/types/data-table.ts +4 -0
- package/templates/features/api/src/lib/api/token-store.ts +0 -13
- package/templates/features/auth/src/app/api/v1/auth/refresh/route.ts +0 -32
- package/templates/features/auth/src/lib/auth/cookies.ts +0 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Moon, Sun } from "lucide-react";
|
|
3
|
+
import { Menu, Moon, Sun } from "lucide-react";
|
|
4
4
|
import { useTheme } from "next-themes";
|
|
5
5
|
import { useTranslations } from "next-intl";
|
|
6
6
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
|
@@ -19,33 +19,42 @@ export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
|
19
19
|
return (
|
|
20
20
|
<SidebarProvider>
|
|
21
21
|
<div className="flex h-screen w-full flex-col">
|
|
22
|
-
<header className="flex h-
|
|
23
|
-
<div className="flex items-center
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
<header className="relative z-50 flex h-[3.75rem] shrink-0 items-center border-b border-border bg-card">
|
|
23
|
+
<div className="flex w-full items-center justify-between px-4 md:px-6">
|
|
24
|
+
<div className="flex items-center gap-2">
|
|
25
|
+
{isMobile ? (
|
|
26
|
+
<SidebarTrigger variant="outline" size="icon">
|
|
27
|
+
<Menu className="h-5 w-5" />
|
|
28
|
+
</SidebarTrigger>
|
|
29
|
+
) : null}
|
|
30
|
+
<Logo variant="header" />
|
|
31
|
+
</div>
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
{!isMobile ? (
|
|
35
|
+
<Button
|
|
36
|
+
variant="ghost"
|
|
37
|
+
size="icon"
|
|
38
|
+
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
|
39
|
+
title={t("toggleTheme")}
|
|
40
|
+
className="rounded-full"
|
|
41
|
+
>
|
|
42
|
+
{theme === "dark" ? (
|
|
43
|
+
<Sun className="h-5 w-5" />
|
|
44
|
+
) : (
|
|
45
|
+
<Moon className="h-5 w-5" />
|
|
46
|
+
)}
|
|
47
|
+
</Button>
|
|
48
|
+
) : null}
|
|
49
|
+
<NavUser />
|
|
50
|
+
</div>
|
|
42
51
|
</div>
|
|
43
52
|
</header>
|
|
44
53
|
|
|
45
54
|
<div className="flex flex-1 overflow-hidden">
|
|
46
55
|
<AppSidebar />
|
|
47
|
-
<ScrollArea className="flex-1">
|
|
48
|
-
<main
|
|
56
|
+
<ScrollArea className="flex-1 bg-background">
|
|
57
|
+
<main>{children}</main>
|
|
49
58
|
</ScrollArea>
|
|
50
59
|
</div>
|
|
51
60
|
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { cn } from "@/utils/cn";
|
|
3
|
+
|
|
4
|
+
type PageShellProps = {
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
actions?: ReactNode;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function PageShell({
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
actions,
|
|
16
|
+
children,
|
|
17
|
+
className,
|
|
18
|
+
}: PageShellProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"mx-auto w-full max-w-screen-2xl px-4 py-10 sm:px-6 lg:px-10",
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
<div className="flex flex-col gap-4 pb-6 sm:flex-row sm:items-start sm:justify-between">
|
|
27
|
+
<div className="space-y-1">
|
|
28
|
+
<h1 className="text-3xl font-black tracking-tight">{title}</h1>
|
|
29
|
+
{description ? (
|
|
30
|
+
<p className="text-sm text-muted-foreground">{description}</p>
|
|
31
|
+
) : null}
|
|
32
|
+
</div>
|
|
33
|
+
{actions ? (
|
|
34
|
+
<div className="flex shrink-0 items-center gap-2">{actions}</div>
|
|
35
|
+
) : null}
|
|
36
|
+
</div>
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -70,5 +70,6 @@ model Verification {
|
|
|
70
70
|
createdAt DateTime? @default(now())
|
|
71
71
|
updatedAt DateTime? @updatedAt
|
|
72
72
|
}
|
|
73
|
+
|
|
73
74
|
// Optional Chatbox models are appended by NexTCLI only when adding the chat feature.
|
|
74
75
|
// Review generated schema changes before running migrations on production databases.
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"page": {
|
|
3
|
-
"title": "Ví dụ"
|
|
3
|
+
"title": "Ví dụ",
|
|
4
|
+
"description": "Trang mẫu với bảng dữ liệu, thẻ nội dung và nút thêm bản ghi.",
|
|
5
|
+
"add": "Thêm mới",
|
|
6
|
+
"createTitle": "Thêm bản ghi ví dụ",
|
|
7
|
+
"nameLabel": "Tên",
|
|
8
|
+
"descriptionLabel": "Mô tả",
|
|
9
|
+
"createSubmit": "Lưu",
|
|
10
|
+
"creating": "Đang lưu...",
|
|
11
|
+
"createSuccess": "Đã thêm bản ghi ví dụ.",
|
|
12
|
+
"createFailed": "Không thể thêm bản ghi ví dụ.",
|
|
13
|
+
"invalidInput": "Vui lòng nhập tên hợp lệ."
|
|
4
14
|
},
|
|
5
15
|
"table": {
|
|
6
16
|
"name": "Tên",
|
|
@@ -1,13 +1,102 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import type { FormEvent } from "react";
|
|
1
5
|
import { useTranslations } from "next-intl";
|
|
6
|
+
import { Plus } from "lucide-react";
|
|
7
|
+
import { toast } from "sonner";
|
|
8
|
+
import { useCreateExample } from "@/example/api/use-mutations";
|
|
9
|
+
import { createExampleSchema } from "@/example/validations";
|
|
2
10
|
import { ExampleTable } from "@/example/components/example-table";
|
|
11
|
+
import { PageShell } from "@/components/layout/private/page-shell";
|
|
12
|
+
import { Button } from "@/components/ui/button";
|
|
13
|
+
import {
|
|
14
|
+
Dialog,
|
|
15
|
+
DialogContent,
|
|
16
|
+
DialogFooter,
|
|
17
|
+
DialogHeader,
|
|
18
|
+
DialogTitle,
|
|
19
|
+
DialogTrigger,
|
|
20
|
+
} from "@/components/ui/dialog";
|
|
21
|
+
import { Input } from "@/components/ui/input";
|
|
22
|
+
import { Label } from "@/components/ui/label";
|
|
3
23
|
|
|
4
24
|
export default function ExamplePage() {
|
|
5
25
|
const t = useTranslations("example.page");
|
|
26
|
+
const createExample = useCreateExample();
|
|
27
|
+
const [open, setOpen] = useState(false);
|
|
28
|
+
const [name, setName] = useState("");
|
|
29
|
+
const [description, setDescription] = useState("");
|
|
30
|
+
|
|
31
|
+
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
|
|
34
|
+
const parsed = createExampleSchema.safeParse({ name, description });
|
|
35
|
+
if (!parsed.success) {
|
|
36
|
+
toast.error(t("invalidInput"));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await createExample.mutateAsync(parsed.data);
|
|
42
|
+
toast.success(t("createSuccess"));
|
|
43
|
+
setName("");
|
|
44
|
+
setDescription("");
|
|
45
|
+
setOpen(false);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const message =
|
|
48
|
+
error instanceof Error ? error.message : t("createFailed");
|
|
49
|
+
toast.error(message);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
6
52
|
|
|
7
53
|
return (
|
|
8
|
-
<
|
|
9
|
-
|
|
54
|
+
<PageShell
|
|
55
|
+
title={t("title")}
|
|
56
|
+
description={t("description")}
|
|
57
|
+
actions={
|
|
58
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
59
|
+
<DialogTrigger asChild>
|
|
60
|
+
<Button>
|
|
61
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
62
|
+
{t("add")}
|
|
63
|
+
</Button>
|
|
64
|
+
</DialogTrigger>
|
|
65
|
+
<DialogContent>
|
|
66
|
+
<DialogHeader>
|
|
67
|
+
<DialogTitle>{t("createTitle")}</DialogTitle>
|
|
68
|
+
</DialogHeader>
|
|
69
|
+
<form onSubmit={onSubmit} className="space-y-4">
|
|
70
|
+
<div className="space-y-2">
|
|
71
|
+
<Label htmlFor="example-name">{t("nameLabel")}</Label>
|
|
72
|
+
<Input
|
|
73
|
+
id="example-name"
|
|
74
|
+
value={name}
|
|
75
|
+
onChange={(event) => setName(event.target.value)}
|
|
76
|
+
required
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
<div className="space-y-2">
|
|
80
|
+
<Label htmlFor="example-description">
|
|
81
|
+
{t("descriptionLabel")}
|
|
82
|
+
</Label>
|
|
83
|
+
<Input
|
|
84
|
+
id="example-description"
|
|
85
|
+
value={description}
|
|
86
|
+
onChange={(event) => setDescription(event.target.value)}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
<DialogFooter>
|
|
90
|
+
<Button type="submit" disabled={createExample.isPending}>
|
|
91
|
+
{createExample.isPending ? t("creating") : t("createSubmit")}
|
|
92
|
+
</Button>
|
|
93
|
+
</DialogFooter>
|
|
94
|
+
</form>
|
|
95
|
+
</DialogContent>
|
|
96
|
+
</Dialog>
|
|
97
|
+
}
|
|
98
|
+
>
|
|
10
99
|
<ExampleTable />
|
|
11
|
-
</
|
|
100
|
+
</PageShell>
|
|
12
101
|
);
|
|
13
102
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "@tanstack/react-table";
|
|
11
11
|
import { useExample } from "@/example/api/use-example";
|
|
12
12
|
import { DataTable } from "@/components/ui/data-table/data-table";
|
|
13
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
13
14
|
|
|
14
15
|
type ExampleItem = {
|
|
15
16
|
id: string;
|
|
@@ -44,8 +45,20 @@ export function ExampleTable() {
|
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
if (isLoading) {
|
|
47
|
-
return
|
|
48
|
+
return (
|
|
49
|
+
<Card>
|
|
50
|
+
<CardContent className="py-8 text-sm text-muted-foreground">
|
|
51
|
+
{t("loading")}
|
|
52
|
+
</CardContent>
|
|
53
|
+
</Card>
|
|
54
|
+
);
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
return
|
|
57
|
+
return (
|
|
58
|
+
<Card>
|
|
59
|
+
<CardContent className="pt-6">
|
|
60
|
+
<DataTable table={table} />
|
|
61
|
+
</CardContent>
|
|
62
|
+
</Card>
|
|
63
|
+
);
|
|
51
64
|
}
|
package/templates/next-base/.env
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
# --- App ---
|
|
2
2
|
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
3
|
+
|
|
4
|
+
# --- module: database ---
|
|
5
|
+
DATABASE_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
|
|
6
|
+
DIRECT_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:5432/postgres"
|
|
7
|
+
|
|
8
|
+
# --- module: supabase ---
|
|
9
|
+
NEXT_PUBLIC_SUPABASE_URL=""
|
|
10
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=""
|
|
11
|
+
NEXT_PUBLIC_SUPABASE_STORAGE_BUCKET="public"
|
|
12
|
+
|
|
13
|
+
# --- module: auth ---
|
|
14
|
+
BETTER_AUTH_SECRET=""
|
|
15
|
+
BETTER_AUTH_URL="http://localhost:3000"
|
|
16
|
+
|
|
17
|
+
# --- module: i18n ---
|
|
18
|
+
NEXT_PUBLIC_DEFAULT_LOCALE="vi"
|
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
# --- App ---
|
|
2
2
|
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
3
|
+
|
|
4
|
+
# --- module: database ---
|
|
5
|
+
DATABASE_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
|
|
6
|
+
DIRECT_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:5432/postgres"
|
|
7
|
+
|
|
8
|
+
# --- module: supabase ---
|
|
9
|
+
NEXT_PUBLIC_SUPABASE_URL=""
|
|
10
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=""
|
|
11
|
+
NEXT_PUBLIC_SUPABASE_STORAGE_BUCKET="public"
|
|
12
|
+
|
|
13
|
+
# --- module: auth ---
|
|
14
|
+
BETTER_AUTH_SECRET=""
|
|
15
|
+
BETTER_AUTH_URL="http://localhost:3000"
|
|
16
|
+
|
|
17
|
+
# --- module: i18n ---
|
|
18
|
+
NEXT_PUBLIC_DEFAULT_LOCALE="vi"
|
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
# --- App ---
|
|
2
2
|
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
3
|
+
|
|
4
|
+
# --- module: database ---
|
|
5
|
+
DATABASE_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
|
|
6
|
+
DIRECT_URL="postgresql://postgres.your-project-ref:your-supabase-db-password@aws-0-us-east-1.pooler.supabase.com:5432/postgres"
|
|
7
|
+
|
|
8
|
+
# --- module: supabase ---
|
|
9
|
+
NEXT_PUBLIC_SUPABASE_URL=""
|
|
10
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=""
|
|
11
|
+
NEXT_PUBLIC_SUPABASE_STORAGE_BUCKET="public"
|
|
12
|
+
|
|
13
|
+
# --- module: auth ---
|
|
14
|
+
BETTER_AUTH_SECRET=""
|
|
15
|
+
BETTER_AUTH_URL="http://localhost:3000"
|
|
16
|
+
|
|
17
|
+
# --- module: i18n ---
|
|
18
|
+
NEXT_PUBLIC_DEFAULT_LOCALE="vi"
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Quick reference for env vars and branding after `nextcli create`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Default stack
|
|
6
6
|
|
|
7
|
-
The
|
|
7
|
+
The base template ships with database (Prisma + Postgres), Supabase client + Storage, Better Auth + RBAC, API client (Axios + React Query), i18n (next-intl), dashboard shell, and an example CRUD demo. Optional modules (`chat`, `supabase-realtime`, `seo`, `email`) can be added at create time or via `nextcli add module`.
|
|
8
8
|
|
|
9
9
|
## First run
|
|
10
10
|
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
11
|
+
1. Fill env keys in `.env` (see tables below).
|
|
12
|
+
2. `bun run db:migrate` (or your package manager equivalent)
|
|
13
|
+
3. `bun run dev`
|
|
14
|
+
4. Open `http://localhost:3000` (redirects to `/dashboard`)
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Bootstrap seeds `admin` / `admin1234` on first dev start.
|
|
15
17
|
|
|
16
18
|
## Branding (edit in repo, not env)
|
|
17
19
|
|
|
@@ -29,21 +31,71 @@ Set in `.env` / `.env.development`.
|
|
|
29
31
|
| --------------------- | ---------------------- | ------------------------------------- |
|
|
30
32
|
| `NEXT_PUBLIC_APP_URL` | Client-visible app URL | Your public site URL (e.g. localhost) |
|
|
31
33
|
|
|
32
|
-
##
|
|
33
|
-
|
|
34
|
-
Reference for all optional modules. Matching keys are merged into `.env` when selected at `nextcli create` or via `nextcli add module`.
|
|
34
|
+
## Base module environment
|
|
35
35
|
|
|
36
36
|
<!-- nextcli:enabled-modules:start -->
|
|
37
37
|
|
|
38
|
-
**Enabled modules:**
|
|
38
|
+
**Enabled modules:** `database`, `supabase`, `auth`, `api`, `i18n`, `dashboard`, `example`
|
|
39
39
|
|
|
40
40
|
<!-- nextcli:enabled-modules:end -->
|
|
41
41
|
|
|
42
42
|
<!-- nextcli:module-env:start -->
|
|
43
43
|
|
|
44
|
+
### Module: Database (Prisma + Postgres) (`database`)
|
|
45
|
+
|
|
46
|
+
| Variable | Where to get |
|
|
47
|
+
| -------------- | -------------------------------------------------------------------------------------- |
|
|
48
|
+
| `DATABASE_URL` | Supabase Dashboard → Connect → ORMs → Prisma → pooled URL (`:6543`, `?pgbouncer=true`) |
|
|
49
|
+
| `DIRECT_URL` | Supabase Dashboard → Connect → direct/session URL (`:5432`) |
|
|
50
|
+
|
|
51
|
+
Run `bun run db:migrate` after setting `DATABASE_URL`.
|
|
52
|
+
|
|
53
|
+
### Module: Supabase client + Storage (`supabase`)
|
|
54
|
+
|
|
55
|
+
| Variable | Where to get |
|
|
56
|
+
| ------------------------------------- | --------------------------------------------------------- |
|
|
57
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase Dashboard → Project Settings → API → Project URL |
|
|
58
|
+
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Same page → `anon` `public` key |
|
|
59
|
+
| `NEXT_PUBLIC_SUPABASE_STORAGE_BUCKET` | Storage bucket name (default scaffold: `public`) |
|
|
60
|
+
|
|
61
|
+
Create the bucket and RLS policies in Supabase Dashboard before uploads.
|
|
62
|
+
|
|
63
|
+
### Module: Auth (Better Auth + RBAC) (`auth`)
|
|
64
|
+
|
|
65
|
+
| Variable | Where to get |
|
|
66
|
+
| -------------------- | ---------------------------------------------- |
|
|
67
|
+
| `BETTER_AUTH_SECRET` | Auto-generated on create; rotate in production |
|
|
68
|
+
| `BETTER_AUTH_URL` | Your app URL (e.g. `http://localhost:3000`) |
|
|
69
|
+
|
|
70
|
+
Requires `database` (included in base). Bootstrap seeds `admin` / `admin1234` on first dev start.
|
|
71
|
+
|
|
72
|
+
### Module: API client (Axios + React Query) (`api`)
|
|
73
|
+
|
|
74
|
+
No extra env keys. Use `publicApi` / `protectedApi` from `src/lib/api/axios.ts` and `ok` / `fail` from `src/lib/api/response.ts`.
|
|
75
|
+
|
|
76
|
+
### Module: Internationalization (next-intl) (`i18n`)
|
|
77
|
+
|
|
78
|
+
| Variable | Where to get |
|
|
79
|
+
| ---------------------------- | ------------------------------------ |
|
|
80
|
+
| `NEXT_PUBLIC_DEFAULT_LOCALE` | Default locale code (scaffold: `vi`) |
|
|
81
|
+
|
|
82
|
+
Add more locales with `nextcli add language`.
|
|
83
|
+
|
|
84
|
+
### Module: Dashboard shell (`dashboard`)
|
|
85
|
+
|
|
86
|
+
Requires `auth`, `api`, and `i18n` (included in base). Protected routes redirect unauthenticated users to `/sign-in`.
|
|
87
|
+
|
|
88
|
+
### Module: Example CRUD demo (`example`)
|
|
89
|
+
|
|
90
|
+
Requires `dashboard` and `database` (included in base). Includes demo `Example` model in `prisma/schema.prisma` — run `db:migrate` after create.
|
|
91
|
+
|
|
44
92
|
<!-- nextcli:module-env:end -->
|
|
45
93
|
|
|
46
|
-
##
|
|
94
|
+
## Optional module environment
|
|
95
|
+
|
|
96
|
+
Additional keys are merged when optional modules are selected at `nextcli create` or via `nextcli add module`.
|
|
97
|
+
|
|
98
|
+
## After adding optional modules
|
|
47
99
|
|
|
48
100
|
1. Fill new env keys in `.env` and `.env.example`.
|
|
49
101
|
2. Run `bun run db:migrate` when a module adds Prisma models.
|