better-ts-stack 2.0.1 → 2.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.
Files changed (46) hide show
  1. package/README.md +114 -113
  2. package/dist/args.js +24 -20
  3. package/dist/builder/buildPlanner.js +1 -1
  4. package/dist/builder/configGenerator.js +1 -5
  5. package/dist/builder/index.js +15 -3
  6. package/dist/builder/moduleSelector.js +4 -2
  7. package/dist/builder/readmeGenerator.js +101 -0
  8. package/dist/builder/templateContext.js +30 -0
  9. package/dist/index.js +7 -1
  10. package/dist/output/nextSteps.js +22 -23
  11. package/dist/prompts/index.js +2 -1
  12. package/dist/prompts/stack.js +21 -2
  13. package/dist/types/index.js +2 -1
  14. package/dist/ui/messages.js +2 -1
  15. package/dist/validators/index.js +5 -2
  16. package/package.json +1 -1
  17. package/templates/backend/express/config.json +42 -40
  18. package/templates/backend/express/src/env.ts.hbs +39 -0
  19. package/templates/backend/express/src/index.ts.hbs +111 -75
  20. package/templates/backend/nestjs/config.json +43 -41
  21. package/templates/backend/nestjs/src/app.module.ts.hbs +13 -0
  22. package/templates/backend/nestjs/src/env.ts.hbs +39 -0
  23. package/templates/backend/nestjs/src/main.ts +3 -2
  24. package/templates/frontend/nextjs/app/page.tsx.hbs +98 -97
  25. package/templates/frontend/tanstack/config.json +45 -45
  26. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +102 -101
  27. package/templates/frontend/tanstack/vite.config.ts +22 -14
  28. package/templates/modules/auth/express/config.json +20 -20
  29. package/templates/modules/auth/express/src/routes/auth.ts.hbs +27 -19
  30. package/templates/modules/auth/nestjs/config.json +20 -20
  31. package/templates/modules/auth/nestjs/src/auth/{auth.controller.ts → auth.controller.ts.hbs} +15 -0
  32. package/templates/modules/auth/nextjs/lib/auth-schema.ts +1 -1
  33. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +132 -134
  34. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +46 -48
  35. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +150 -152
  36. package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +38 -38
  37. package/templates/modules/docker/.dockerignore +1 -0
  38. package/templates/modules/docker/Dockerfile.hbs +147 -136
  39. package/templates/modules/docker/docker-compose.yml.hbs +96 -1
  40. package/templates/modules/drizzle/express/src/lib/schema.ts +3 -1
  41. package/templates/modules/drizzle/nestjs/src/database/schema.ts +3 -1
  42. package/templates/modules/drizzle/nextjs/lib/schema.ts.hbs +11 -5
  43. package/templates/modules/drizzle/tanstack/src/lib/schema.ts.hbs +90 -84
  44. package/templates/modules/rate-limit/express/config.json +12 -0
  45. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +21 -0
  46. package/templates/modules/rate-limit/nestjs/config.json +12 -0
