create-m5kdev 0.28.7 → 0.28.9
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/package.json +1 -1
- package/templates/minimal-app/CLAUDE.md.tpl +131 -0
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +26 -26
- package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +36 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +1889 -1889
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +12 -12
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +40 -33
- package/templates/minimal-app/apps/server/drizzle/seed.e2e.ts.tpl +246 -244
- package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +183 -181
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +16 -14
|
@@ -1,181 +1,183 @@
|
|
|
1
|
-
import { hashPassword } from "better-auth/crypto";
|
|
2
|
-
import { eq } from "drizzle-orm";
|
|
3
|
-
import { v4 as uuidv4 } from "uuid";
|
|
4
|
-
import { isRemote, orm, schema } from "./db";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
.
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
.
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
.
|
|
90
|
-
.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
.
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
await
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
1
|
+
import { hashPassword } from "better-auth/crypto";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { isRemote, orm, schema } from "./db";
|
|
5
|
+
import { ensureDevServerStopped } from "./guard";
|
|
6
|
+
|
|
7
|
+
const DEMO_EMAIL = "admin{{PACKAGE_SCOPE}}.local";
|
|
8
|
+
const DEMO_PASSWORD = "password1234";
|
|
9
|
+
const ORGANIZATION_ID = "{{APP_SLUG}}-org";
|
|
10
|
+
const TEAM_ID = "{{APP_SLUG}}-team";
|
|
11
|
+
|
|
12
|
+
async function ensureDemoUser() {
|
|
13
|
+
const [existingUser] = await orm
|
|
14
|
+
.select()
|
|
15
|
+
.from(schema.users)
|
|
16
|
+
.where(eq(schema.users.email, DEMO_EMAIL))
|
|
17
|
+
.limit(1);
|
|
18
|
+
|
|
19
|
+
if (existingUser) {
|
|
20
|
+
return existingUser;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const userId = uuidv4();
|
|
24
|
+
const [user] = await orm
|
|
25
|
+
.insert(schema.users)
|
|
26
|
+
.values({
|
|
27
|
+
id: userId,
|
|
28
|
+
name: "Demo Editor",
|
|
29
|
+
email: DEMO_EMAIL,
|
|
30
|
+
emailVerified: true,
|
|
31
|
+
role: "admin",
|
|
32
|
+
createdAt: new Date(),
|
|
33
|
+
updatedAt: new Date(),
|
|
34
|
+
})
|
|
35
|
+
.returning();
|
|
36
|
+
|
|
37
|
+
if (!user) {
|
|
38
|
+
throw new Error("Failed to create demo user.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await orm.insert(schema.accounts).values({
|
|
42
|
+
id: uuidv4(),
|
|
43
|
+
accountId: user.id,
|
|
44
|
+
providerId: "credential",
|
|
45
|
+
userId: user.id,
|
|
46
|
+
password: await hashPassword(DEMO_PASSWORD),
|
|
47
|
+
createdAt: new Date(),
|
|
48
|
+
updatedAt: new Date(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return user;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function ensureOrganization(userId: string) {
|
|
55
|
+
const [existingOrganization] = await orm
|
|
56
|
+
.select()
|
|
57
|
+
.from(schema.organizations)
|
|
58
|
+
.where(eq(schema.organizations.id, ORGANIZATION_ID))
|
|
59
|
+
.limit(1);
|
|
60
|
+
|
|
61
|
+
if (!existingOrganization) {
|
|
62
|
+
await orm.insert(schema.organizations).values({
|
|
63
|
+
id: ORGANIZATION_ID,
|
|
64
|
+
name: "{{APP_NAME}}",
|
|
65
|
+
slug: "{{APP_SLUG}}",
|
|
66
|
+
type: "enterprise",
|
|
67
|
+
createdAt: new Date(),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const [existingMember] = await orm
|
|
72
|
+
.select()
|
|
73
|
+
.from(schema.members)
|
|
74
|
+
.where(eq(schema.members.userId, userId))
|
|
75
|
+
.limit(1);
|
|
76
|
+
|
|
77
|
+
if (!existingMember) {
|
|
78
|
+
await orm.insert(schema.members).values({
|
|
79
|
+
id: uuidv4(),
|
|
80
|
+
organizationId: ORGANIZATION_ID,
|
|
81
|
+
userId,
|
|
82
|
+
role: "owner",
|
|
83
|
+
createdAt: new Date(),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const [existingTeam] = await orm
|
|
88
|
+
.select()
|
|
89
|
+
.from(schema.teams)
|
|
90
|
+
.where(eq(schema.teams.id, TEAM_ID))
|
|
91
|
+
.limit(1);
|
|
92
|
+
|
|
93
|
+
if (!existingTeam) {
|
|
94
|
+
await orm.insert(schema.teams).values({
|
|
95
|
+
id: TEAM_ID,
|
|
96
|
+
name: "Editorial",
|
|
97
|
+
organizationId: ORGANIZATION_ID,
|
|
98
|
+
createdAt: new Date(),
|
|
99
|
+
updatedAt: new Date(),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const [existingTeamMember] = await orm
|
|
104
|
+
.select()
|
|
105
|
+
.from(schema.teamMembers)
|
|
106
|
+
.where(eq(schema.teamMembers.userId, userId))
|
|
107
|
+
.limit(1);
|
|
108
|
+
|
|
109
|
+
if (!existingTeamMember) {
|
|
110
|
+
await orm.insert(schema.teamMembers).values({
|
|
111
|
+
id: uuidv4(),
|
|
112
|
+
teamId: TEAM_ID,
|
|
113
|
+
userId,
|
|
114
|
+
role: "owner",
|
|
115
|
+
createdAt: new Date(),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { organizationId: ORGANIZATION_ID, teamId: TEAM_ID };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function seedPosts(userId: string, organizationId: string | null, teamId: string | null) {
|
|
123
|
+
const existingPosts = await orm.select().from(schema.posts).limit(1);
|
|
124
|
+
if (existingPosts.length > 0) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await orm.insert(schema.posts).values([
|
|
129
|
+
{
|
|
130
|
+
authorUserId: userId,
|
|
131
|
+
organizationId,
|
|
132
|
+
teamId,
|
|
133
|
+
title: "An editorial workflow you can ship in an afternoon",
|
|
134
|
+
slug: "editorial-workflow-in-an-afternoon",
|
|
135
|
+
excerpt: "A first draft on how this minimal starter is wired together.",
|
|
136
|
+
content:
|
|
137
|
+
"This starter keeps the moving parts visible. Shared contracts live in the shared app, business rules stay in services, and the web layer only does composition and UI work.",
|
|
138
|
+
status: "published",
|
|
139
|
+
publishedAt: new Date(),
|
|
140
|
+
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5),
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
authorUserId: userId,
|
|
144
|
+
organizationId,
|
|
145
|
+
teamId,
|
|
146
|
+
title: "Three habits that keep CRUD apps from drifting into chaos",
|
|
147
|
+
slug: "three-habits-that-keep-crud{{PACKAGE_SCOPE}}s-focused",
|
|
148
|
+
excerpt: "A draft about explicit composition, typed contracts, and URL state.",
|
|
149
|
+
content:
|
|
150
|
+
"Keep your composition explicit, keep your schemas honest, and keep the URL involved in user intent. Those three choices do most of the architectural work in a small product.",
|
|
151
|
+
status: "draft",
|
|
152
|
+
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
authorUserId: userId,
|
|
156
|
+
organizationId,
|
|
157
|
+
teamId,
|
|
158
|
+
title: "What a minimal platform starter should still refuse to compromise on",
|
|
159
|
+
slug: "minimal-platform-starter-non-negotiables",
|
|
160
|
+
excerpt: "A published note on keeping the foundation opinionated without being heavy.",
|
|
161
|
+
content:
|
|
162
|
+
"Even a minimal starter should include auth, structured modules, and a coherent app shell. Cutting those corners only moves the complexity into the first week of work.",
|
|
163
|
+
status: "published",
|
|
164
|
+
publishedAt: new Date(Date.now() - 1000 * 60 * 60 * 16),
|
|
165
|
+
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 18),
|
|
166
|
+
},
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function seed() {
|
|
171
|
+
await ensureDevServerStopped();
|
|
172
|
+
if (isRemote) {
|
|
173
|
+
await orm.$client.sync();
|
|
174
|
+
}
|
|
175
|
+
const user = await ensureDemoUser();
|
|
176
|
+
const { organizationId, teamId } = await ensureOrganization(user.id);
|
|
177
|
+
|
|
178
|
+
await seedPosts(user.id, organizationId, teamId);
|
|
179
|
+
|
|
180
|
+
console.info(`Seed completed. Demo login: ${DEMO_EMAIL} / ${DEMO_PASSWORD}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
void seed();
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { isRemote, orm } from "./db";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { isRemote, orm } from "./db";
|
|
2
|
+
import { ensureDevServerStopped } from "./guard";
|
|
3
|
+
|
|
4
|
+
async function sync() {
|
|
5
|
+
await ensureDevServerStopped();
|
|
6
|
+
if (isRemote) {
|
|
7
|
+
await orm.$client.sync();
|
|
8
|
+
} else {
|
|
9
|
+
console.info("Local database no sync required");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
sync().then(() => {
|
|
14
|
+
console.info("Sync completed");
|
|
15
|
+
process.exit(0);
|
|
16
|
+
});
|