better-ts-stack 0.1.1 → 1.1.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 +12 -6
- package/dist/builder/configGenerator.d.ts.map +1 -1
- package/dist/builder/configGenerator.js +30 -24
- package/dist/builder/configGenerator.js.map +1 -1
- package/dist/builder/dependencyInstaller.d.ts.map +1 -1
- package/dist/builder/dependencyInstaller.js +1 -10
- package/dist/builder/dependencyInstaller.js.map +1 -1
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +3 -7
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/moduleSelector.d.ts.map +1 -1
- package/dist/builder/moduleSelector.js +1 -1
- package/dist/builder/moduleSelector.js.map +1 -1
- package/dist/builder/templateContext.d.ts +2 -2
- package/dist/builder/templateContext.d.ts.map +1 -1
- package/dist/builder/templateContext.js +1 -3
- package/dist/builder/templateContext.js.map +1 -1
- package/dist/modules/registry.d.ts.map +1 -1
- package/dist/modules/registry.js +2 -5
- package/dist/modules/registry.js.map +1 -1
- package/dist/output/nextSteps.d.ts.map +1 -1
- package/dist/output/nextSteps.js +7 -34
- package/dist/output/nextSteps.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -14
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/stack.d.ts +3 -0
- package/dist/prompts/stack.d.ts.map +1 -0
- package/dist/prompts/stack.js +77 -0
- package/dist/prompts/stack.js.map +1 -0
- package/dist/types/index.d.ts +12 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -10
- package/dist/types/index.js.map +1 -1
- package/dist/validators/index.d.ts +1 -1
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +1 -1
- package/package.json +12 -12
- package/templates/backend/express/config.json +44 -42
- package/templates/backend/express/eslint.config.mjs +40 -31
- package/templates/backend/express/src/index.ts +61 -46
- package/templates/backend/express/src/routes/health.ts +12 -12
- package/templates/backend/express/tsconfig.json +25 -23
- package/templates/frontend/nextjs/config.json +33 -27
- package/templates/frontend/nextjs/eslint.config.mjs +11 -11
- package/templates/frontend/nextjs/proxy.ts.hbs +9 -8
- package/templates/frontend/nextjs/tsconfig.json +34 -21
- package/templates/frontend/tanstack/components.json +18 -0
- package/templates/frontend/tanstack/config.json +49 -0
- package/templates/frontend/tanstack/eslint.config.mjs +23 -0
- package/templates/frontend/tanstack/gitignore +38 -0
- package/templates/frontend/tanstack/public/favicon.svg +7 -0
- package/templates/frontend/tanstack/src/components/ui/button.tsx +51 -0
- package/templates/frontend/tanstack/src/components/ui/card.tsx +60 -0
- package/templates/frontend/tanstack/src/components/ui/field.tsx +67 -0
- package/templates/frontend/tanstack/src/components/ui/input.tsx +18 -0
- package/templates/frontend/tanstack/src/lib/utils.ts +6 -0
- package/templates/frontend/tanstack/src/routeTree.gen.ts +68 -0
- package/templates/frontend/tanstack/src/router.tsx +32 -0
- package/templates/frontend/tanstack/src/routes/__root.tsx +70 -0
- package/templates/frontend/tanstack/src/routes/index.tsx.hbs +102 -0
- package/templates/frontend/tanstack/src/styles/app.css +85 -0
- package/templates/frontend/tanstack/tsconfig.json +22 -0
- package/templates/frontend/tanstack/vite.config.ts +14 -0
- package/templates/modules/auth/express/config.json +26 -1
- package/templates/modules/auth/express/src/routes/auth.ts.hbs +1 -1
- package/templates/modules/auth/express/src/services/userStore.ts.hbs +95 -107
- package/templates/modules/auth/nextjs/config.json +6 -6
- package/templates/modules/auth/nextjs/lib/auth.ts.hbs +34 -32
- package/templates/modules/auth/tanstack/config.json +33 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +135 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +49 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +153 -0
- package/templates/modules/auth/tanstack/src/lib/auth-client.ts +3 -0
- package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +12 -0
- package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +39 -0
- package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +38 -0
- package/templates/modules/auth/tanstack/src/routeTree.gen.ts +140 -0
- package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +16 -0
- package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +111 -0
- package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +37 -0
- package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +37 -0
- package/templates/modules/docker/.dockerignore +14 -13
- package/templates/modules/docker/Dockerfile.hbs +64 -9
- package/templates/modules/docker/config.json +19 -16
- package/templates/modules/docker/docker-compose.yml.hbs +9 -9
- package/templates/modules/drizzle/express/config.json +27 -0
- package/templates/modules/drizzle/express/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/express/src/lib/db.ts +13 -0
- package/templates/modules/drizzle/express/src/lib/schema.ts +10 -0
- package/templates/modules/drizzle/nextjs/config.json +27 -19
- package/templates/modules/drizzle/nextjs/lib/db.ts +3 -1
- package/templates/modules/drizzle/nextjs/lib/schema.ts.hbs +84 -84
- package/templates/modules/drizzle/tanstack/config.json +26 -0
- package/templates/modules/drizzle/tanstack/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -0
- package/templates/modules/drizzle/tanstack/src/lib/schema.ts.hbs +84 -0
- package/templates/modules/mongoose/config.json +16 -16
- package/templates/modules/mongoose/src/lib/db.ts.hbs +0 -13
- package/templates/modules/mongoose/src/models/User.ts +48 -47
- package/templates/modules/prisma/express/config.json +27 -21
- package/templates/modules/prisma/express/prisma/schema.prisma +3 -2
- package/templates/modules/prisma/express/prisma.config.ts +12 -13
- package/templates/modules/prisma/express/src/lib/prisma.ts +24 -37
- package/templates/modules/prisma/nextjs/config.json +28 -23
- package/templates/modules/prisma/nextjs/prisma/schema.prisma.hbs +91 -90
- package/templates/modules/prisma/tanstack/config.json +28 -0
- package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -0
- package/templates/modules/prisma/tanstack/prisma.config.ts +12 -0
- package/templates/modules/prisma/tanstack/src/lib/prisma.ts +18 -0
- package/dist/prompts/backend/index.d.ts +0 -3
- package/dist/prompts/backend/index.d.ts.map +0 -1
- package/dist/prompts/backend/index.js +0 -128
- package/dist/prompts/backend/index.js.map +0 -1
- package/dist/prompts/frontend/index.d.ts +0 -3
- package/dist/prompts/frontend/index.d.ts.map +0 -1
- package/dist/prompts/frontend/index.js +0 -111
- package/dist/prompts/frontend/index.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createFileRoute, redirect } from "@tanstack/react-router";
|
|
2
|
+
import { Link } from "@tanstack/react-router";
|
|
3
|
+
import { ArrowRight, ShieldCheck } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { SignOutButton } from "~/components/auth/sign-out-button";
|
|
6
|
+
import { buttonVariants } from "~/components/ui/button";
|
|
7
|
+
import {
|
|
8
|
+
Card,
|
|
9
|
+
CardContent,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
} from "~/components/ui/card";
|
|
14
|
+
import { getSession } from "~/lib/auth-functions";
|
|
15
|
+
import { cn } from "~/lib/utils";
|
|
16
|
+
|
|
17
|
+
export const Route = createFileRoute("/dashboard")({
|
|
18
|
+
beforeLoad: async () => {
|
|
19
|
+
const session = await getSession();
|
|
20
|
+
|
|
21
|
+
if (!session) {
|
|
22
|
+
throw redirect({ to: "/sign-in" });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { user: session.user };
|
|
26
|
+
},
|
|
27
|
+
component: Dashboard,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function Dashboard() {
|
|
31
|
+
const user = Route.useRouteContext({ select: (ctx) => ctx.user });
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<main className="min-h-screen bg-[linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
|
|
35
|
+
<div className="mx-auto max-w-5xl space-y-8">
|
|
36
|
+
<header className="flex flex-col gap-6 rounded-[2rem] border border-white/70 bg-white/85 p-8 shadow-xl shadow-slate-200/60 backdrop-blur md:flex-row md:items-end md:justify-between">
|
|
37
|
+
<div className="space-y-3">
|
|
38
|
+
<span className="inline-flex items-center gap-2 rounded-full bg-emerald-50 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-emerald-700">
|
|
39
|
+
<ShieldCheck className="size-4" />
|
|
40
|
+
Authenticated session
|
|
41
|
+
</span>
|
|
42
|
+
<div className="space-y-1">
|
|
43
|
+
<h1 className="text-3xl font-semibold tracking-tight text-slate-950">
|
|
44
|
+
Welcome {user.name}
|
|
45
|
+
</h1>
|
|
46
|
+
<p className="text-sm text-slate-600">{user.email}</p>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<SignOutButton />
|
|
50
|
+
</header>
|
|
51
|
+
|
|
52
|
+
<section className="grid gap-5 md:grid-cols-2">
|
|
53
|
+
<Card>
|
|
54
|
+
<CardHeader>
|
|
55
|
+
<CardTitle>Protected route example</CardTitle>
|
|
56
|
+
<CardDescription>
|
|
57
|
+
This page verifies the session on the server before rendering any
|
|
58
|
+
content.
|
|
59
|
+
</CardDescription>
|
|
60
|
+
</CardHeader>
|
|
61
|
+
<CardContent className="space-y-4 text-sm text-slate-600">
|
|
62
|
+
<p>
|
|
63
|
+
The route guard calls{" "}
|
|
64
|
+
<code className="font-mono text-slate-900">getSession()</code> in
|
|
65
|
+
<code className="font-mono text-slate-900"> beforeLoad</code>,
|
|
66
|
+
which reads the cookie headers server-side via{" "}
|
|
67
|
+
<code className="font-mono text-slate-900">
|
|
68
|
+
auth.api.getSession
|
|
69
|
+
</code>{" "}
|
|
70
|
+
on every render and navigation.
|
|
71
|
+
</p>
|
|
72
|
+
<p>
|
|
73
|
+
That keeps route protection aligned with Better Auth's
|
|
74
|
+
TanStack Start guidance while staying easy to extend.
|
|
75
|
+
</p>
|
|
76
|
+
</CardContent>
|
|
77
|
+
</Card>
|
|
78
|
+
|
|
79
|
+
<Card>
|
|
80
|
+
<CardHeader>
|
|
81
|
+
<CardTitle>Suggested next steps</CardTitle>
|
|
82
|
+
<CardDescription>
|
|
83
|
+
Keep the generated auth flow, then customize the app around your
|
|
84
|
+
product.
|
|
85
|
+
</CardDescription>
|
|
86
|
+
</CardHeader>
|
|
87
|
+
<CardContent className="space-y-3 text-sm text-slate-600">
|
|
88
|
+
<p>
|
|
89
|
+
Replace the demo dashboard with your first authenticated page.
|
|
90
|
+
</p>
|
|
91
|
+
<p>Extend the schema with profile or onboarding fields.</p>
|
|
92
|
+
<p>
|
|
93
|
+
Add provider-based auth or email verification later if needed.
|
|
94
|
+
</p>
|
|
95
|
+
<Link
|
|
96
|
+
to="/"
|
|
97
|
+
className={cn(
|
|
98
|
+
buttonVariants({ variant: "outline" }),
|
|
99
|
+
"w-fit gap-2"
|
|
100
|
+
)}
|
|
101
|
+
>
|
|
102
|
+
Back to home
|
|
103
|
+
<ArrowRight className="size-4" />
|
|
104
|
+
</Link>
|
|
105
|
+
</CardContent>
|
|
106
|
+
</Card>
|
|
107
|
+
</section>
|
|
108
|
+
</div>
|
|
109
|
+
</main>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createFileRoute, redirect } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
import { SignInForm } from "~/components/auth/sign-in-form";
|
|
4
|
+
import { getSession } from "~/lib/auth-functions";
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute("/sign-in")({
|
|
7
|
+
beforeLoad: async () => {
|
|
8
|
+
const session = await getSession();
|
|
9
|
+
|
|
10
|
+
if (session) {
|
|
11
|
+
throw redirect({ to: "/dashboard" });
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
component: SignInPage,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function SignInPage() {
|
|
18
|
+
return (
|
|
19
|
+
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(14,165,233,0.16),_transparent_30%),linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
|
|
20
|
+
<div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
|
|
21
|
+
<section className="hidden space-y-5 lg:block">
|
|
22
|
+
<span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
|
|
23
|
+
Better Auth
|
|
24
|
+
</span>
|
|
25
|
+
<h1 className="text-4xl font-semibold tracking-tight text-slate-950">
|
|
26
|
+
Welcome back.
|
|
27
|
+
</h1>
|
|
28
|
+
<p className="max-w-md text-base leading-7 text-slate-600">
|
|
29
|
+
This generated starter includes protected routes, session-aware page
|
|
30
|
+
guards, and reusable auth form primitives you can extend from here.
|
|
31
|
+
</p>
|
|
32
|
+
</section>
|
|
33
|
+
<SignInForm />
|
|
34
|
+
</div>
|
|
35
|
+
</main>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createFileRoute, redirect } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
import { SignUpForm } from "~/components/auth/sign-up-form";
|
|
4
|
+
import { getSession } from "~/lib/auth-functions";
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute("/sign-up")({
|
|
7
|
+
beforeLoad: async () => {
|
|
8
|
+
const session = await getSession();
|
|
9
|
+
|
|
10
|
+
if (session) {
|
|
11
|
+
throw redirect({ to: "/dashboard" });
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
component: SignUpPage,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function SignUpPage() {
|
|
18
|
+
return (
|
|
19
|
+
<main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(56,189,248,0.18),_transparent_28%),linear-gradient(180deg,_#f8fafc_0%,_#ecfeff_100%)] px-6 py-16 sm:px-10">
|
|
20
|
+
<div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
|
|
21
|
+
<section className="hidden space-y-5 lg:block">
|
|
22
|
+
<span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
|
|
23
|
+
Better Auth
|
|
24
|
+
</span>
|
|
25
|
+
<h1 className="text-4xl font-semibold tracking-tight text-slate-950">
|
|
26
|
+
Create your account.
|
|
27
|
+
</h1>
|
|
28
|
+
<p className="max-w-md text-base leading-7 text-slate-600">
|
|
29
|
+
Start with email and password today, then layer in profile data,
|
|
30
|
+
onboarding, and provider-based auth when your product needs it.
|
|
31
|
+
</p>
|
|
32
|
+
</section>
|
|
33
|
+
<SignUpForm />
|
|
34
|
+
</div>
|
|
35
|
+
</main>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
node_modules
|
|
2
|
-
npm-debug.log
|
|
3
|
-
.env
|
|
4
|
-
.
|
|
5
|
-
.
|
|
6
|
-
.
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
node_modules
|
|
2
|
+
npm-debug.log
|
|
3
|
+
.env*
|
|
4
|
+
.git
|
|
5
|
+
.gitignore
|
|
6
|
+
README.md
|
|
7
|
+
.vscode
|
|
8
|
+
.idea
|
|
9
|
+
dist
|
|
10
|
+
coverage
|
|
11
|
+
.DS_Store
|
|
12
|
+
*.log
|
|
13
|
+
|
|
14
|
+
.next
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{{#if (eq framework 'nextjs')}}
|
|
2
2
|
# Base stage
|
|
3
|
-
FROM node:
|
|
3
|
+
FROM node:24-alpine AS base
|
|
4
4
|
WORKDIR /app
|
|
5
5
|
RUN apk add --no-cache libc6-compat
|
|
6
6
|
|
|
@@ -13,10 +13,16 @@ RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
|
13
13
|
FROM base AS builder
|
|
14
14
|
COPY --from=deps /app/node_modules ./node_modules
|
|
15
15
|
COPY . .
|
|
16
|
+
{{#if (eq database 'prisma')}}
|
|
17
|
+
RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
|
|
18
|
+
{{else if (eq database 'mongoose')}}
|
|
19
|
+
RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
|
|
20
|
+
{{else}}
|
|
16
21
|
RUN npm run build
|
|
22
|
+
{{/if}}
|
|
17
23
|
|
|
18
24
|
# Production stage
|
|
19
|
-
FROM node:
|
|
25
|
+
FROM node:24-alpine AS runner
|
|
20
26
|
WORKDIR /app
|
|
21
27
|
ENV NODE_ENV=production
|
|
22
28
|
|
|
@@ -33,9 +39,54 @@ ENV PORT={{port}} HOSTNAME=0.0.0.0
|
|
|
33
39
|
|
|
34
40
|
CMD ["node", "server.js"]
|
|
35
41
|
|
|
42
|
+
{{else if (eq framework 'tanstack')}}
|
|
43
|
+
# Build stage
|
|
44
|
+
FROM node:24-alpine AS builder
|
|
45
|
+
WORKDIR /app
|
|
46
|
+
|
|
47
|
+
COPY package*.json ./
|
|
48
|
+
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
49
|
+
|
|
50
|
+
COPY . .
|
|
51
|
+
|
|
52
|
+
# Only build if package.json has a build script
|
|
53
|
+
{{#if (eq database 'prisma')}}
|
|
54
|
+
RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
|
|
55
|
+
{{else if (eq database 'mongoose')}}
|
|
56
|
+
RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
|
|
57
|
+
{{else}}
|
|
58
|
+
RUN npm run build
|
|
59
|
+
{{/if}}
|
|
60
|
+
|
|
61
|
+
# Production stage
|
|
62
|
+
FROM node:24-alpine
|
|
63
|
+
WORKDIR /app
|
|
64
|
+
ENV NODE_ENV=production
|
|
65
|
+
|
|
66
|
+
# Create non-root user for security
|
|
67
|
+
RUN addgroup --system --gid 1001 nodejs && \
|
|
68
|
+
adduser --system --uid 1001 tanstackuser
|
|
69
|
+
|
|
70
|
+
COPY package*.json ./
|
|
71
|
+
RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi
|
|
72
|
+
|
|
73
|
+
# Copy built output (dist/server/server.js + dist/client)
|
|
74
|
+
COPY --from=builder --chown=tanstackuser:nodejs /app/dist ./dist
|
|
75
|
+
|
|
76
|
+
# Include the Prisma client generated during the build stage
|
|
77
|
+
{{#if (eq database 'prisma')}}
|
|
78
|
+
COPY --from=builder --chown=tanstackuser:nodejs /app/generated ./generated
|
|
79
|
+
{{/if}}
|
|
80
|
+
|
|
81
|
+
USER tanstackuser
|
|
82
|
+
EXPOSE {{port}}
|
|
83
|
+
ENV PORT={{port}}
|
|
84
|
+
|
|
85
|
+
CMD ["node_modules/.bin/srvx", "--prod", "-s", "../client", "dist/server/server.js"]
|
|
86
|
+
|
|
36
87
|
{{else}}
|
|
37
88
|
# Build stage
|
|
38
|
-
FROM node:
|
|
89
|
+
FROM node:24-alpine AS builder
|
|
39
90
|
WORKDIR /app
|
|
40
91
|
|
|
41
92
|
COPY package*.json ./
|
|
@@ -44,10 +95,16 @@ RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
|
44
95
|
COPY . .
|
|
45
96
|
|
|
46
97
|
# Only build if package.json has a build script
|
|
47
|
-
|
|
98
|
+
{{#if (eq database 'prisma')}}
|
|
99
|
+
RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
|
|
100
|
+
{{else if (eq database 'mongoose')}}
|
|
101
|
+
RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
|
|
102
|
+
{{else}}
|
|
103
|
+
RUN npm run build
|
|
104
|
+
{{/if}}
|
|
48
105
|
|
|
49
106
|
# Production stage
|
|
50
|
-
FROM node:
|
|
107
|
+
FROM node:24-alpine
|
|
51
108
|
WORKDIR /app
|
|
52
109
|
ENV NODE_ENV=production
|
|
53
110
|
|
|
@@ -59,13 +116,11 @@ COPY package*.json ./
|
|
|
59
116
|
RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi
|
|
60
117
|
|
|
61
118
|
# Copy built files (if TypeScript) or source files (if JavaScript)
|
|
62
|
-
COPY --from=builder --chown=expressuser:nodejs /app/dist ./dist
|
|
63
|
-
COPY --chown=expressuser:nodejs /app/src ./src 2>/dev/null || \
|
|
64
|
-
COPY --chown=expressuser:nodejs /app/*.js ./
|
|
119
|
+
COPY --from=builder --chown=expressuser:nodejs /app/dist ./dist
|
|
65
120
|
|
|
66
121
|
USER expressuser
|
|
67
122
|
EXPOSE {{port}}
|
|
68
123
|
ENV PORT={{port}}
|
|
69
124
|
|
|
70
|
-
CMD ["node", "
|
|
125
|
+
CMD ["node", "dist/index.js"]
|
|
71
126
|
{{/if}}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "docker",
|
|
3
|
-
"name": "Docker",
|
|
4
|
-
"description": "Docker containerization with multi-stage build and docker-compose",
|
|
5
|
-
"type": "feature",
|
|
6
|
-
"dependencies": {},
|
|
7
|
-
"devDependencies": {},
|
|
8
|
-
"scripts": {
|
|
9
|
-
"docker:build": "docker build -t {{projectName}} .",
|
|
10
|
-
"docker:up": "docker
|
|
11
|
-
"docker:down": "docker
|
|
12
|
-
"docker:logs": "docker
|
|
13
|
-
},
|
|
14
|
-
"envVars": {},
|
|
15
|
-
"templateFiles": [
|
|
16
|
-
|
|
1
|
+
{
|
|
2
|
+
"id": "docker",
|
|
3
|
+
"name": "Docker",
|
|
4
|
+
"description": "Docker containerization with multi-stage build and docker-compose",
|
|
5
|
+
"type": "feature",
|
|
6
|
+
"dependencies": {},
|
|
7
|
+
"devDependencies": {},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"docker:build": "docker build -t {{projectName}} .",
|
|
10
|
+
"docker:up": "docker compose up -d",
|
|
11
|
+
"docker:down": "docker compose down",
|
|
12
|
+
"docker:logs": "docker compose logs -f"
|
|
13
|
+
},
|
|
14
|
+
"envVars": {},
|
|
15
|
+
"templateFiles": [
|
|
16
|
+
"docker-compose.yml.hbs",
|
|
17
|
+
"Dockerfile.hbs"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
environment:
|
|
9
|
-
- NODE_ENV=production
|
|
1
|
+
services:
|
|
2
|
+
{{projectName}}:
|
|
3
|
+
build: .
|
|
4
|
+
ports:
|
|
5
|
+
- "{{port}}:{{port}}"
|
|
6
|
+
env_file:
|
|
7
|
+
- .env
|
|
8
|
+
environment:
|
|
9
|
+
- NODE_ENV=production
|
|
10
10
|
restart: unless-stopped
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "drizzle",
|
|
3
|
+
"name": "Drizzle ORM",
|
|
4
|
+
"description": "Drizzle ORM for Express with PostgreSQL",
|
|
5
|
+
"type": "database",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"drizzle-orm": "^0.45.2",
|
|
8
|
+
"pg": "^8.23.0",
|
|
9
|
+
"dotenv": "^17.4.2"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"drizzle-kit": "^0.31.10",
|
|
13
|
+
"@types/pg": "^8.23.1"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"db:generate": "drizzle-kit generate",
|
|
17
|
+
"db:migrate": "drizzle-kit migrate",
|
|
18
|
+
"db:studio": "drizzle-kit studio"
|
|
19
|
+
},
|
|
20
|
+
"envVars": {
|
|
21
|
+
"DATABASE_URL": "postgresql://user:password@localhost:5432/mydb?schema=public"
|
|
22
|
+
},
|
|
23
|
+
"templateFiles": [],
|
|
24
|
+
"overrides": {
|
|
25
|
+
"esbuild": "^0.28.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
import { defineConfig } from "drizzle-kit";
|
|
3
|
+
|
|
4
|
+
config({ path: ".env" });
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
schema: "./src/lib/schema.ts",
|
|
8
|
+
out: "./migrations",
|
|
9
|
+
dialect: "postgresql",
|
|
10
|
+
dbCredentials: {
|
|
11
|
+
url: process.env.DATABASE_URL!,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
|
+
import { Pool } from "pg";
|
|
4
|
+
|
|
5
|
+
import * as schema from "./schema";
|
|
6
|
+
|
|
7
|
+
config({ path: ".env" }); // or .env.local
|
|
8
|
+
|
|
9
|
+
const pool = new Pool({
|
|
10
|
+
connectionString: process.env.DATABASE_URL,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const db = drizzle(pool, { schema });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
2
|
+
|
|
3
|
+
export const users = pgTable("users", {
|
|
4
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5
|
+
email: text("email").notNull().unique(),
|
|
6
|
+
name: text("name"),
|
|
7
|
+
password: text("password").notNull(),
|
|
8
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
9
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
|
10
|
+
});
|
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "drizzle",
|
|
3
|
-
"name": "Drizzle ORM",
|
|
4
|
-
"description": "Drizzle ORM for Next.js with PostgreSQL",
|
|
5
|
-
"type": "database",
|
|
1
|
+
{
|
|
2
|
+
"id": "drizzle",
|
|
3
|
+
"name": "Drizzle ORM",
|
|
4
|
+
"description": "Drizzle ORM for Next.js with PostgreSQL",
|
|
5
|
+
"type": "database",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"drizzle-orm": "^0.
|
|
8
|
-
"pg": "^8.
|
|
9
|
-
"dotenv": "^17.
|
|
7
|
+
"drizzle-orm": "^0.45.2",
|
|
8
|
+
"pg": "^8.23.0",
|
|
9
|
+
"dotenv": "^17.4.2"
|
|
10
10
|
},
|
|
11
|
-
"devDependencies": {
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
},
|
|
20
|
-
"
|
|
21
|
-
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"drizzle-kit": "^0.31.10",
|
|
13
|
+
"@types/pg": "^8.23.1"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"db:generate": "drizzle-kit generate",
|
|
17
|
+
"db:migrate": "drizzle-kit migrate",
|
|
18
|
+
"db:studio": "drizzle-kit studio"
|
|
19
|
+
},
|
|
20
|
+
"envVars": {
|
|
21
|
+
"DATABASE_URL": "postgresql://user:password@localhost:5432/mydb?schema=public"
|
|
22
|
+
},
|
|
23
|
+
"templateFiles": [
|
|
24
|
+
"lib/schema.ts.hbs"
|
|
25
|
+
],
|
|
26
|
+
"overrides": {
|
|
27
|
+
"esbuild": "^0.28.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -2,10 +2,12 @@ import { config } from "dotenv";
|
|
|
2
2
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
3
|
import { Pool } from "pg";
|
|
4
4
|
|
|
5
|
+
import * as schema from "./schema";
|
|
6
|
+
|
|
5
7
|
config({ path: ".env" }); // or .env.local
|
|
6
8
|
|
|
7
9
|
const pool = new Pool({
|
|
8
10
|
connectionString: process.env.DATABASE_URL,
|
|
9
11
|
});
|
|
10
12
|
|
|
11
|
-
export const db = drizzle(pool);
|
|
13
|
+
export const db = drizzle(pool, { schema });
|