@@ -1,136 +1,147 @@
1
- {{#if (eq framework 'nextjs')}}
2
- # Base stage
3
- FROM node:24-alpine AS base
4
- WORKDIR /app
5
- RUN apk add --no-cache libc6-compat
6
-
7
- # Dependencies stage
8
- FROM base AS deps
9
- COPY package*.json ./
10
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
11
-
12
- # Build stage
13
- FROM base AS builder
14
- COPY --from=deps /app/node_modules ./node_modules
15
- COPY . .
16
- {{#if useAuth}}
17
- # next build loads the auth route, which initializes Better Auth. The build gets
18
- # a placeholder secret for this command only; the real one is supplied at runtime.
19
- {{/if}}
20
- {{#if (eq database 'prisma')}}
21
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
22
- {{else if (eq database 'mongoose')}}
23
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}MONGODB_URI="mongodb://localhost:27017/build" npm run build
24
- {{else}}
25
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}npm run build
26
- {{/if}}
27
-
28
- # Production stage
29
- FROM node:24-alpine AS runner
30
- WORKDIR /app
31
- ENV NODE_ENV=production
32
-
33
- RUN addgroup --system --gid 1001 nodejs && \
34
- adduser --system --uid 1001 nextjs
35
-
36
- COPY --from=builder /app/public ./public
37
- COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
38
- COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
39
-
40
- USER nextjs
41
- EXPOSE {{port}}
42
- ENV PORT={{port}} HOSTNAME=0.0.0.0
43
-
44
- CMD ["node", "server.js"]
45
-
46
- {{else if (eq framework 'tanstack')}}
47
- # Build stage
48
- FROM node:24-alpine AS builder
49
- WORKDIR /app
50
-
51
- COPY package*.json ./
52
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
53
-
54
- COPY . .
55
-
56
- {{#if (eq database 'prisma')}}
57
- RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
58
- {{else if (eq database 'mongoose')}}
59
- RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
60
- {{else}}
61
- RUN npm run build
62
- {{/if}}
63
-
64
- # Production stage
65
- FROM node:24-alpine
66
- WORKDIR /app
67
- ENV NODE_ENV=production
68
-
69
- # Create non-root user for security
70
- RUN addgroup --system --gid 1001 nodejs && \
71
- adduser --system --uid 1001 tanstackuser
72
-
73
- COPY package*.json ./
74
- # Clear the npm cache in the same layer so it does not ship in the image
75
- RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
76
- && npm cache clean --force
77
-
78
- # Copy built output (dist/server/server.js + dist/client)
79
- COPY --from=builder --chown=tanstackuser:nodejs /app/dist ./dist
80
-
81
- # Include the Prisma client generated during the build stage
82
- {{#if (eq database 'prisma')}}
83
- COPY --from=builder --chown=tanstackuser:nodejs /app/generated ./generated
84
- {{/if}}
85
-
86
- USER tanstackuser
87
- EXPOSE {{port}}
88
- ENV PORT={{port}}
89
-
90
- CMD ["node_modules/.bin/srvx", "--prod", "-s", "../client", "dist/server/server.js"]
91
-
92
- {{else}}
93
- # Build stage
94
- FROM node:24-alpine AS builder
95
- WORKDIR /app
96
-
97
- COPY package*.json ./
98
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
99
-
100
- COPY . .
101
-
102
- {{#if (eq database 'prisma')}}
103
- RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
104
- {{else if (eq database 'mongoose')}}
105
- RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
106
- {{else}}
107
- RUN npm run build
108
- {{/if}}
109
-
110
- # Production stage
111
- FROM node:24-alpine
112
- WORKDIR /app
113
- ENV NODE_ENV=production
114
-
115
- # Create non-root user for security
116
- RUN addgroup --system --gid 1001 nodejs && \
117
- adduser --system --uid 1001 appuser
118
-
119
- COPY package*.json ./
120
- # Clear the npm cache in the same layer so it does not ship in the image
121
- RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
122
- && npm cache clean --force
123
-
124
- # Copy the compiled output
125
- COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
126
-
127
- USER appuser
128
- EXPOSE {{port}}
129
- ENV PORT={{port}}
130
-
131
- {{#if (eq framework 'nestjs')}}
132
- CMD ["node", "dist/main.js"]
133
- {{else}}
134
- CMD ["node", "dist/index.js"]
135
- {{/if}}
136
- {{/if}}
1
+ {{#if (eq framework 'nextjs')}}
2
+ # Base stage
3
+ FROM node:24-alpine AS base
4
+ WORKDIR /app
5
+ RUN apk add --no-cache libc6-compat
6
+ {{#if docker.setup}}
7
+ {{docker.setup}}
8
+ {{/if}}
9
+
10
+ # Dependencies stage
11
+ FROM base AS deps
12
+ COPY {{docker.manifests}} ./
13
+ RUN {{docker.install}}
14
+
15
+ # Build stage
16
+ FROM base AS builder
17
+ COPY --from=deps /app/node_modules ./node_modules
18
+ COPY . .
19
+ {{#if useAuth}}
20
+ # next build loads the auth route, which initializes Better Auth. The build gets
21
+ # a placeholder secret for this command only; the real one is supplied at runtime.
22
+ {{/if}}
23
+ {{#if (eq database 'prisma')}}
24
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" {{docker.run}} build
25
+ {{else if (eq database 'mongoose')}}
26
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}MONGODB_URI="mongodb://localhost:27017/build" {{docker.run}} build
27
+ {{else}}
28
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}{{docker.run}} build
29
+ {{/if}}
30
+
31
+ # Production stage
32
+ FROM node:24-alpine AS runner
33
+ WORKDIR /app
34
+ ENV NODE_ENV=production
35
+
36
+ RUN addgroup --system --gid 1001 nodejs && \
37
+ adduser --system --uid 1001 nextjs
38
+
39
+ COPY --from=builder /app/public ./public
40
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
41
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
42
+
43
+ USER nextjs
44
+ EXPOSE {{port}}
45
+ ENV PORT={{port}} HOSTNAME=0.0.0.0
46
+
47
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
48
+ CMD wget -q -O /dev/null http://127.0.0.1:{{port}}/ || exit 1
49
+
50
+ CMD ["node", "server.js"]
51
+
52
+ {{else if (eq framework 'tanstack')}}
53
+ # Build stage
54
+ FROM node:24-alpine AS builder
55
+ WORKDIR /app
56
+ {{#if docker.setup}}
57
+ {{docker.setup}}
58
+ {{/if}}
59
+
60
+ COPY {{docker.manifests}} ./
61
+ RUN {{docker.install}}
62
+
63
+ COPY . .
64
+
65
+ {{#if (eq database 'prisma')}}
66
+ RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" {{docker.run}} build
67
+ {{else if (eq database 'mongoose')}}
68
+ RUN MONGODB_URI="mongodb://localhost:27017/build" {{docker.run}} build
69
+ {{else}}
70
+ RUN {{docker.run}} build
71
+ {{/if}}
72
+
73
+ # Production stage. Nitro bundles the server and the dependencies it needs
74
+ # into .output, so no install is needed here
75
+ FROM node:24-alpine
76
+ WORKDIR /app
77
+ ENV NODE_ENV=production
78
+
79
+ # Create non-root user for security
80
+ RUN addgroup --system --gid 1001 nodejs && \
81
+ adduser --system --uid 1001 tanstackuser
82
+
83
+ COPY --from=builder --chown=tanstackuser:nodejs /app/.output ./.output
84
+
85
+ USER tanstackuser
86
+ EXPOSE {{port}}
87
+ ENV PORT={{port}}
88
+
89
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
90
+ CMD wget -q -O /dev/null http://127.0.0.1:{{port}}/ || exit 1
91
+
92
+ CMD ["node", ".output/server/index.mjs"]
93
+
94
+ {{else}}
95
+ # Build stage
96
+ FROM node:24-alpine AS builder
97
+ WORKDIR /app
98
+ {{#if docker.setup}}
99
+ {{docker.setup}}
100
+ {{/if}}
101
+
102
+ COPY {{docker.manifests}} ./
103
+ RUN {{docker.install}}
104
+
105
+ COPY . .
106
+
107
+ {{#if (eq database 'prisma')}}
108
+ RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" {{docker.run}} build
109
+ {{else if (eq database 'mongoose')}}
110
+ RUN MONGODB_URI="mongodb://localhost:27017/build" {{docker.run}} build
111
+ {{else}}
112
+ RUN {{docker.run}} build
113
+ {{/if}}
114
+
115
+ # Production stage
116
+ FROM node:24-alpine
117
+ WORKDIR /app
118
+ ENV NODE_ENV=production
119
+ {{#if docker.setup}}
120
+ {{docker.setup}}
121
+ {{/if}}
122
+
123
+ # Create non-root user for security
124
+ RUN addgroup --system --gid 1001 nodejs && \
125
+ adduser --system --uid 1001 appuser
126
+
127
+ COPY {{docker.manifests}} ./
128
+ # Clear the download cache in the same layer so it does not ship in the image
129
+ RUN {{docker.installProduction}} \
130
+ && {{docker.cleanCache}}
131
+
132
+ # Copy the compiled output
133
+ COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
134
+
135
+ USER appuser
136
+ EXPOSE {{port}}
137
+ ENV PORT={{port}}
138
+
139
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
140
+ CMD wget -q -O /dev/null http://127.0.0.1:{{port}}/health || exit 1
141
+
142
+ {{#if (eq framework 'nestjs')}}
143
+ CMD ["node", "dist/main.js"]
144
+ {{else}}
145
+ CMD ["node", "dist/index.js"]
146
+ {{/if}}
147
+ {{/if}}
@@ -7,4 +7,99 @@ services:
7
7
  - .env
8
8
  environment:
9
9
  - NODE_ENV=production
10
- restart: unless-stopped
10
+ {{#if (eq database 'prisma')}}
11
+ # Inside Docker the database is the "db" service, not localhost
12
+ - DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
13
+ {{/if}}
14
+ {{#if (eq database 'drizzle')}}
15
+ # Inside Docker the database is the "db" service, not localhost
16
+ - DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
17
+ {{/if}}
18
+ {{#if (eq database 'mongoose')}}
19
+ # Inside Docker the database is the "db" service, not localhost
20
+ - MONGODB_URI=mongodb://db:27017/myapp?directConnection=true
21
+ {{/if}}
22
+ {{#unless (eq database 'none')}}
23
+ depends_on:
24
+ db:
25
+ condition: service_healthy
26
+ {{/unless}}
27
+ restart: unless-stopped
28
+ {{#if (eq database 'prisma')}}
29
+
30
+ # PostgreSQL for local development. The port is published so migrations and
31
+ # the app can also reach it from your machine at localhost:5432
32
+ db:
33
+ image: postgres:17-alpine
34
+ environment:
35
+ POSTGRES_USER: postgres
36
+ POSTGRES_PASSWORD: postgres
37
+ POSTGRES_DB: postgres
38
+ ports:
39
+ - "5432:5432"
40
+ volumes:
41
+ - db-data:/var/lib/postgresql/data
42
+ healthcheck:
43
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
44
+ interval: 5s
45
+ timeout: 5s
46
+ retries: 10
47
+ restart: unless-stopped
48
+
49
+ volumes:
50
+ db-data:
51
+ {{/if}}
52
+ {{#if (eq database 'drizzle')}}
53
+
54
+ # PostgreSQL for local development. The port is published so migrations and
55
+ # the app can also reach it from your machine at localhost:5432
56
+ db:
57
+ image: postgres:17-alpine
58
+ environment:
59
+ POSTGRES_USER: postgres
60
+ POSTGRES_PASSWORD: postgres
61
+ POSTGRES_DB: postgres
62
+ ports:
63
+ - "5432:5432"
64
+ volumes:
65
+ - db-data:/var/lib/postgresql/data
66
+ healthcheck:
67
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
68
+ interval: 5s
69
+ timeout: 5s
70
+ retries: 10
71
+ restart: unless-stopped
72
+
73
+ volumes:
74
+ db-data:
75
+ {{/if}}
76
+ {{#if (eq database 'mongoose')}}
77
+
78
+ # MongoDB for local development, run as a one-node replica set because
79
+ # transactions need one. The health check starts the replica set on first
80
+ # run. The port is published so the app can also reach it from your machine
81
+ # at localhost:27017
82
+ db:
83
+ image: mongo:8
84
+ command: ["--replSet", "rs0", "--bind_ip_all"]
85
+ ports:
86
+ - "27017:27017"
87
+ volumes:
88
+ - db-data:/data/db
89
+ healthcheck:
90
+ test:
91
+ [
92
+ "CMD",
93
+ "mongosh",
94
+ "--quiet",
95
+ "--eval",
96
+ "try { rs.status().ok } catch (e) { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] }).ok }",
97
+ ]
98
+ interval: 5s
99
+ timeout: 10s
100
+ retries: 20
101
+ restart: unless-stopped
102
+
103
+ volumes:
104
+ db-data:
105
+ {{/if}}
@@ -6,5 +6,7 @@ export const users = pgTable("users", {
6
6
  name: text("name"),
7
7
  password: text("password").notNull(),
8
8
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
9
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
9
+ updatedAt: timestamp("updated_at", { withTimezone: true })
10
+ .defaultNow()
11
+ .$onUpdate(() => new Date()),
10
12
  });
@@ -6,5 +6,7 @@ export const users = pgTable("users", {
6
6
  name: text("name"),
7
7
  password: text("password").notNull(),
8
8
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
9
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
9
+ updatedAt: timestamp("updated_at", { withTimezone: true })
10
+ .defaultNow()
11
+ .$onUpdate(() => new Date()),
10
12
  });
@@ -12,7 +12,8 @@ export const user = pgTable("user", {
12
12
  .defaultNow(),
13
13
  updatedAt: timestamp("updated_at", { withTimezone: true })
14
14
  .notNull()
15
- .defaultNow(),
15
+ .defaultNow()
16
+ .$onUpdate(() => new Date()),
16
17
  });
17
18
 
18
19
  export const session = pgTable("session", {
@@ -24,7 +25,8 @@ export const session = pgTable("session", {
24
25
  .defaultNow(),
25
26
  updatedAt: timestamp("updated_at", { withTimezone: true })
26
27
  .notNull()
27
- .defaultNow(),
28
+ .defaultNow()
29
+ .$onUpdate(() => new Date()),
28
30
  ipAddress: text("ip_address"),
29
31
  userAgent: text("user_agent"),
30
32
  userId: text("user_id")
@@ -55,7 +57,8 @@ export const account = pgTable("account", {
55
57
  .defaultNow(),
56
58
  updatedAt: timestamp("updated_at", { withTimezone: true })
57
59
  .notNull()
58
- .defaultNow(),
60
+ .defaultNow()
61
+ .$onUpdate(() => new Date()),
59
62
  }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
60
63
 
61
64
  export const verification = pgTable("verification", {
@@ -68,7 +71,8 @@ export const verification = pgTable("verification", {
68
71
  .defaultNow(),
69
72
  updatedAt: timestamp("updated_at", { withTimezone: true })
70
73
  .notNull()
71
- .defaultNow(),
74
+ .defaultNow()
75
+ .$onUpdate(() => new Date()),
72
76
  });
73
77
  {{else}}
74
78
  import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
@@ -79,6 +83,8 @@ export const users = pgTable("users", {
79
83
  name: text("name"),
80
84
  password: text("password").notNull(),
81
85
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
82
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
86
+ updatedAt: timestamp("updated_at", { withTimezone: true })
87
+ .defaultNow()
88
+ .$onUpdate(() => new Date()),
83
89
  });
84
90
  {{/if}}
@@ -1,84 +1,90 @@
1
- {{#if useAuth}}
2
- import { boolean, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
3
-
4
- export const user = pgTable("user", {
5
- id: text("id").primaryKey(),
6
- name: text("name").notNull(),
7
- email: text("email").notNull().unique(),
8
- emailVerified: boolean("email_verified").notNull().default(false),
9
- image: text("image"),
10
- createdAt: timestamp("created_at", { withTimezone: true })
11
- .notNull()
12
- .defaultNow(),
13
- updatedAt: timestamp("updated_at", { withTimezone: true })
14
- .notNull()
15
- .defaultNow(),
16
- });
17
-
18
- export const session = pgTable("session", {
19
- id: text("id").primaryKey(),
20
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
21
- token: text("token").notNull().unique(),
22
- createdAt: timestamp("created_at", { withTimezone: true })
23
- .notNull()
24
- .defaultNow(),
25
- updatedAt: timestamp("updated_at", { withTimezone: true })
26
- .notNull()
27
- .defaultNow(),
28
- ipAddress: text("ip_address"),
29
- userAgent: text("user_agent"),
30
- userId: text("user_id")
31
- .notNull()
32
- .references(() => user.id, { onDelete: "cascade" }),
33
- });
34
-
35
- export const account = pgTable("account", {
36
- id: text("id").primaryKey(),
37
- accountId: text("account_id").notNull(),
38
- providerId: text("provider_id").notNull(),
39
- userId: text("user_id")
40
- .notNull()
41
- .references(() => user.id, { onDelete: "cascade" }),
42
- accessToken: text("access_token"),
43
- refreshToken: text("refresh_token"),
44
- idToken: text("id_token"),
45
- accessTokenExpiresAt: timestamp("access_token_expires_at", {
46
- withTimezone: true,
47
- }),
48
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
49
- withTimezone: true,
50
- }),
51
- scope: text("scope"),
52
- password: text("password"),
53
- createdAt: timestamp("created_at", { withTimezone: true })
54
- .notNull()
55
- .defaultNow(),
56
- updatedAt: timestamp("updated_at", { withTimezone: true })
57
- .notNull()
58
- .defaultNow(),
59
- }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
60
-
61
- export const verification = pgTable("verification", {
62
- id: text("id").primaryKey(),
63
- identifier: text("identifier").notNull(),
64
- value: text("value").notNull(),
65
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
66
- createdAt: timestamp("created_at", { withTimezone: true })
67
- .notNull()
68
- .defaultNow(),
69
- updatedAt: timestamp("updated_at", { withTimezone: true })
70
- .notNull()
71
- .defaultNow(),
72
- });
73
- {{else}}
74
- import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
75
-
76
- export const users = pgTable("users", {
77
- id: uuid("id").primaryKey().defaultRandom(),
78
- email: text("email").notNull().unique(),
79
- name: text("name"),
80
- password: text("password").notNull(),
81
- createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
82
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
83
- });
84
- {{/if}}
1
+ {{#if useAuth}}
2
+ import { boolean, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
3
+
4
+ export const user = pgTable("user", {
5
+ id: text("id").primaryKey(),
6
+ name: text("name").notNull(),
7
+ email: text("email").notNull().unique(),
8
+ emailVerified: boolean("email_verified").notNull().default(false),
9
+ image: text("image"),
10
+ createdAt: timestamp("created_at", { withTimezone: true })
11
+ .notNull()
12
+ .defaultNow(),
13
+ updatedAt: timestamp("updated_at", { withTimezone: true })
14
+ .notNull()
15
+ .defaultNow()
16
+ .$onUpdate(() => new Date()),
17
+ });
18
+
19
+ export const session = pgTable("session", {
20
+ id: text("id").primaryKey(),
21
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
22
+ token: text("token").notNull().unique(),
23
+ createdAt: timestamp("created_at", { withTimezone: true })
24
+ .notNull()
25
+ .defaultNow(),
26
+ updatedAt: timestamp("updated_at", { withTimezone: true })
27
+ .notNull()
28
+ .defaultNow()
29
+ .$onUpdate(() => new Date()),
30
+ ipAddress: text("ip_address"),
31
+ userAgent: text("user_agent"),
32
+ userId: text("user_id")
33
+ .notNull()
34
+ .references(() => user.id, { onDelete: "cascade" }),
35
+ });
36
+
37
+ export const account = pgTable("account", {
38
+ id: text("id").primaryKey(),
39
+ accountId: text("account_id").notNull(),
40
+ providerId: text("provider_id").notNull(),
41
+ userId: text("user_id")
42
+ .notNull()
43
+ .references(() => user.id, { onDelete: "cascade" }),
44
+ accessToken: text("access_token"),
45
+ refreshToken: text("refresh_token"),
46
+ idToken: text("id_token"),
47
+ accessTokenExpiresAt: timestamp("access_token_expires_at", {
48
+ withTimezone: true,
49
+ }),
50
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
51
+ withTimezone: true,
52
+ }),
53
+ scope: text("scope"),
54
+ password: text("password"),
55
+ createdAt: timestamp("created_at", { withTimezone: true })
56
+ .notNull()
57
+ .defaultNow(),
58
+ updatedAt: timestamp("updated_at", { withTimezone: true })
59
+ .notNull()
60
+ .defaultNow()
61
+ .$onUpdate(() => new Date()),
62
+ }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
63
+
64
+ export const verification = pgTable("verification", {
65
+ id: text("id").primaryKey(),
66
+ identifier: text("identifier").notNull(),
67
+ value: text("value").notNull(),
68
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
69
+ createdAt: timestamp("created_at", { withTimezone: true })
70
+ .notNull()
71
+ .defaultNow(),
72
+ updatedAt: timestamp("updated_at", { withTimezone: true })
73
+ .notNull()
74
+ .defaultNow()
75
+ .$onUpdate(() => new Date()),
76
+ });
77
+ {{else}}
78
+ import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
79
+
80
+ export const users = pgTable("users", {
81
+ id: uuid("id").primaryKey().defaultRandom(),
82
+ email: text("email").notNull().unique(),
83
+ name: text("name"),
84
+ password: text("password").notNull(),
85
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
86
+ updatedAt: timestamp("updated_at", { withTimezone: true })
87
+ .defaultNow()
88
+ .$onUpdate(() => new Date()),
89
+ });
90
+ {{/if}}
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "rate-limit",
3
+ "name": "Rate limiting",
4
+ "description": "Per-IP request limits for Express with express-rate-limit",
5
+ "type": "feature",
6
+ "dependencies": {
7
+ "express-rate-limit": "^8.7.1"
8
+ },
9
+ "devDependencies": {},
10
+ "scripts": {},
11
+ "envVars": {}
12
+ }