@strapi-community/plugin-better-auth-dashboard 1.0.0-alpha.1 → 1.0.0-alpha.10

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.
@@ -35,7 +35,6 @@ const proxyController = () => ({
35
35
  overrideOptions: { jwt: { expirationTime: "5m" } }
36
36
  }
37
37
  });
38
- console.log("Generated JWT with payload:", { token });
39
38
  const url = new URL(
40
39
  ctx.request.url,
41
40
  `${ctx.request.protocol}://${ctx.request.host}`
@@ -61,17 +60,245 @@ const proxyController = () => ({
61
60
  ctx.set(key, value);
62
61
  });
63
62
  const contentType = response.headers.get("content-type");
64
- if (contentType?.includes("application/json")) {
65
- ctx.body = await response.json();
66
- } else if (contentType?.includes("text/")) {
67
- ctx.body = await response.text();
63
+ const text = await response.text();
64
+ if (!text) {
65
+ ctx.body = {};
66
+ } else if (contentType?.includes("application/json")) {
67
+ ctx.body = JSON.parse(text);
68
68
  } else {
69
- ctx.body = await response.text();
69
+ ctx.body = text;
70
70
  }
71
71
  }
72
72
  });
73
+ function assertValidUid(ctx, uid) {
74
+ if (typeof uid !== "string" || !uid.startsWith("plugin::") && !uid.startsWith("api::") && !uid.startsWith("admin::")) {
75
+ ctx.status = 400;
76
+ ctx.body = { error: "uid must be a valid Strapi content-type UID" };
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ const dbController = () => ({
82
+ /**
83
+ * List documents.
84
+ *
85
+ * Query params (all optional):
86
+ * uid – content-type UID, e.g. plugin::better-auth.session
87
+ * filters – Strapi filter object, supports nested qs syntax:
88
+ * ?filters[userId][$eq]=1
89
+ * sort – sort string or array, e.g. sort[0]=createdAt:desc
90
+ * pagination[page] – 1-indexed page number (default 1)
91
+ * pagination[pageSize] – items per page (default 50)
92
+ * populate – "*" for all relations, or comma-separated field names
93
+ *
94
+ * Response: { results: [...], pagination: { page, pageSize, total, pageCount } }
95
+ */
96
+ async list(ctx) {
97
+ const { uid, filters, pagination, populate } = ctx.query;
98
+ if (!assertValidUid(ctx, uid)) return;
99
+ const page = Math.max(1, parseInt(pagination?.page ?? "1", 10));
100
+ const pageSize = Math.min(
101
+ 200,
102
+ Math.max(1, parseInt(pagination?.pageSize ?? "50", 10))
103
+ );
104
+ const populateArg = populate ? populate === "*" ? "*" : populate.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
105
+ const service = strapi.documents(uid);
106
+ const [results, total] = await Promise.all([
107
+ service.findMany({
108
+ filters: filters ?? {},
109
+ sort: "createdAt:desc",
110
+ limit: pageSize,
111
+ start: (page - 1) * pageSize,
112
+ ...populateArg ? { populate: populateArg } : {}
113
+ }),
114
+ strapi.documents(uid).count({ filters: filters ?? {} })
115
+ ]);
116
+ ctx.body = {
117
+ results,
118
+ pagination: {
119
+ page,
120
+ pageSize,
121
+ total,
122
+ pageCount: Math.ceil(total / pageSize)
123
+ }
124
+ };
125
+ },
126
+ /**
127
+ * Update a single document by its documentId (UUID).
128
+ *
129
+ * Query params:
130
+ * uid – content-type UID
131
+ *
132
+ * Body: partial field values to update.
133
+ *
134
+ * Response: updated document.
135
+ */
136
+ async update(ctx) {
137
+ const { uid } = ctx.query;
138
+ if (!assertValidUid(ctx, uid)) return;
139
+ const { documentId } = ctx.params;
140
+ const result = await strapi.documents(uid).update({
141
+ documentId,
142
+ data: ctx.request.body
143
+ });
144
+ if (!result) {
145
+ ctx.status = 404;
146
+ ctx.body = { error: "Document not found" };
147
+ return;
148
+ }
149
+ ctx.body = result;
150
+ }
151
+ });
73
152
  const controllers = {
74
- "auth-controller": proxyController
153
+ "auth-controller": proxyController,
154
+ "db-controller": dbController
155
+ };
156
+ const { PolicyError } = errors;
157
+ const endpointsCategories = Object.freeze({
158
+ "/auth/dash/config": ["overview", "user", "organization"],
159
+ "/auth/dash/user-stats": ["overview"],
160
+ "/auth/dash/user-graph-data": ["overview"],
161
+ "/auth/dash/user-retention-data": ["overview"],
162
+ "/auth/dash/list-users": ["overview", "user"],
163
+ "/auth/dash/list-organization": ["overview", "organization"],
164
+ "/auth/dash/user": ["user"],
165
+ "/auth/dash/organization": ["organization"]
166
+ });
167
+ const types = Object.freeze({
168
+ POST: "read",
169
+ PUT: "update",
170
+ GET: "read",
171
+ DELETE: "delete"
172
+ });
173
+ const endpointsTypes = Object.freeze({
174
+ "/auth/dash/user-stats": "read",
175
+ "/auth/dash/user-graph-data": "read",
176
+ "/auth/dash/user-retention-data": "read",
177
+ "/auth/dash/list-users": "read",
178
+ "/auth/dash/list-organizations": "read",
179
+ "/auth/dash/organization/options": "read",
180
+ "/auth/dash/create-user": "create",
181
+ "/auth/dash/update-user": "update",
182
+ "/auth/dash/user": "read",
183
+ "/auth/dash/delete-user": "delete",
184
+ "/auth/dash/organization/create": "create",
185
+ "/auth/dash/organization/delete": "delete"
186
+ });
187
+ const hasPermission = async (ctx, _, { strapi: strapi2 }) => {
188
+ const { user, route } = ctx.state;
189
+ if (!user) return false;
190
+ const inherentType = types[route.method];
191
+ const determinedType = endpointsTypes?.[Object.keys(endpointsTypes)?.find(
192
+ (k) => ctx.originalUrl?.includes(k)
193
+ )];
194
+ const found = [];
195
+ for await (const role of user.roles) {
196
+ const categories = endpointsCategories[Object.keys(endpointsCategories).find(
197
+ (key) => ctx.originalUrl.startsWith(key)
198
+ )];
199
+ const permissions = await strapi2.documents("admin::permission").findMany({
200
+ filters: {
201
+ $or: (categories || []).map((cat) => ({
202
+ role: { id: role.id },
203
+ action: {
204
+ $startsWith: "plugin::better-auth-dashboard",
205
+ $containsi: cat,
206
+ $endsWith: determinedType || inherentType
207
+ }
208
+ }))
209
+ }
210
+ });
211
+ if (permissions?.length > 0) found.push(...permissions);
212
+ }
213
+ if (found.length <= 0) {
214
+ throw new PolicyError("You do not have permission to see this content.");
215
+ }
216
+ return true;
217
+ };
218
+ const policies = { "has-permission": hasPermission };
219
+ const strapi$1 = { "name": "better-auth-dashboard" };
220
+ const packageJson = {
221
+ strapi: strapi$1
222
+ };
223
+ const PLUGIN_ID = packageJson.strapi.name;
224
+ const ACTIONS = {
225
+ overview: [
226
+ {
227
+ section: "plugins",
228
+ subCategory: "overview",
229
+ displayName: "Read",
230
+ pluginName: PLUGIN_ID,
231
+ uid: "overview.read"
232
+ }
233
+ ],
234
+ user: [
235
+ {
236
+ section: "plugins",
237
+ subCategory: "user",
238
+ displayName: "Create",
239
+ pluginName: PLUGIN_ID,
240
+ uid: "user.create"
241
+ },
242
+ {
243
+ section: "plugins",
244
+ subCategory: "user",
245
+ displayName: "Read",
246
+ pluginName: PLUGIN_ID,
247
+ uid: "user.read"
248
+ },
249
+ {
250
+ section: "plugins",
251
+ subCategory: "user",
252
+ displayName: "Update",
253
+ pluginName: PLUGIN_ID,
254
+ uid: "user.update"
255
+ },
256
+ {
257
+ section: "plugins",
258
+ subCategory: "user",
259
+ displayName: "Delete",
260
+ pluginName: PLUGIN_ID,
261
+ uid: "user.delete"
262
+ }
263
+ ],
264
+ organization: [
265
+ {
266
+ section: "plugins",
267
+ subCategory: "organization",
268
+ displayName: "Create",
269
+ pluginName: PLUGIN_ID,
270
+ uid: "organization.create"
271
+ },
272
+ {
273
+ section: "plugins",
274
+ subCategory: "organization",
275
+ displayName: "Read",
276
+ pluginName: PLUGIN_ID,
277
+ uid: "organization.read"
278
+ },
279
+ {
280
+ section: "plugins",
281
+ subCategory: "organization",
282
+ displayName: "Update",
283
+ pluginName: PLUGIN_ID,
284
+ uid: "organization.update"
285
+ },
286
+ {
287
+ section: "plugins",
288
+ subCategory: "organization",
289
+ displayName: "Delete",
290
+ pluginName: PLUGIN_ID,
291
+ uid: "organization.delete"
292
+ }
293
+ ]
294
+ };
295
+ const register = ({ strapi: strapi2 }) => {
296
+ const actionsToRegister = [...ACTIONS.overview, ...ACTIONS.user];
297
+ if (strapi2.plugin("better-auth").contentTypes?.organization)
298
+ actionsToRegister.push(...ACTIONS.organization);
299
+ strapi2.admin.services.permission.actionProvider.registerMany(
300
+ actionsToRegister
301
+ );
75
302
  };
76
303
  const adminRoutes = () => ({
77
304
  type: "admin",
@@ -81,9 +308,8 @@ const adminRoutes = () => ({
81
308
  path: "/auth/:path*",
82
309
  handler: "auth-controller.handleAuthRequest",
83
310
  config: {
84
- policies: [],
85
- prefix: "",
86
- auth: false
311
+ policies: ["has-permission"],
312
+ prefix: ""
87
313
  }
88
314
  },
89
315
  {
@@ -91,8 +317,7 @@ const adminRoutes = () => ({
91
317
  path: "/auth/:path*",
92
318
  handler: "auth-controller.handleAuthRequest",
93
319
  config: {
94
- policies: [],
95
- auth: false,
320
+ policies: ["has-permission"],
96
321
  prefix: ""
97
322
  }
98
323
  },
@@ -101,8 +326,7 @@ const adminRoutes = () => ({
101
326
  path: "/auth/:path*",
102
327
  handler: "auth-controller.handleAuthRequest",
103
328
  config: {
104
- policies: [],
105
- auth: false,
329
+ policies: ["has-permission"],
106
330
  prefix: ""
107
331
  }
108
332
  },
@@ -111,8 +335,7 @@ const adminRoutes = () => ({
111
335
  path: "/auth/:path*",
112
336
  handler: "auth-controller.handleAuthRequest",
113
337
  config: {
114
- policies: [],
115
- auth: false,
338
+ policies: ["has-permission"],
116
339
  prefix: ""
117
340
  }
118
341
  },
@@ -121,8 +344,25 @@ const adminRoutes = () => ({
121
344
  path: "/auth/:path*",
122
345
  handler: "auth-controller.handleAuthRequest",
123
346
  config: {
124
- policies: [],
125
- auth: false,
347
+ policies: ["has-permission"],
348
+ prefix: ""
349
+ }
350
+ },
351
+ {
352
+ method: "GET",
353
+ path: "/better-auth-dashboard/db",
354
+ handler: "db-controller.list",
355
+ config: {
356
+ policies: ["has-permission"],
357
+ prefix: ""
358
+ }
359
+ },
360
+ {
361
+ method: "PUT",
362
+ path: "/better-auth-dashboard/db/:documentId",
363
+ handler: "db-controller.update",
364
+ config: {
365
+ policies: ["has-permission"],
126
366
  prefix: ""
127
367
  }
128
368
  }
@@ -131,7 +371,9 @@ const adminRoutes = () => ({
131
371
  const routes = { admin: adminRoutes };
132
372
  const index = {
133
373
  controllers,
134
- routes
374
+ routes,
375
+ policies,
376
+ register
135
377
  };
136
378
  export {
137
379
  index as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi-community/plugin-better-auth-dashboard",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.10",
4
4
  "description": "Better Auth dashboard for the Strapi admin panel",
5
5
  "keywords": [
6
6
  "strapi",
@@ -11,13 +11,13 @@
11
11
  "license": "MIT",
12
12
  "author": {
13
13
  "name": "Boaz Poolman",
14
- "email": "boaz@pluginpal.io",
14
+ "email": "boaz.poolman@strapi.io",
15
15
  "url": "https://github.com/boazpoolman"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
19
19
  "url": "git+https://github.com/strapi-community/plugin-better-auth.git",
20
- "directory": "packages/plugin-better-auth-dashboard"
20
+ "directory": "plugins/plugin-better-auth-dashboard"
21
21
  },
22
22
  "type": "commonjs",
23
23
  "exports": {
@@ -48,7 +48,8 @@
48
48
  "@types/node": "^22",
49
49
  "@types/react": "^18.0.0",
50
50
  "@types/styled-components": "^5.1.36",
51
- "typescript": "^5.9.3"
51
+ "typescript": "^5.9.3",
52
+ "@strapi-community/dev-utils": "0.0.1"
52
53
  },
53
54
  "peerDependencies": {
54
55
  "@better-auth/infra": "^0.2.6",
@@ -63,6 +64,7 @@
63
64
  "react-intl": "^6.0.0 || ^7.0.0",
64
65
  "react-query": "^3.0.0",
65
66
  "styled-components": "^6.0.0",
67
+ "react-router-dom": "^6.0.0",
66
68
  "@strapi-community/plugin-better-auth": "^1.0.0-beta.6"
67
69
  },
68
70
  "strapi": {
@@ -76,9 +78,9 @@
76
78
  "watch": "strapi-plugin watch",
77
79
  "dev": "strapi-plugin watch",
78
80
  "verify": "strapi-plugin verify",
79
- "test:ts:server": "cd server && tsc --noEmit",
80
- "test:ts:admin": "cd admin && tsc --noEmit",
81
- "check-types": "pnpm run test:ts:server && pnpm run test:ts:admin",
82
- "lint": "biome check --fix"
81
+ "lint:ts:server": "cd server && tsc --noEmit",
82
+ "lint:ts:admin": "cd admin && tsc --noEmit",
83
+ "lint": "biome check --fix",
84
+ "test:e2e": "playwright test"
83
85
  }
84
86
  }
@@ -1,26 +0,0 @@
1
- "use strict";
2
- const PLUGIN_ID = "better-auth-dashboard";
3
- function captureApp(app) {
4
- window.__betterAuthDashApp = app;
5
- }
6
- function getMediaLibraryComponent() {
7
- return window.__betterAuthDashApp?.library?.components?.["media-library"] ?? null;
8
- }
9
- const index = {
10
- register(app) {
11
- captureApp(app);
12
- app.addMenuLink({
13
- to: `/plugins/${PLUGIN_ID}`,
14
- icon: () => null,
15
- intlLabel: {
16
- id: `${PLUGIN_ID}.plugin.name`,
17
- defaultMessage: "Auth Dashboard"
18
- },
19
- Component: async () => Promise.resolve().then(() => require("./Root-Bl4iPGDu.js"))
20
- });
21
- },
22
- bootstrap() {
23
- }
24
- };
25
- exports.getMediaLibraryComponent = getMediaLibraryComponent;
26
- exports.index = index;
@@ -1,27 +0,0 @@
1
- const PLUGIN_ID = "better-auth-dashboard";
2
- function captureApp(app) {
3
- window.__betterAuthDashApp = app;
4
- }
5
- function getMediaLibraryComponent() {
6
- return window.__betterAuthDashApp?.library?.components?.["media-library"] ?? null;
7
- }
8
- const index = {
9
- register(app) {
10
- captureApp(app);
11
- app.addMenuLink({
12
- to: `/plugins/${PLUGIN_ID}`,
13
- icon: () => null,
14
- intlLabel: {
15
- id: `${PLUGIN_ID}.plugin.name`,
16
- defaultMessage: "Auth Dashboard"
17
- },
18
- Component: async () => import("./Root-hwPhIfaT.mjs")
19
- });
20
- },
21
- bootstrap() {
22
- }
23
- };
24
- export {
25
- getMediaLibraryComponent as g,
26
- index as i
27
- };