web-mojo 2.2.55 → 2.2.56
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/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +315 -1
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunks/{version-Gcf9opot.js → version-CQCmm8rw.js} +2 -2
- package/dist/chunks/{version-Gcf9opot.js.map → version-CQCmm8rw.js.map} +1 -1
- package/dist/chunks/{version-ZWtWuDxk.js → version-MS6IWlRk.js} +4 -4
- package/dist/chunks/{version-ZWtWuDxk.js.map → version-MS6IWlRk.js.map} +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +1 -1
- package/package.json +1 -1
package/dist/admin.es.js
CHANGED
|
@@ -10,7 +10,7 @@ import { M as MapView, a as MetricsCountryMapView } from "./chunks/MetricsCountr
|
|
|
10
10
|
import { M as Model, C as Collection } from "./chunks/Collection-DaiL0uGl.js";
|
|
11
11
|
import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-EJ9cOfPF.js";
|
|
12
12
|
import { W } from "./chunks/WebApp-6qvqmOts.js";
|
|
13
|
-
import { B, a, V, b, c, d } from "./chunks/version-
|
|
13
|
+
import { B, a, V, b, c, d } from "./chunks/version-MS6IWlRk.js";
|
|
14
14
|
class AdminHeaderView extends View {
|
|
15
15
|
constructor(options = {}) {
|
|
16
16
|
super({
|
|
@@ -2249,6 +2249,311 @@ class GeoLocatedIPTablePage extends TablePage {
|
|
|
2249
2249
|
}
|
|
2250
2250
|
}
|
|
2251
2251
|
}
|
|
2252
|
+
class ApiKey extends Model {
|
|
2253
|
+
constructor(data = {}, options = {}) {
|
|
2254
|
+
super(data, {
|
|
2255
|
+
endpoint: "/api/group/apikey",
|
|
2256
|
+
...options
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
class ApiKeyList extends Collection {
|
|
2261
|
+
constructor(options = {}) {
|
|
2262
|
+
super({
|
|
2263
|
+
ModelClass: ApiKey,
|
|
2264
|
+
endpoint: "/api/group/apikey",
|
|
2265
|
+
size: 25,
|
|
2266
|
+
...options
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
const ApiKeyForms = {
|
|
2271
|
+
create: {
|
|
2272
|
+
title: "Create API Key",
|
|
2273
|
+
fields: [
|
|
2274
|
+
{
|
|
2275
|
+
name: "name",
|
|
2276
|
+
type: "text",
|
|
2277
|
+
label: "Name",
|
|
2278
|
+
placeholder: "Mobile App v2",
|
|
2279
|
+
required: true,
|
|
2280
|
+
columns: 12,
|
|
2281
|
+
help: "A descriptive name to identify this key."
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "group",
|
|
2285
|
+
type: "number",
|
|
2286
|
+
label: "Group ID",
|
|
2287
|
+
required: true,
|
|
2288
|
+
columns: 6,
|
|
2289
|
+
help: "The group this key is scoped to."
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
name: "permissions",
|
|
2293
|
+
type: "textarea",
|
|
2294
|
+
label: "Permissions (JSON)",
|
|
2295
|
+
placeholder: '{"view_orders": true, "create_orders": true}',
|
|
2296
|
+
columns: 12,
|
|
2297
|
+
help: "JSON dict of permissions to grant. Leave empty for no permissions."
|
|
2298
|
+
}
|
|
2299
|
+
]
|
|
2300
|
+
},
|
|
2301
|
+
edit: {
|
|
2302
|
+
title: "Edit API Key",
|
|
2303
|
+
fields: [
|
|
2304
|
+
{
|
|
2305
|
+
name: "name",
|
|
2306
|
+
type: "text",
|
|
2307
|
+
label: "Name",
|
|
2308
|
+
required: true,
|
|
2309
|
+
columns: 12
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
name: "is_active",
|
|
2313
|
+
type: "switch",
|
|
2314
|
+
label: "Active",
|
|
2315
|
+
columns: 12,
|
|
2316
|
+
help: "Deactivate to revoke access without deleting the key."
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
name: "permissions",
|
|
2320
|
+
type: "textarea",
|
|
2321
|
+
label: "Permissions (JSON)",
|
|
2322
|
+
columns: 12,
|
|
2323
|
+
help: "JSON dict of granted permissions."
|
|
2324
|
+
}
|
|
2325
|
+
]
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
class ApiKeyView extends View {
|
|
2329
|
+
constructor(options = {}) {
|
|
2330
|
+
super({
|
|
2331
|
+
className: "api-key-view",
|
|
2332
|
+
...options
|
|
2333
|
+
});
|
|
2334
|
+
this.model = options.model || new ApiKey(options.data || {});
|
|
2335
|
+
this.template = `
|
|
2336
|
+
<div class="api-key-view-container">
|
|
2337
|
+
<!-- Header -->
|
|
2338
|
+
<div class="d-flex justify-content-between align-items-start mb-4">
|
|
2339
|
+
<!-- Left: Icon & Identity -->
|
|
2340
|
+
<div class="d-flex align-items-center gap-3">
|
|
2341
|
+
<div class="fs-1 text-primary">
|
|
2342
|
+
<i class="bi bi-key"></i>
|
|
2343
|
+
</div>
|
|
2344
|
+
<div>
|
|
2345
|
+
<h3 class="mb-1">{{model.name|default('Unnamed Key')}}</h3>
|
|
2346
|
+
<div class="text-muted small">
|
|
2347
|
+
ID: {{model.id}}
|
|
2348
|
+
<span class="mx-2">|</span>
|
|
2349
|
+
Group: {{model.group.name|default(model.group)}}
|
|
2350
|
+
</div>
|
|
2351
|
+
<div class="mt-1">
|
|
2352
|
+
<span class="badge {{model.is_active|boolean('bg-success','bg-secondary')}}">
|
|
2353
|
+
{{model.is_active|boolean('Active','Inactive')}}
|
|
2354
|
+
</span>
|
|
2355
|
+
</div>
|
|
2356
|
+
</div>
|
|
2357
|
+
</div>
|
|
2358
|
+
|
|
2359
|
+
<!-- Right: Meta & Actions -->
|
|
2360
|
+
<div class="d-flex align-items-start gap-4">
|
|
2361
|
+
<div class="text-end">
|
|
2362
|
+
<div class="text-muted small">Created</div>
|
|
2363
|
+
<div>{{model.created|datetime}}</div>
|
|
2364
|
+
</div>
|
|
2365
|
+
<div data-container="apikey-context-menu"></div>
|
|
2366
|
+
</div>
|
|
2367
|
+
</div>
|
|
2368
|
+
|
|
2369
|
+
<!-- Details -->
|
|
2370
|
+
<div class="list-group mb-3">
|
|
2371
|
+
<div class="list-group-item">
|
|
2372
|
+
<h6 class="mb-1 text-muted">Token Preview</h6>
|
|
2373
|
+
<p class="mb-1 font-monospace small text-muted">
|
|
2374
|
+
The raw token is only shown once at creation time.
|
|
2375
|
+
</p>
|
|
2376
|
+
</div>
|
|
2377
|
+
<div class="list-group-item">
|
|
2378
|
+
<h6 class="mb-1 text-muted">Permissions</h6>
|
|
2379
|
+
{{#model.permissions}}
|
|
2380
|
+
<pre class="mb-0 small">{{model.permissions|json}}</pre>
|
|
2381
|
+
{{/model.permissions}}
|
|
2382
|
+
{{^model.permissions}}
|
|
2383
|
+
<span class="text-muted small">No permissions granted</span>
|
|
2384
|
+
{{/model.permissions}}
|
|
2385
|
+
</div>
|
|
2386
|
+
{{#model.limits}}
|
|
2387
|
+
<div class="list-group-item">
|
|
2388
|
+
<h6 class="mb-1 text-muted">Rate Limit Overrides</h6>
|
|
2389
|
+
<pre class="mb-0 small">{{model.limits|json}}</pre>
|
|
2390
|
+
</div>
|
|
2391
|
+
{{/model.limits}}
|
|
2392
|
+
<div class="list-group-item">
|
|
2393
|
+
<h6 class="mb-1 text-muted">Usage</h6>
|
|
2394
|
+
<p class="mb-0 small text-muted">
|
|
2395
|
+
Include in requests as:
|
|
2396
|
+
<code>Authorization: apikey <token></code>
|
|
2397
|
+
</p>
|
|
2398
|
+
</div>
|
|
2399
|
+
</div>
|
|
2400
|
+
</div>
|
|
2401
|
+
`;
|
|
2402
|
+
}
|
|
2403
|
+
async onInit() {
|
|
2404
|
+
const isActive = this.model.get("is_active");
|
|
2405
|
+
const apiKeyMenu = new ContextMenu({
|
|
2406
|
+
containerId: "apikey-context-menu",
|
|
2407
|
+
className: "context-menu-view header-menu-absolute",
|
|
2408
|
+
context: this.model,
|
|
2409
|
+
config: {
|
|
2410
|
+
icon: "bi-three-dots-vertical",
|
|
2411
|
+
items: [
|
|
2412
|
+
{ label: "Edit", action: "edit-key", icon: "bi-pencil" },
|
|
2413
|
+
isActive ? { label: "Deactivate", action: "deactivate-key", icon: "bi-x-circle" } : { label: "Activate", action: "activate-key", icon: "bi-check-circle" },
|
|
2414
|
+
{ type: "divider" },
|
|
2415
|
+
{ label: "Delete Key", action: "delete-key", icon: "bi-trash", danger: true }
|
|
2416
|
+
]
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
this.addChild(apiKeyMenu);
|
|
2420
|
+
}
|
|
2421
|
+
async onActionEditKey() {
|
|
2422
|
+
const resp = await Dialog$1.showModelForm({
|
|
2423
|
+
title: `Edit API Key — ${this.model.get("name")}`,
|
|
2424
|
+
model: this.model,
|
|
2425
|
+
formConfig: ApiKeyForms.edit
|
|
2426
|
+
});
|
|
2427
|
+
if (resp) {
|
|
2428
|
+
this.render();
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
async onActionDeactivateKey() {
|
|
2432
|
+
const confirmed = await Dialog$1.confirm({
|
|
2433
|
+
title: "Deactivate API Key",
|
|
2434
|
+
message: `Deactivate "${this.model.get("name")}"? Requests using this key will be rejected.`,
|
|
2435
|
+
confirmLabel: "Deactivate",
|
|
2436
|
+
confirmClass: "btn-warning"
|
|
2437
|
+
});
|
|
2438
|
+
if (!confirmed) return;
|
|
2439
|
+
const app = this.getApp();
|
|
2440
|
+
app.showLoading();
|
|
2441
|
+
const resp = await this.model.save({ is_active: false });
|
|
2442
|
+
app.hideLoading();
|
|
2443
|
+
if (resp && resp.success !== false) {
|
|
2444
|
+
app.toast.success("API key deactivated");
|
|
2445
|
+
this.render();
|
|
2446
|
+
} else {
|
|
2447
|
+
app.toast.error("Failed to deactivate key");
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
async onActionActivateKey() {
|
|
2451
|
+
const app = this.getApp();
|
|
2452
|
+
app.showLoading();
|
|
2453
|
+
const resp = await this.model.save({ is_active: true });
|
|
2454
|
+
app.hideLoading();
|
|
2455
|
+
if (resp && resp.success !== false) {
|
|
2456
|
+
app.toast.success("API key activated");
|
|
2457
|
+
this.render();
|
|
2458
|
+
} else {
|
|
2459
|
+
app.toast.error("Failed to activate key");
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
async onActionDeleteKey() {
|
|
2463
|
+
const confirmed = await Dialog$1.confirm({
|
|
2464
|
+
title: "Delete API Key",
|
|
2465
|
+
message: `Permanently delete "${this.model.get("name")}"? This cannot be undone.`,
|
|
2466
|
+
confirmLabel: "Delete",
|
|
2467
|
+
confirmClass: "btn-danger"
|
|
2468
|
+
});
|
|
2469
|
+
if (!confirmed) return;
|
|
2470
|
+
const app = this.getApp();
|
|
2471
|
+
app.showLoading();
|
|
2472
|
+
const resp = await this.model.delete();
|
|
2473
|
+
app.hideLoading();
|
|
2474
|
+
if (resp && resp.success !== false) {
|
|
2475
|
+
app.toast.success("API key deleted");
|
|
2476
|
+
this.emit("deleted", { model: this.model });
|
|
2477
|
+
} else {
|
|
2478
|
+
app.toast.error("Failed to delete key");
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
ApiKey.VIEW_CLASS = ApiKeyView;
|
|
2483
|
+
class ApiKeyTablePage extends TablePage {
|
|
2484
|
+
constructor(options = {}) {
|
|
2485
|
+
super({
|
|
2486
|
+
...options,
|
|
2487
|
+
name: "admin_api_keys",
|
|
2488
|
+
pageName: "API Keys",
|
|
2489
|
+
router: "admin/api-keys",
|
|
2490
|
+
Collection: ApiKeyList,
|
|
2491
|
+
itemViewClass: ApiKeyView,
|
|
2492
|
+
viewDialogOptions: {
|
|
2493
|
+
header: false,
|
|
2494
|
+
size: "lg"
|
|
2495
|
+
},
|
|
2496
|
+
columns: [
|
|
2497
|
+
{ key: "id", label: "ID", width: "70px", sortable: true, class: "text-muted" },
|
|
2498
|
+
{ key: "name", label: "Name", sortable: true },
|
|
2499
|
+
{ key: "group.name", label: "Group", sortable: true, formatter: "default('—')" },
|
|
2500
|
+
{
|
|
2501
|
+
key: "is_active",
|
|
2502
|
+
label: "Status",
|
|
2503
|
+
formatter: "boolean('Active|bg-success','Inactive|bg-secondary')|badge",
|
|
2504
|
+
width: "100px"
|
|
2505
|
+
},
|
|
2506
|
+
{ key: "created", label: "Created", formatter: "datetime", sortable: true }
|
|
2507
|
+
],
|
|
2508
|
+
selectable: true,
|
|
2509
|
+
searchable: true,
|
|
2510
|
+
sortable: true,
|
|
2511
|
+
filterable: true,
|
|
2512
|
+
paginated: true,
|
|
2513
|
+
showRefresh: true,
|
|
2514
|
+
showAdd: true,
|
|
2515
|
+
showExport: false,
|
|
2516
|
+
addButtonLabel: "New API Key",
|
|
2517
|
+
emptyMessage: "No API keys found.",
|
|
2518
|
+
tableOptions: {
|
|
2519
|
+
striped: true,
|
|
2520
|
+
bordered: false,
|
|
2521
|
+
hover: true,
|
|
2522
|
+
responsive: false
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Override onAdd to show the create form and display the token on success.
|
|
2528
|
+
* The token is only returned at creation time — display it in an alert.
|
|
2529
|
+
*/
|
|
2530
|
+
async onAdd() {
|
|
2531
|
+
const app = this.getApp();
|
|
2532
|
+
const data = await Dialog$1.showForm({
|
|
2533
|
+
title: "Create API Key",
|
|
2534
|
+
formConfig: ApiKeyForms.create,
|
|
2535
|
+
size: "md"
|
|
2536
|
+
});
|
|
2537
|
+
if (!data) return;
|
|
2538
|
+
app.showLoading();
|
|
2539
|
+
const resp = await app.rest.POST("/api/group/apikey", data);
|
|
2540
|
+
app.hideLoading();
|
|
2541
|
+
if (resp && resp.status) {
|
|
2542
|
+
const token = resp.data?.token;
|
|
2543
|
+
await Dialog$1.alert({
|
|
2544
|
+
title: "API Key Created",
|
|
2545
|
+
message: token ? `Copy this token now — it will not be shown again.
|
|
2546
|
+
|
|
2547
|
+
${token}` : "API key created successfully.",
|
|
2548
|
+
type: token ? "warning" : "success",
|
|
2549
|
+
size: "lg"
|
|
2550
|
+
});
|
|
2551
|
+
this.collection.fetch();
|
|
2552
|
+
} else {
|
|
2553
|
+
app.toast.error(resp?.error || "Failed to create API key");
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2252
2557
|
class IncidentDashboardHeader extends View {
|
|
2253
2558
|
constructor(options = {}) {
|
|
2254
2559
|
super({
|
|
@@ -9527,6 +9832,7 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9527
9832
|
app.registerPage("system/push/devices", PushDeviceTablePage, { permissions: ["manage_users"] });
|
|
9528
9833
|
app.registerPage("system/phonehub/numbers", PhoneNumberTablePage, { permissions: ["manage_users"] });
|
|
9529
9834
|
app.registerPage("system/phonehub/sms", SMSTablePage, { permissions: ["manage_users"] });
|
|
9835
|
+
app.registerPage("system/api-keys", ApiKeyTablePage, { permissions: ["manage_groups", "manage_group"] });
|
|
9530
9836
|
if (addToMenu && app.sidebar && app.sidebar.getMenuConfig) {
|
|
9531
9837
|
const adminMenuConfig = app.sidebar.getMenuConfig("system");
|
|
9532
9838
|
if (adminMenuConfig && adminMenuConfig.items) {
|
|
@@ -9628,6 +9934,12 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9628
9934
|
route: "?page=system/metrics/permissions",
|
|
9629
9935
|
icon: "bi-bar-chart-line",
|
|
9630
9936
|
permissions: ["manage_metrics"]
|
|
9937
|
+
},
|
|
9938
|
+
{
|
|
9939
|
+
text: "API Keys",
|
|
9940
|
+
route: "?page=system/api-keys",
|
|
9941
|
+
icon: "bi-key",
|
|
9942
|
+
permissions: ["manage_groups", "manage_group"]
|
|
9631
9943
|
}
|
|
9632
9944
|
]
|
|
9633
9945
|
},
|
|
@@ -9729,6 +10041,8 @@ function registerSystemPages(app, addToMenu = true) {
|
|
|
9729
10041
|
}
|
|
9730
10042
|
export {
|
|
9731
10043
|
AdminDashboardPage,
|
|
10044
|
+
ApiKeyTablePage,
|
|
10045
|
+
ApiKeyView,
|
|
9732
10046
|
B as BUILD_TIME,
|
|
9733
10047
|
DeviceView,
|
|
9734
10048
|
EmailDomainTablePage,
|