@donkeylabs/cli 2.0.14 → 2.0.16
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/config.ts +610 -0
- package/src/commands/deploy-enhanced.ts +354 -0
- package/src/commands/deploy.ts +204 -0
- package/src/commands/generate.ts +11 -13
- package/src/commands/init-enhanced.ts +1994 -0
- package/src/deployment/manager.ts +356 -0
- package/src/index.ts +47 -19
- package/templates/starter/.env.example +0 -44
- package/templates/starter/.gitignore.template +0 -4
- package/templates/starter/donkeylabs.config.ts +0 -6
- package/templates/starter/package.json +0 -21
- package/templates/starter/src/index.ts +0 -54
- package/templates/starter/src/plugins/stats/index.ts +0 -105
- package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
- package/templates/starter/src/routes/health/index.ts +0 -19
- package/templates/starter/tsconfig.json +0 -27
- package/templates/sveltekit-app/.env.example +0 -59
- package/templates/sveltekit-app/README.md +0 -103
- package/templates/sveltekit-app/bun.lock +0 -683
- package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
- package/templates/sveltekit-app/package.json +0 -38
- package/templates/sveltekit-app/src/app.css +0 -40
- package/templates/sveltekit-app/src/app.html +0 -12
- package/templates/sveltekit-app/src/hooks.server.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
- package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
- package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
- package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
- package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
- package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
- package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
- package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
- package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
- package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
- package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
- package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
- package/templates/sveltekit-app/src/server/events.ts +0 -28
- package/templates/sveltekit-app/src/server/index.ts +0 -124
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
- package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
- package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
- package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
- package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
- package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
- package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
- package/templates/sveltekit-app/static/robots.txt +0 -3
- package/templates/sveltekit-app/svelte.config.ts +0 -17
- package/templates/sveltekit-app/tsconfig.json +0 -20
- package/templates/sveltekit-app/vite.config.ts +0 -12
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Permissions Routes - Client-side permission checking
|
|
3
|
-
*
|
|
4
|
-
* These routes allow the frontend to check permissions for UI locking.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
|
|
10
|
-
const permissions = createRouter("permissions");
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get current user's permission context for a tenant
|
|
14
|
-
* Returns roles and all static permissions
|
|
15
|
-
*/
|
|
16
|
-
permissions.route("context").typed(
|
|
17
|
-
defineRoute({
|
|
18
|
-
input: z.object({
|
|
19
|
-
tenantId: z.string(),
|
|
20
|
-
}),
|
|
21
|
-
output: z.object({
|
|
22
|
-
tenantId: z.string(),
|
|
23
|
-
roles: z.array(z.object({
|
|
24
|
-
id: z.string(),
|
|
25
|
-
name: z.string(),
|
|
26
|
-
})),
|
|
27
|
-
permissions: z.array(z.string()),
|
|
28
|
-
}),
|
|
29
|
-
handle: async (input, ctx) => {
|
|
30
|
-
if (!ctx.user?.id) {
|
|
31
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return (ctx.plugins as any).permissions.getClientContext(ctx.user.id, input.tenantId);
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Check if user has specific static permissions
|
|
41
|
-
* Returns a map of permission -> boolean
|
|
42
|
-
*/
|
|
43
|
-
permissions.route("check").typed(
|
|
44
|
-
defineRoute({
|
|
45
|
-
input: z.object({
|
|
46
|
-
tenantId: z.string(),
|
|
47
|
-
permissions: z.array(z.string()),
|
|
48
|
-
}),
|
|
49
|
-
output: z.record(z.string(), z.boolean()),
|
|
50
|
-
handle: async (input, ctx) => {
|
|
51
|
-
if (!ctx.user?.id) {
|
|
52
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const results: Record<string, boolean> = {};
|
|
56
|
-
|
|
57
|
-
for (const permission of input.permissions) {
|
|
58
|
-
results[permission] = await (ctx.plugins as any).permissions.hasPermission(
|
|
59
|
-
ctx.user.id,
|
|
60
|
-
input.tenantId,
|
|
61
|
-
permission
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return results;
|
|
66
|
-
},
|
|
67
|
-
})
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Check if user can access specific resources
|
|
72
|
-
* Returns a map of "resourceType:resourceId:action" -> boolean
|
|
73
|
-
*/
|
|
74
|
-
permissions.route("canAccess").typed(
|
|
75
|
-
defineRoute({
|
|
76
|
-
input: z.object({
|
|
77
|
-
tenantId: z.string(),
|
|
78
|
-
checks: z.array(z.object({
|
|
79
|
-
resourceType: z.string(),
|
|
80
|
-
resourceId: z.string(),
|
|
81
|
-
action: z.enum(["create", "read", "write", "delete", "admin"]),
|
|
82
|
-
ownerId: z.string().optional(), // For owner check
|
|
83
|
-
})),
|
|
84
|
-
}),
|
|
85
|
-
output: z.record(z.string(), z.boolean()),
|
|
86
|
-
handle: async (input, ctx) => {
|
|
87
|
-
if (!ctx.user?.id) {
|
|
88
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const results: Record<string, boolean> = {};
|
|
92
|
-
|
|
93
|
-
for (const check of input.checks) {
|
|
94
|
-
const key = `${check.resourceType}:${check.resourceId}:${check.action}`;
|
|
95
|
-
results[key] = await (ctx.plugins as any).permissions.canAccess(
|
|
96
|
-
ctx.user.id,
|
|
97
|
-
input.tenantId,
|
|
98
|
-
check.resourceType,
|
|
99
|
-
check.resourceId,
|
|
100
|
-
check.action,
|
|
101
|
-
check.ownerId
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return results;
|
|
106
|
-
},
|
|
107
|
-
})
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get all grants for a specific resource
|
|
112
|
-
* Useful for showing "shared with" UI
|
|
113
|
-
*/
|
|
114
|
-
permissions.route("grants").typed(
|
|
115
|
-
defineRoute({
|
|
116
|
-
input: z.object({
|
|
117
|
-
tenantId: z.string(),
|
|
118
|
-
resourceType: z.string(),
|
|
119
|
-
resourceId: z.string(),
|
|
120
|
-
}),
|
|
121
|
-
output: z.array(z.object({
|
|
122
|
-
resourceType: z.string(),
|
|
123
|
-
resourceId: z.string(),
|
|
124
|
-
granteeType: z.enum(["user", "role"]),
|
|
125
|
-
granteeId: z.string(),
|
|
126
|
-
permissions: z.array(z.enum(["create", "read", "write", "delete", "admin"])),
|
|
127
|
-
})),
|
|
128
|
-
handle: async (input, ctx) => {
|
|
129
|
-
if (!ctx.user?.id) {
|
|
130
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Check if user can admin this resource (to see grants)
|
|
134
|
-
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
135
|
-
ctx.user.id,
|
|
136
|
-
input.tenantId,
|
|
137
|
-
input.resourceType,
|
|
138
|
-
input.resourceId,
|
|
139
|
-
"admin"
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (!canAdmin) {
|
|
143
|
-
throw ctx.errors.Forbidden("Cannot view grants for this resource");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return (ctx.plugins as any).permissions.getResourceGrants(
|
|
147
|
-
input.tenantId,
|
|
148
|
-
input.resourceType,
|
|
149
|
-
input.resourceId
|
|
150
|
-
);
|
|
151
|
-
},
|
|
152
|
-
})
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Grant access to a resource
|
|
157
|
-
*/
|
|
158
|
-
permissions.route("grant").typed(
|
|
159
|
-
defineRoute({
|
|
160
|
-
input: z.object({
|
|
161
|
-
tenantId: z.string(),
|
|
162
|
-
resourceType: z.string(),
|
|
163
|
-
resourceId: z.string(),
|
|
164
|
-
granteeType: z.enum(["user", "role"]),
|
|
165
|
-
granteeId: z.string(),
|
|
166
|
-
permissions: z.array(z.enum(["create", "read", "write", "delete", "admin"])),
|
|
167
|
-
}),
|
|
168
|
-
output: z.object({ success: z.boolean() }),
|
|
169
|
-
handle: async (input, ctx) => {
|
|
170
|
-
if (!ctx.user?.id) {
|
|
171
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Check if user can admin this resource
|
|
175
|
-
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
176
|
-
ctx.user.id,
|
|
177
|
-
input.tenantId,
|
|
178
|
-
input.resourceType,
|
|
179
|
-
input.resourceId,
|
|
180
|
-
"admin"
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
if (!canAdmin) {
|
|
184
|
-
throw ctx.errors.Forbidden("Cannot grant access to this resource");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
await (ctx.plugins as any).permissions.grantAccess({
|
|
188
|
-
tenantId: input.tenantId,
|
|
189
|
-
resourceType: input.resourceType,
|
|
190
|
-
resourceId: input.resourceId,
|
|
191
|
-
granteeType: input.granteeType,
|
|
192
|
-
granteeId: input.granteeId,
|
|
193
|
-
permissions: input.permissions,
|
|
194
|
-
grantedBy: ctx.user.id,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return { success: true };
|
|
198
|
-
},
|
|
199
|
-
})
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Revoke access to a resource
|
|
204
|
-
*/
|
|
205
|
-
permissions.route("revoke").typed(
|
|
206
|
-
defineRoute({
|
|
207
|
-
input: z.object({
|
|
208
|
-
tenantId: z.string(),
|
|
209
|
-
resourceType: z.string(),
|
|
210
|
-
resourceId: z.string(),
|
|
211
|
-
granteeType: z.enum(["user", "role"]),
|
|
212
|
-
granteeId: z.string(),
|
|
213
|
-
permissions: z.array(z.enum(["create", "read", "write", "delete", "admin"])).optional(),
|
|
214
|
-
}),
|
|
215
|
-
output: z.object({ success: z.boolean() }),
|
|
216
|
-
handle: async (input, ctx) => {
|
|
217
|
-
if (!ctx.user?.id) {
|
|
218
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Check if user can admin this resource
|
|
222
|
-
const canAdmin = await (ctx.plugins as any).permissions.canAccess(
|
|
223
|
-
ctx.user.id,
|
|
224
|
-
input.tenantId,
|
|
225
|
-
input.resourceType,
|
|
226
|
-
input.resourceId,
|
|
227
|
-
"admin"
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
if (!canAdmin) {
|
|
231
|
-
throw ctx.errors.Forbidden("Cannot revoke access to this resource");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
await (ctx.plugins as any).permissions.revokeAccess({
|
|
235
|
-
tenantId: input.tenantId,
|
|
236
|
-
resourceType: input.resourceType,
|
|
237
|
-
resourceId: input.resourceId,
|
|
238
|
-
granteeType: input.granteeType,
|
|
239
|
-
granteeId: input.granteeId,
|
|
240
|
-
permissions: input.permissions,
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
return { success: true };
|
|
244
|
-
},
|
|
245
|
-
})
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
export { permissions as permissionsRouter };
|
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tenants Routes - Multi-tenant management
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
|
|
8
|
-
const tenants = createRouter("tenants");
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get all tenants the current user is a member of
|
|
12
|
-
*/
|
|
13
|
-
tenants.route("mine").typed(
|
|
14
|
-
defineRoute({
|
|
15
|
-
input: z.object({}),
|
|
16
|
-
output: z.array(z.object({
|
|
17
|
-
id: z.string(),
|
|
18
|
-
name: z.string(),
|
|
19
|
-
slug: z.string(),
|
|
20
|
-
settings: z.record(z.string(), z.unknown()).nullable(),
|
|
21
|
-
})),
|
|
22
|
-
handle: async (input, ctx) => {
|
|
23
|
-
if (!ctx.user?.id) {
|
|
24
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (ctx.plugins as any).permissions.getUserTenants(ctx.user.id);
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Create a new tenant (user becomes owner/admin)
|
|
34
|
-
*/
|
|
35
|
-
tenants.route("create").typed(
|
|
36
|
-
defineRoute({
|
|
37
|
-
input: z.object({
|
|
38
|
-
name: z.string().min(1).max(100),
|
|
39
|
-
slug: z.string().min(1).max(50).regex(/^[a-z0-9-]+$/, "Slug must be lowercase alphanumeric with hyphens"),
|
|
40
|
-
settings: z.record(z.string(), z.unknown()).optional(),
|
|
41
|
-
}),
|
|
42
|
-
output: z.object({
|
|
43
|
-
id: z.string(),
|
|
44
|
-
name: z.string(),
|
|
45
|
-
slug: z.string(),
|
|
46
|
-
settings: z.record(z.string(), z.unknown()).nullable(),
|
|
47
|
-
}),
|
|
48
|
-
handle: async (input, ctx) => {
|
|
49
|
-
if (!ctx.user?.id) {
|
|
50
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check if slug is taken
|
|
54
|
-
const existing = await (ctx.plugins as any).permissions.getTenantBySlug(input.slug);
|
|
55
|
-
if (existing) {
|
|
56
|
-
throw ctx.errors.BadRequest("Tenant slug already taken");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return (ctx.plugins as any).permissions.createTenant({
|
|
60
|
-
name: input.name,
|
|
61
|
-
slug: input.slug,
|
|
62
|
-
ownerId: ctx.user.id,
|
|
63
|
-
settings: input.settings,
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get tenant by ID
|
|
71
|
-
*/
|
|
72
|
-
tenants.route("get").typed(
|
|
73
|
-
defineRoute({
|
|
74
|
-
input: z.object({
|
|
75
|
-
tenantId: z.string(),
|
|
76
|
-
}),
|
|
77
|
-
output: z.object({
|
|
78
|
-
id: z.string(),
|
|
79
|
-
name: z.string(),
|
|
80
|
-
slug: z.string(),
|
|
81
|
-
settings: z.record(z.string(), z.unknown()).nullable(),
|
|
82
|
-
}).nullable(),
|
|
83
|
-
handle: async (input, ctx) => {
|
|
84
|
-
if (!ctx.user?.id) {
|
|
85
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Verify membership
|
|
89
|
-
const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
90
|
-
if (!isMember) {
|
|
91
|
-
throw ctx.errors.Forbidden("Not a member of this tenant");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return (ctx.plugins as any).permissions.getTenant(input.tenantId);
|
|
95
|
-
},
|
|
96
|
-
})
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Get tenant roles
|
|
101
|
-
*/
|
|
102
|
-
tenants.route("roles").typed(
|
|
103
|
-
defineRoute({
|
|
104
|
-
input: z.object({
|
|
105
|
-
tenantId: z.string(),
|
|
106
|
-
}),
|
|
107
|
-
output: z.array(z.object({
|
|
108
|
-
id: z.string(),
|
|
109
|
-
name: z.string(),
|
|
110
|
-
description: z.string().nullable(),
|
|
111
|
-
permissions: z.array(z.string()),
|
|
112
|
-
inheritsFrom: z.string().nullable(),
|
|
113
|
-
isDefault: z.boolean(),
|
|
114
|
-
})),
|
|
115
|
-
handle: async (input, ctx) => {
|
|
116
|
-
if (!ctx.user?.id) {
|
|
117
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Verify membership
|
|
121
|
-
const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
|
|
122
|
-
if (!isMember) {
|
|
123
|
-
throw ctx.errors.Forbidden("Not a member of this tenant");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const roles = await (ctx.plugins as any).permissions.getTenantRoles(input.tenantId);
|
|
127
|
-
return roles.map((r: any) => ({
|
|
128
|
-
id: r.id,
|
|
129
|
-
name: r.name,
|
|
130
|
-
description: r.description,
|
|
131
|
-
permissions: r.permissions,
|
|
132
|
-
inheritsFrom: r.inheritsFrom,
|
|
133
|
-
isDefault: r.isDefault,
|
|
134
|
-
}));
|
|
135
|
-
},
|
|
136
|
-
})
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Create a role in a tenant
|
|
141
|
-
*/
|
|
142
|
-
tenants.route("createRole").typed(
|
|
143
|
-
defineRoute({
|
|
144
|
-
input: z.object({
|
|
145
|
-
tenantId: z.string(),
|
|
146
|
-
name: z.string().min(1).max(50),
|
|
147
|
-
description: z.string().max(200).optional(),
|
|
148
|
-
permissions: z.array(z.string()),
|
|
149
|
-
inheritsFrom: z.string().optional(),
|
|
150
|
-
isDefault: z.boolean().optional(),
|
|
151
|
-
}),
|
|
152
|
-
output: z.object({
|
|
153
|
-
id: z.string(),
|
|
154
|
-
name: z.string(),
|
|
155
|
-
description: z.string().nullable(),
|
|
156
|
-
permissions: z.array(z.string()),
|
|
157
|
-
inheritsFrom: z.string().nullable(),
|
|
158
|
-
isDefault: z.boolean(),
|
|
159
|
-
}),
|
|
160
|
-
handle: async (input, ctx) => {
|
|
161
|
-
if (!ctx.user?.id) {
|
|
162
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Check admin permission
|
|
166
|
-
const hasAdmin = await (ctx.plugins as any).permissions.hasPermission(
|
|
167
|
-
ctx.user.id,
|
|
168
|
-
input.tenantId,
|
|
169
|
-
"roles.manage"
|
|
170
|
-
);
|
|
171
|
-
if (!hasAdmin) {
|
|
172
|
-
throw ctx.errors.Forbidden("Cannot manage roles");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const role = await (ctx.plugins as any).permissions.createRole({
|
|
176
|
-
tenantId: input.tenantId,
|
|
177
|
-
name: input.name,
|
|
178
|
-
description: input.description,
|
|
179
|
-
permissions: input.permissions,
|
|
180
|
-
inheritsFrom: input.inheritsFrom,
|
|
181
|
-
isDefault: input.isDefault,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
id: role.id,
|
|
186
|
-
name: role.name,
|
|
187
|
-
description: role.description,
|
|
188
|
-
permissions: role.permissions,
|
|
189
|
-
inheritsFrom: role.inheritsFrom,
|
|
190
|
-
isDefault: role.isDefault,
|
|
191
|
-
};
|
|
192
|
-
},
|
|
193
|
-
})
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Invite user to tenant (add as member)
|
|
198
|
-
*/
|
|
199
|
-
tenants.route("addMember").typed(
|
|
200
|
-
defineRoute({
|
|
201
|
-
input: z.object({
|
|
202
|
-
tenantId: z.string(),
|
|
203
|
-
userId: z.string(),
|
|
204
|
-
}),
|
|
205
|
-
output: z.object({ success: z.boolean() }),
|
|
206
|
-
handle: async (input, ctx) => {
|
|
207
|
-
if (!ctx.user?.id) {
|
|
208
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Check permission
|
|
212
|
-
const canInvite = await (ctx.plugins as any).permissions.hasPermission(
|
|
213
|
-
ctx.user.id,
|
|
214
|
-
input.tenantId,
|
|
215
|
-
"members.invite"
|
|
216
|
-
);
|
|
217
|
-
if (!canInvite) {
|
|
218
|
-
throw ctx.errors.Forbidden("Cannot invite members");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
await (ctx.plugins as any).permissions.addTenantMember(
|
|
222
|
-
input.tenantId,
|
|
223
|
-
input.userId,
|
|
224
|
-
ctx.user.id
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
return { success: true };
|
|
228
|
-
},
|
|
229
|
-
})
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Remove user from tenant
|
|
234
|
-
*/
|
|
235
|
-
tenants.route("removeMember").typed(
|
|
236
|
-
defineRoute({
|
|
237
|
-
input: z.object({
|
|
238
|
-
tenantId: z.string(),
|
|
239
|
-
userId: z.string(),
|
|
240
|
-
}),
|
|
241
|
-
output: z.object({ success: z.boolean() }),
|
|
242
|
-
handle: async (input, ctx) => {
|
|
243
|
-
if (!ctx.user?.id) {
|
|
244
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Check permission
|
|
248
|
-
const canRemove = await (ctx.plugins as any).permissions.hasPermission(
|
|
249
|
-
ctx.user.id,
|
|
250
|
-
input.tenantId,
|
|
251
|
-
"members.remove"
|
|
252
|
-
);
|
|
253
|
-
if (!canRemove) {
|
|
254
|
-
throw ctx.errors.Forbidden("Cannot remove members");
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
await (ctx.plugins as any).permissions.removeTenantMember(input.tenantId, input.userId);
|
|
258
|
-
|
|
259
|
-
return { success: true };
|
|
260
|
-
},
|
|
261
|
-
})
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Assign role to user
|
|
266
|
-
*/
|
|
267
|
-
tenants.route("assignRole").typed(
|
|
268
|
-
defineRoute({
|
|
269
|
-
input: z.object({
|
|
270
|
-
tenantId: z.string(),
|
|
271
|
-
userId: z.string(),
|
|
272
|
-
roleId: z.string(),
|
|
273
|
-
}),
|
|
274
|
-
output: z.object({ success: z.boolean() }),
|
|
275
|
-
handle: async (input, ctx) => {
|
|
276
|
-
if (!ctx.user?.id) {
|
|
277
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Check permission
|
|
281
|
-
const canManage = await (ctx.plugins as any).permissions.hasPermission(
|
|
282
|
-
ctx.user.id,
|
|
283
|
-
input.tenantId,
|
|
284
|
-
"roles.assign"
|
|
285
|
-
);
|
|
286
|
-
if (!canManage) {
|
|
287
|
-
throw ctx.errors.Forbidden("Cannot assign roles");
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
await (ctx.plugins as any).permissions.assignRole(
|
|
291
|
-
input.userId,
|
|
292
|
-
input.roleId,
|
|
293
|
-
input.tenantId,
|
|
294
|
-
ctx.user.id
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
return { success: true };
|
|
298
|
-
},
|
|
299
|
-
})
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Revoke role from user
|
|
304
|
-
*/
|
|
305
|
-
tenants.route("revokeRole").typed(
|
|
306
|
-
defineRoute({
|
|
307
|
-
input: z.object({
|
|
308
|
-
tenantId: z.string(),
|
|
309
|
-
userId: z.string(),
|
|
310
|
-
roleId: z.string(),
|
|
311
|
-
}),
|
|
312
|
-
output: z.object({ success: z.boolean() }),
|
|
313
|
-
handle: async (input, ctx) => {
|
|
314
|
-
if (!ctx.user?.id) {
|
|
315
|
-
throw ctx.errors.Unauthorized("Authentication required");
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Check permission
|
|
319
|
-
const canManage = await (ctx.plugins as any).permissions.hasPermission(
|
|
320
|
-
ctx.user.id,
|
|
321
|
-
input.tenantId,
|
|
322
|
-
"roles.assign"
|
|
323
|
-
);
|
|
324
|
-
if (!canManage) {
|
|
325
|
-
throw ctx.errors.Forbidden("Cannot revoke roles");
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
await (ctx.plugins as any).permissions.revokeRole(
|
|
329
|
-
input.userId,
|
|
330
|
-
input.roleId,
|
|
331
|
-
input.tenantId
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
return { success: true };
|
|
335
|
-
},
|
|
336
|
-
})
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
export { tenants as tenantsRouter };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import adapter from '@donkeylabs/adapter-sveltekit';
|
|
2
|
-
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
3
|
-
|
|
4
|
-
import type { Config } from '@sveltejs/kit';
|
|
5
|
-
|
|
6
|
-
const config: Config = {
|
|
7
|
-
preprocess: vitePreprocess(),
|
|
8
|
-
|
|
9
|
-
kit: {
|
|
10
|
-
adapter: adapter(),
|
|
11
|
-
alias: {
|
|
12
|
-
$server: '.@donkeylabs/server',
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default config;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./.svelte-kit/tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"allowJs": true,
|
|
5
|
-
"checkJs": true,
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"forceConsistentCasingInFileNames": true,
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"moduleResolution": "bundler"
|
|
13
|
-
},
|
|
14
|
-
"include": [
|
|
15
|
-
".@donkeylabs/server/**/*.ts",
|
|
16
|
-
".@donkeylabs/server/**/*.d.ts",
|
|
17
|
-
"src/**/*.ts",
|
|
18
|
-
"src/**/*.svelte"
|
|
19
|
-
]
|
|
20
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { sveltekit } from '@sveltejs/kit/vite';
|
|
2
|
-
import tailwindcss from '@tailwindcss/vite';
|
|
3
|
-
import { defineConfig } from 'vite';
|
|
4
|
-
import { donkeylabsDev } from '@donkeylabs/adapter-sveltekit/vite';
|
|
5
|
-
|
|
6
|
-
export default defineConfig({
|
|
7
|
-
plugins: [donkeylabsDev(), tailwindcss(), sveltekit()],
|
|
8
|
-
ssr: {
|
|
9
|
-
// Bundle @donkeylabs packages in SSR so TypeScript files get transpiled
|
|
10
|
-
noExternal: ['@donkeylabs/adapter-sveltekit', '@donkeylabs/server'],
|
|
11
|
-
},
|
|
12
|
-
});
|