@donkeylabs/cli 1.1.17 → 1.1.19
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/src/commands/generate.ts +154 -3
- package/templates/sveltekit-app/package.json +3 -3
- package/templates/sveltekit-app/src/lib/permissions.ts +15 -5
- package/templates/sveltekit-app/src/routes/+page.server.ts +1 -1
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +1 -1
- package/templates/sveltekit-app/src/server/index.ts +1 -1
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +377 -0
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +7 -7
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +65 -0
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +369 -0
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +24 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +10 -7
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +566 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +67 -0
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +3 -2
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +5 -8
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +4 -7
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +5 -8
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +6 -7
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +3 -5
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +9 -9
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +18 -18
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import type { Handler, Routes, AppContext } from "$server/api";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Refresh Handler - Get new access token using refresh token
|
|
5
3
|
* Only available with refresh-token strategy
|
|
6
4
|
*/
|
|
7
|
-
export class RefreshHandler
|
|
8
|
-
constructor(private ctx:
|
|
5
|
+
export class RefreshHandler {
|
|
6
|
+
constructor(private ctx: any) {}
|
|
9
7
|
|
|
10
|
-
async handle(input:
|
|
11
|
-
const tokens = await this.ctx.plugins.auth.refresh(input.refreshToken);
|
|
8
|
+
async handle(input: { refreshToken: string }) {
|
|
9
|
+
const tokens = await (this.ctx.plugins as any).auth.refresh(input.refreshToken);
|
|
12
10
|
|
|
13
11
|
return {
|
|
14
12
|
accessToken: tokens.accessToken,
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import type { Handler, Routes, AppContext } from "$server/api";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Register Handler - Create a new user account
|
|
5
3
|
*/
|
|
6
|
-
export class RegisterHandler
|
|
7
|
-
constructor(private ctx:
|
|
4
|
+
export class RegisterHandler {
|
|
5
|
+
constructor(private ctx: any) {}
|
|
8
6
|
|
|
9
|
-
async handle(input:
|
|
10
|
-
const result = await this.ctx.plugins.auth.register({
|
|
7
|
+
async handle(input: { email: string; password: string; name: string }) {
|
|
8
|
+
const result = await (this.ctx.plugins as any).auth.register({
|
|
11
9
|
email: input.email,
|
|
12
10
|
password: input.password,
|
|
13
11
|
name: input.name,
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import type { Handler, Routes, AppContext } from "$server/api";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Update Profile Handler - Update current user's profile
|
|
5
3
|
*/
|
|
6
|
-
export class UpdateProfileHandler
|
|
7
|
-
constructor(private ctx:
|
|
4
|
+
export class UpdateProfileHandler {
|
|
5
|
+
constructor(private ctx: any) {}
|
|
8
6
|
|
|
9
|
-
async handle(input:
|
|
10
|
-
|
|
11
|
-
const user = (this.ctx as any).user;
|
|
7
|
+
async handle(input: { name?: string; email?: string }) {
|
|
8
|
+
const user = this.ctx.user;
|
|
12
9
|
|
|
13
10
|
if (!user) {
|
|
14
11
|
throw this.ctx.errors.Unauthorized("Not authenticated");
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
const updated = await this.ctx.plugins.auth.updateProfile(user.id, {
|
|
14
|
+
const updated = await (this.ctx.plugins as any).auth.updateProfile(user.id, {
|
|
18
15
|
name: input.name,
|
|
19
16
|
email: input.email,
|
|
20
17
|
});
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* - auth.register - Create new account
|
|
6
6
|
* - auth.login - Login and get tokens
|
|
7
7
|
* - auth.refresh - Refresh access token (refresh-token strategy only)
|
|
8
|
-
* - auth.logout - Invalidate session/token
|
|
9
|
-
* - auth.me - Get current user
|
|
10
|
-
* - auth.updateProfile - Update profile
|
|
8
|
+
* - auth.logout - Invalidate session/token
|
|
9
|
+
* - auth.me - Get current user
|
|
10
|
+
* - auth.updateProfile - Update profile
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { createRouter } from "@donkeylabs/server";
|
|
@@ -51,22 +51,21 @@ export const authRouter = createRouter("auth")
|
|
|
51
51
|
handle: RefreshHandler,
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
.middleware.auth({ required: false })
|
|
54
|
+
// Get current user (returns null if not authenticated)
|
|
56
55
|
.route("me").typed({
|
|
57
56
|
input: z.object({}),
|
|
58
57
|
output: userSchema.nullable(),
|
|
59
58
|
handle: MeHandler,
|
|
60
59
|
})
|
|
61
60
|
|
|
62
|
-
//
|
|
63
|
-
.middleware.auth({ required: true })
|
|
61
|
+
// Logout (invalidate session/token)
|
|
64
62
|
.route("logout").typed({
|
|
65
63
|
input: z.object({}),
|
|
66
64
|
output: logoutResponseSchema,
|
|
67
65
|
handle: LogoutHandler,
|
|
68
66
|
})
|
|
69
67
|
|
|
68
|
+
// Update profile
|
|
70
69
|
.route("updateProfile").typed({
|
|
71
70
|
input: updateProfileSchema,
|
|
72
71
|
output: userSchema,
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import type { Handler, Routes, AppContext } from "$server/api";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Greet Handler
|
|
5
3
|
*
|
|
6
4
|
* Example handler demonstrating the feature module pattern.
|
|
7
5
|
* Business logic lives directly in the handler - no separate model layer needed.
|
|
8
6
|
*/
|
|
9
|
-
export class GreetHandler
|
|
10
|
-
constructor(private ctx:
|
|
7
|
+
export class GreetHandler {
|
|
8
|
+
constructor(private ctx: any) {}
|
|
11
9
|
|
|
12
|
-
handle(input:
|
|
10
|
+
handle(input: { name: string; formal?: boolean }) {
|
|
13
11
|
// Business logic directly in handler
|
|
14
12
|
const greeting = input.formal
|
|
15
13
|
? `Good day, ${input.name}. How may I assist you?`
|
|
@@ -31,7 +31,7 @@ permissions.route("context").typed(
|
|
|
31
31
|
throw ctx.errors.Unauthorized("Authentication required");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
return ctx.plugins.permissions.getClientContext(ctx.user.id, input.tenantId);
|
|
34
|
+
return (ctx.plugins as any).permissions.getClientContext(ctx.user.id, input.tenantId);
|
|
35
35
|
},
|
|
36
36
|
})
|
|
37
37
|
);
|
|
@@ -55,7 +55,7 @@ permissions.route("check").typed(
|
|
|
55
55
|
const results: Record<string, boolean> = {};
|
|
56
56
|
|
|
57
57
|
for (const permission of input.permissions) {
|
|
58
|
-
results[permission] = await ctx.plugins.permissions.hasPermission(
|
|
58
|
+
results[permission] = await (ctx.plugins as any).permissions.hasPermission(
|
|
59
59
|
ctx.user.id,
|
|
60
60
|
input.tenantId,
|
|
61
61
|
permission
|
|
@@ -92,7 +92,7 @@ permissions.route("canAccess").typed(
|
|
|
92
92
|
|
|
93
93
|
for (const check of input.checks) {
|
|
94
94
|
const key = `${check.resourceType}:${check.resourceId}:${check.action}`;
|
|
95
|
-
results[key] = await ctx.plugins.permissions.canAccess(
|
|
95
|
+
results[key] = await (ctx.plugins as any).permissions.canAccess(
|
|
96
96
|
ctx.user.id,
|
|
97
97
|
input.tenantId,
|
|
98
98
|
check.resourceType,
|
|
@@ -131,7 +131,7 @@ permissions.route("grants").typed(
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// Check if user can admin this resource (to see grants)
|
|
134
|
-
const canAdmin = await ctx.plugins.permissions.canAccess(
|
|
134
|
+
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
135
135
|
ctx.user.id,
|
|
136
136
|
input.tenantId,
|
|
137
137
|
input.resourceType,
|
|
@@ -143,7 +143,7 @@ permissions.route("grants").typed(
|
|
|
143
143
|
throw ctx.errors.Forbidden("Cannot view grants for this resource");
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
return ctx.plugins.permissions.getResourceGrants(
|
|
146
|
+
return (ctx.plugins as any).permissions.getResourceGrants(
|
|
147
147
|
input.tenantId,
|
|
148
148
|
input.resourceType,
|
|
149
149
|
input.resourceId
|
|
@@ -172,7 +172,7 @@ permissions.route("grant").typed(
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
// Check if user can admin this resource
|
|
175
|
-
const canAdmin = await ctx.plugins.permissions.canAccess(
|
|
175
|
+
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
176
176
|
ctx.user.id,
|
|
177
177
|
input.tenantId,
|
|
178
178
|
input.resourceType,
|
|
@@ -184,7 +184,7 @@ permissions.route("grant").typed(
|
|
|
184
184
|
throw ctx.errors.Forbidden("Cannot grant access to this resource");
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
await ctx.plugins.permissions.grantAccess({
|
|
187
|
+
await (ctx.plugins as any).permissions.grantAccess({
|
|
188
188
|
tenantId: input.tenantId,
|
|
189
189
|
resourceType: input.resourceType,
|
|
190
190
|
resourceId: input.resourceId,
|
|
@@ -219,7 +219,7 @@ permissions.route("revoke").typed(
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
// Check if user can admin this resource
|
|
222
|
-
const canAdmin = await ctx.plugins.permissions.canAccess(
|
|
222
|
+
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
223
223
|
ctx.user.id,
|
|
224
224
|
input.tenantId,
|
|
225
225
|
input.resourceType,
|
|
@@ -231,7 +231,7 @@ permissions.route("revoke").typed(
|
|
|
231
231
|
throw ctx.errors.Forbidden("Cannot revoke access to this resource");
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
await ctx.plugins.permissions.revokeAccess({
|
|
234
|
+
await (ctx.plugins as any).permissions.revokeAccess({
|
|
235
235
|
tenantId: input.tenantId,
|
|
236
236
|
resourceType: input.resourceType,
|
|
237
237
|
resourceId: input.resourceId,
|
|
@@ -24,7 +24,7 @@ tenants.route("mine").typed(
|
|
|
24
24
|
throw ctx.errors.Unauthorized("Authentication required");
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
return ctx.plugins.permissions.getUserTenants(ctx.user.id);
|
|
27
|
+
return (ctx.plugins as any).permissions.getUserTenants(ctx.user.id);
|
|
28
28
|
},
|
|
29
29
|
})
|
|
30
30
|
);
|
|
@@ -51,12 +51,12 @@ tenants.route("create").typed(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Check if slug is taken
|
|
54
|
-
const existing = await ctx.plugins.permissions.getTenantBySlug(input.slug);
|
|
54
|
+
const existing = await (ctx.plugins as any).permissions.getTenantBySlug(input.slug);
|
|
55
55
|
if (existing) {
|
|
56
56
|
throw ctx.errors.BadRequest("Tenant slug already taken");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
return ctx.plugins.permissions.createTenant({
|
|
59
|
+
return (ctx.plugins as any).permissions.createTenant({
|
|
60
60
|
name: input.name,
|
|
61
61
|
slug: input.slug,
|
|
62
62
|
ownerId: ctx.user.id,
|
|
@@ -86,12 +86,12 @@ tenants.route("get").typed(
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Verify membership
|
|
89
|
-
const isMember = await ctx.plugins.permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
89
|
+
const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
90
90
|
if (!isMember) {
|
|
91
91
|
throw ctx.errors.Forbidden("Not a member of this tenant");
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
return ctx.plugins.permissions.getTenant(input.tenantId);
|
|
94
|
+
return (ctx.plugins as any).permissions.getTenant(input.tenantId);
|
|
95
95
|
},
|
|
96
96
|
})
|
|
97
97
|
);
|
|
@@ -118,13 +118,13 @@ tenants.route("roles").typed(
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// Verify membership
|
|
121
|
-
const isMember = await ctx.plugins.permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
121
|
+
const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
122
122
|
if (!isMember) {
|
|
123
123
|
throw ctx.errors.Forbidden("Not a member of this tenant");
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const roles = await ctx.plugins.permissions.getTenantRoles(input.tenantId);
|
|
127
|
-
return roles.map(r => ({
|
|
126
|
+
const roles = await (ctx.plugins as any).permissions.getTenantRoles(input.tenantId);
|
|
127
|
+
return roles.map((r: any) => ({
|
|
128
128
|
id: r.id,
|
|
129
129
|
name: r.name,
|
|
130
130
|
description: r.description,
|
|
@@ -163,7 +163,7 @@ tenants.route("createRole").typed(
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// Check admin permission
|
|
166
|
-
const hasAdmin = await ctx.plugins.permissions.hasPermission(
|
|
166
|
+
const hasAdmin = await (ctx.plugins as any).permissions.hasPermission(
|
|
167
167
|
ctx.user.id,
|
|
168
168
|
input.tenantId,
|
|
169
169
|
"roles.manage"
|
|
@@ -172,7 +172,7 @@ tenants.route("createRole").typed(
|
|
|
172
172
|
throw ctx.errors.Forbidden("Cannot manage roles");
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
const role = await ctx.plugins.permissions.createRole({
|
|
175
|
+
const role = await (ctx.plugins as any).permissions.createRole({
|
|
176
176
|
tenantId: input.tenantId,
|
|
177
177
|
name: input.name,
|
|
178
178
|
description: input.description,
|
|
@@ -209,7 +209,7 @@ tenants.route("addMember").typed(
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// Check permission
|
|
212
|
-
const canInvite = await ctx.plugins.permissions.hasPermission(
|
|
212
|
+
const canInvite = await (ctx.plugins as any).permissions.hasPermission(
|
|
213
213
|
ctx.user.id,
|
|
214
214
|
input.tenantId,
|
|
215
215
|
"members.invite"
|
|
@@ -218,7 +218,7 @@ tenants.route("addMember").typed(
|
|
|
218
218
|
throw ctx.errors.Forbidden("Cannot invite members");
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
await ctx.plugins.permissions.addTenantMember(
|
|
221
|
+
await (ctx.plugins as any).permissions.addTenantMember(
|
|
222
222
|
input.tenantId,
|
|
223
223
|
input.userId,
|
|
224
224
|
ctx.user.id
|
|
@@ -245,7 +245,7 @@ tenants.route("removeMember").typed(
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
// Check permission
|
|
248
|
-
const canRemove = await ctx.plugins.permissions.hasPermission(
|
|
248
|
+
const canRemove = await (ctx.plugins as any).permissions.hasPermission(
|
|
249
249
|
ctx.user.id,
|
|
250
250
|
input.tenantId,
|
|
251
251
|
"members.remove"
|
|
@@ -254,7 +254,7 @@ tenants.route("removeMember").typed(
|
|
|
254
254
|
throw ctx.errors.Forbidden("Cannot remove members");
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
await ctx.plugins.permissions.removeTenantMember(input.tenantId, input.userId);
|
|
257
|
+
await (ctx.plugins as any).permissions.removeTenantMember(input.tenantId, input.userId);
|
|
258
258
|
|
|
259
259
|
return { success: true };
|
|
260
260
|
},
|
|
@@ -278,7 +278,7 @@ tenants.route("assignRole").typed(
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
// Check permission
|
|
281
|
-
const canManage = await ctx.plugins.permissions.hasPermission(
|
|
281
|
+
const canManage = await (ctx.plugins as any).permissions.hasPermission(
|
|
282
282
|
ctx.user.id,
|
|
283
283
|
input.tenantId,
|
|
284
284
|
"roles.assign"
|
|
@@ -287,7 +287,7 @@ tenants.route("assignRole").typed(
|
|
|
287
287
|
throw ctx.errors.Forbidden("Cannot assign roles");
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
await ctx.plugins.permissions.assignRole(
|
|
290
|
+
await (ctx.plugins as any).permissions.assignRole(
|
|
291
291
|
input.userId,
|
|
292
292
|
input.roleId,
|
|
293
293
|
input.tenantId,
|
|
@@ -316,7 +316,7 @@ tenants.route("revokeRole").typed(
|
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
// Check permission
|
|
319
|
-
const canManage = await ctx.plugins.permissions.hasPermission(
|
|
319
|
+
const canManage = await (ctx.plugins as any).permissions.hasPermission(
|
|
320
320
|
ctx.user.id,
|
|
321
321
|
input.tenantId,
|
|
322
322
|
"roles.assign"
|
|
@@ -325,7 +325,7 @@ tenants.route("revokeRole").typed(
|
|
|
325
325
|
throw ctx.errors.Forbidden("Cannot revoke roles");
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
await ctx.plugins.permissions.revokeRole(
|
|
328
|
+
await (ctx.plugins as any).permissions.revokeRole(
|
|
329
329
|
input.userId,
|
|
330
330
|
input.roleId,
|
|
331
331
|
input.tenantId
|